c语言中判断数据类型长度符 sizeof
用法
1) sizeof(类型说明符,数组名或表达式);
2) sizeof 变量名
1. 定义:
sizeof是一个操作符,返回(unsigned int size_t)一个对象或者类型所占的内存字节数。
sizeof( char、signed char unsigned char ) == 1
2. 语法:
sizeof有三种语法形式
1) sizeof( object ); // sizeof( 对象 );
2) sizeof( type_name ); // sizeof( 类型 );
3) sizeof object; // sizeof 对象;
例如:
sizeof( 2 ); == sizeof( int );
sizeof( 2 + 3.14 ); == sizeof( double );
char foo(void);
sizeof( foo() ); == sizeof( char ); //其结果是函数返回类型的大小,函数并不会被调用
C99标准规定,函数、不能确定类型的表达式以及位域(bit-field)成员不能被计算sizeof值,即下面这些写法都是错的
sizeof( foo ); // error
void foo2() { }
sizeof( foo2() ); // error
struct S
{
unsigned int f1 : 1;
};
sizeof( S.f1 ); // error
3. sizeof的常量性
sizeof的计算发生在编译时刻,所以它可以被当作常量表达式使用,如:
char ary[ sizeof( int ) * 10 ]; // ok
最新的C99标准规定sizeof也可以在运行时刻进行计算,如下面的程序在Dev-C++中可以正确执行:(但在没有完全实现C99标准的编译器中就行不通了)
int n = 10; // n动态赋值
char ary[n]; // C99也支持数组的动态定义
printf("%d\n", sizeof(ary)); // ok. 输出10
4. 基本数据类型的sizeof
short、int、long、float、double由于和系统相关的,所以可能不同,可能会在程序移植造成麻烦。
一般的,在32位编译环境中,sizeof(int)的取值为4。
5. 指针变量的sizeof
指针,它记录了另一个对象的地址。那么它当然等于计算机内部地址总线的宽度。
所以在32位计算机中,指针变量的sizeof是4,64位系统的sizeof为8.
指针变量的sizeof值与指针所指的对象没有任何关系.
char* pc = "abc"; sizeof( pc ); // 结果为4
int* pi; sizeof( pi ); // 结果为4
string* ps; sizeof( ps ); // 结果为4
char** ppc = &pc; sizeof( ppc ); // 结果为4
void (*pf)(); sizeof( pf ); // 结果为4
6. 数组的sizeof
数组的sizeof值等于数组所占用的内存字节数,如:
char a1[] = "abc"; // 结果为4,一个NULL终止符
int a2[3]; // 结果为3*4=12(依赖于int)
求数组元素的个数
int c1 = sizeof( a1 ) / sizeof( int ); // 总长度/单个元素的长度
int c2 = sizeof( a1 ) / sizeof( a1[0] ); // 总长度/第一个元素的长度
写到这里,提一下:
void foo3(char a2[3]) //没有分配一个数组
{ int c2 = sizeof( a2 ); } // c2 == 4, 蜕变成指针
7. 结构体的sizeof
重要话题:字节对齐
原因:字节对齐有助于加快计算机的取数速度,否则就得多花指令周期了。
为此,编译器默认会对结构体进行处理(实际上其它地方的数据变量也是如此),
让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,
让宽度为4的基本数据类型(int等 )都位于能被4整除的地址上,以此类推。
这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。
一个结构体示例:
struct S1
{
char c;
int i;
}; VC6中sizeof(S1) == 8。
struct S2 交换位置
{
int i;
char c; sizeof(S1) == 8 //此后面有3个填充字节
};
struct S3 sizeof(S3) == 8
{
int i;
char c; offsetof(S3, c) == 4 相对首地址的偏移
char cc; offsetof(Sd, cc) == 5
};
字节对齐的细节和编译器实现相关,但一般满足三个准则:
1) 结构体变量的首地址能够被其 ‘最宽基本类型成员’大小所整除;
2) 每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,
3) 结构体的总大小为结构体‘最宽基本类型成员’大小的整数倍。
对于上面的准则,有几点需要说明:
1)某个成员相的地址偏移量可以通过宏offsetof()来获得,stddef.h中定义如下:
#define offsetof(s,m) (size_t)&(((s *)0)->m)
2)基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型
“数据宽度”就是指其sizeof的大小。
3)当是复合类型时,最宽基本类型在其子成员中找,而不是把复合成员看成是一个整体。
在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。
示例:
struct S3
{
char c1; c1的偏移量为0 ,占0-4 其大小为4,填充3个字节
S1 s; s 的偏移量为4 ,占5-12 其大小为8, 无填充
char c2; c2的偏移量为12,占13-13 其大小为8, 填充3字节
};
S1的最宽简单类型为int,
S3的最宽简单类型为int,(将S1“打散”看的)。
sizeof(S3)==16。
编译器的pack指令对sizeof的影响
它是用来调整结构体对齐方式的,VC6中通过#pragma pack实现,也可以直接修改/Zp编译开关。
#pragma pack的基本用法为:#pragma pack(n),其取值为1、2、4、8、16,默认是8
结构体成员的偏移 offsetof( .item ) = min( n, sizeof( .item ) )
示例:
#pragma pack(push) // 将当前pack设置压栈保存
#pragma pack(2) // 必须在结构体定义之前使用
struct S1
{
char c;
int i; min(2, sizeof(i))==2,所以i的偏移量==2,
}; sizeof(S1)==6
struct S3
{
char c1; c1的偏移量为0 ,占0-1 其大小为2,填充1个字节
S1 s; s 的偏移量为2 ,占2-7 其大小为6, 无填充
char c2; c2的偏移量为8 ,占8-9 其大小为2, 填充1字节
}; 所以sizeof(S3)==10
#pragma pack(pop) // 恢复先前的pack设置
空结构体
示例:
struct S5 { };
sizeof( S5 ); // 结果为1
试想一个“不占空间”的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢于是,于是为其分配一个字节的空间用于占位。
8. 含位域结构体的sizeof
使用位域的主要目的是压缩存储,其大致规则为:
1) 如果相邻位域字段的类型相同, 且其位宽之和 < 类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同, 但其位宽之和 >= 类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻位域字段的类型不同, VC6采取不压缩方式,Dev-C++采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
示例1:
struct BF1
{
char f1 : 3; 第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中
char f2 : 4; f3只能从下一个字节开始
char f3 : 5;
}; sizeof(BF1)==2
示例2:
struct BF2
{
char f1 : 3;
short f2 : 4;
char f3 : 5;
}; VC6中 sizeof(BF2)==6 Dev-C++中sizeof(BF2)==2
示例3:
struct BF3
{
char f1 : 3;
char f2;
char f3 : 5;
}; VC6和Dev-C++中 sizeof(BF2)==3
9. 联合体的sizeof
整个联合体的sizeof也就是每个成员sizeof的最大值。其成员也可以是复合类型,被作为整体考虑。
示例1:
union U
{
int i;
char c;
S1 s;
}; sizeof(U)==sizeof(s)。