数据类型

C语言中,数据类型包括:基本类型(整型、浮点型、字符型),复合类型(数组、结构体、联合体、枚举),指针类型。这篇笔记我们介绍C语言中基本数据类型相关的知识,有关复合数据类型和指针将在后续章节介绍。

什么是数据类型

数据类型是对数据的抽象,相同类型的数据有相同的表示形式、存储格式以及相关操作。编程语言中所有的数据都必定属于某种类型。可以这样简单的理解:数据类型就是创建变量的模具。

整型

C语言中,整型包括charshortintlong int(简写为long),long long int(简写为long long),signedunsigned表示有符号数或无符号数。八进制数以0开头,十六进制以0x开头,字面值结尾加u表示unsigned,加L表示long

整型的长度可参考下表。

类型 ILP32 LP64
char 8 8
short 16 16
int 32 32
long 32 64
long long 64 64
指针 32 64

注:ILP32/LP64指Intel x86/64平台,Linux,gcc编译器。

浮点型

C语言中,浮点型包括floatdoublelong double。IEEE754标准中,float为32位,double为64位,long double则平台差异较大,不能一概而论。

类型转换

C语言中,类型转换可以分为隐式类型转换和强制类型转换。其中,隐式转换虽然规则看起来很复杂,但实际使用时并不需要考虑太多,下面我们简单介绍。

Integer Promotion(整型提升)

在一个表达式中,凡是可以使用intunsigned int类型做右值的地方也都可以使用有符号或无符号的char类型、short类型。如果原始类型的取值范围都能用int类型表示,则其类型被提升为int,如果原始类型的取值范围用int类型表示不了,则提升为unsigned int型,这称为Integer Promotion(整型提升)。

整型提升只影响上述几种类型的值,对其它类型无影响。C99规定整型提升适用于以下几种情况:

  1. 如果一个函数的形参类型未知,或者函数的参数列表中有...,那么调用函数时要对相应的实参做整型提升。此外,相应的实参如果是float类型的也要被提升为double类型,这条规则称为Default Argument Promotion(默认参数提升)。
  2. 算术运算中存在类型转换,有符号或无符号的char型、short型在做算术运算之前首先要做整型提升,然后才能参与计算。例:
unsigned char c1 = 255, c2 = 2;
int n = c1 + c2;

Usual Arithmetic Conversion(常用算数转换)

  1. 如果有一边的类型是long double,则把另一边也转成long double
  2. 否则,如果有一边的类型是double,则把另一边也转成double
  3. 否则,如果有一边的类型是float,则把另一边也转成float
  4. 否则,两边应该都是整型,首先按上一小节讲过的规则对ab做整型提升,如果类型仍不相同,则需要继续转换。首先我们规定charshortintlonglong long的转换级别( Integer Conversion Rank) 一个比一个高,同一类型的有符号和无符号数具有相同的Rank。转换规则如下:
    1. 如果两边都是有符号数,或者都是无符号数,那么较低Rank的类型转换成较高Rank的类型。例如unsigned intunsigned long做算术运算时都转成unsigned long
    2. 否则,如果一边是无符号数另一边是有符号数,无符号数的Rank不低于有符号数的Rank,则把有符号数转成另一边的无符号类型。例如unsigned longint做算术运算时都转成unsigned longunsigned longlong做算术运算时也都转成unsigned long
    3. 剩下的情况是:一边有符号另一边无符号,并且无符号数的Rank低于有符号数的Rank。这时又分为两种情况,如果这个有符号数类型能够覆盖这个无符号数类型的取值范围,则把无符号数转成另一边的有符号类型。例如遵循LP64的平台上unsigned intlong在做算术运算时都转成long
    4. 否则,也就是这个有符号数类型不足以覆盖这个无符号数类型的取值范围,则把两边都转成有符号数的Rank对应的无符号类型。例如在遵循ILP32的平台上unsigned intlong在做算术运算时都转成unsigned long

赋值产生的类型转换

如果赋值或初始化时等号两边的类型不相同,则编译器会把等号右边的类型转换成等号左边的类型再做赋值。函数参数、返回值也是赋值的过程,也是同理的。

强制类型转换

例如计算表达式(double)3 + i,首先将整数3强制转换成double型(值为3.0),然后和整型变量i相加,这时适用Usual Arithmetic Conversion规则,首先把i也转成double型,然后两者相加,最后整个表达式也是double型的。

编译器处理类型转换

现在要把一个M位的类型(值为X)转换成一个N位的类型,所有可能的情况如下表所示。实际上下图中容易出错的做法很少出现,全部转换不需要记住,遇到bug能想到这方面现查即可。

typedef 数据类型别名

typedef可以给一个数据类型起别名,下面是一个例子。

typedef unsigned int u32;

typedef struct complex_struct
{
    double x, y;
} complex_struct;

代码中,我们给unsigned int起了别名u32,给struct complex_struct起了别名complex_struct,使用这些数据类型声明变量时我们可以直接使用别名。

注意:不要滥用别名!过度使用别名是一种画蛇添足的行为,不仅不会提升代码可读性,反而会严重降低代码可读性。

void类型

C语言中有一种特殊的“空”类型void,它也有对应的指针类型void *(参考指针相关章节)。void类型单独使用没有意义,它的意义在于对函数参数和函数返回进行限定。

void printMessage(void)
{
    printf("Hello, world!\n");
}

参数中的void表示该函数不接受任何参数,返回值中的void表示该函数不返回任何值。

sizeof操作符

sizeof是一个特殊的运算符,它能返回变量或类型在内存中分配空间时分配的字节数。

#include <stdio.h>

int main(void)
{
    printf("%zu\n", sizeof(int));
    printf("%zu\n", sizeof 1);
    return 0;
}
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap