15.2.4 sizeof运算符与typedef类型声明
sizeof是一个很特殊的运算符,它有两种形式:"sizeof 表达式"和"sizeof (类型名)"。这个运算符很特殊,"sizeof 表达式"中的子表达式并不求值,而只是根据类型转换规则求得子表达式的类型,然后把这种类型所占的字节数作为整个表达式的值。有些人喜欢写成"sizeof(表达式)"的形式也可以,这里的括号和return(1)的括号一样,不起任何作用。但另外一种形式"sizeof(类型名)"的括号则是必须写的,整个表达式的值即为这种类型所占的字节数。
比如用sizeof运算符求一个数组的长度:
- int a[12];
- printf("%u\n", sizeof a/sizeof a[0]);
注意sizeof a中的a做左值,表示整个数组,而不是做右值转换成指向首元素的指针。表达式a的类型在编译时就可以确定,所以sizeof a的值在编译时计算 ,12×4=48, sizeof a[0]的值是4,所以sizeof a/sizeof a[0]是常量表达式,在编译时就被替换成12了。事实上,即使写成sizeof i++,表达式i++也不求值,求sizeof i++就是求表达式i++的类型所占的字节数,i++的类型在编译时就知道了,不需要到运行时求值之后才知道。
sizeof运算符的结果是size_t类型的,这个类型定义在stddef.h头文件中,不过你的代码中只要不出现size_t这个类型名就不用包含这个头文件,比如像上面的例子就不用包含这个头文件。C标准规定size_t是一种无符号整型,编译器可以用typedef做一个类型声明:
- typedef unsigned long size_t;
那么size_t就代表unsigned long型。不同平台的编译器可能会根据自己平台的具体情况定义size_t所代表的类型,比如有的平台定义为unsigned long型,有的平台定义为unsigned long long型,C标准规定size_t这个名字就是为了隐藏这些细节,使代码具有可移植性。所以注意不要把size_t类型和它所代表的真实类型混用,例如:
- unsigned long x;
- size_t y;
- x = y;
如果在一种ILP32平台上定义size_t代表unsigned long long型,这段代码把y赋给x时就把高位截掉了,结果可能是错的。注意上面的printf中使用了%u转换说明,表示后面的参数是无符号整型,这是一个小细节,通常用%d也可以,但在一些极限情况下可能会出问题,比如后面的参数值很大,结果打印出来是负数。
typedef这个关键字用于给某种类型起个新名字,比如上面的typedef声明就可以这么看:去掉typedef就成了一个变量声明unsigned long size_t,size_t是一个变量名,类型是unsigned long,那么加上typedef之后,size_t就是一个类型名,就代表unsigned long类型。再举个例子:
- typedef char array_t[10];
- array_t a;
这相当于声明char a[10]。类型名也遵循标识符的命名规则,并且通常加个_t后缀表示Type。C标准库的头文件stdint.h中定义了很多这样的类型名,如表15.1所示,使用这些类型名编写代码就可以屏蔽ILP32和LP64之间的差异了:
表15.1 stdint.h中定义的部分类型名
|
类型名< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> |
说明 |
|
int8_t int16_t int32_t int64_t |
8位、16位、32位、64位的有符号整数 |
|
uint8_t uint16_t uint32_t uint64_t |
8位、16位、32位、64位的无符号整数 |
|
intptr_t |
一种有符号整数类型,指针类型可以转换成这种类型而不丢失信息 |
【责任编辑:董书 TEL:(010)68476606】