sizeof 和 结构体

    sizeof是运算符,可用于任何变量名、类型名或常量值,当用于变量名(不是数组名)或常量时,它不需要用圆括号。结果类型是size_t,它在头文件中typedef为unsigned int类型。主要有四种语法形式:

        1. sizeof object

        2. sizeof(object)

        3. sizeof type_name

        4. sizeof(type_name)

//自定义类型
class A
{};
//声明一个变量
int i;
A a;
//下面几种情况
sizeof i;       // =4
sizeof(i);      // =4
sizeof a;       // =1
sizeof(a);      // =1
sizeof int;     // 错的
sizeof(int);    // =4
sizeof string;  // =32
sizeof(string); // =32
sizeof 1;              // =4
sizeof 1>2?true:1.0;   // 错的
sizeof(1>2?true:1.0);  // =8
sizeof 2+3.1;          // =7.1

    它在编译时起作用,而不是运行时。

    这里统一一下,在32位操作系统下,一个字为4个字节,char占1个字节,short占2个字节,int占4个字节,double占8个字节,string占32个字节,指针占4个字节。 
 
// 1.只有一个 int 的结构体
struct S1
{
    int a;
};
sizeof(S1)=4

// 2.有 int , char 两个属性的结构体
struct S2
{
    int a;
    char b;
};
sizeof(S2)=8

// 换一下位置
struct S3
{
    char a;
    int b;
};
sizeof(S3)=8

// 3.有 char , char , int 三个属性的结构体
struct S4
{
    char a;
    char b;
    int c;
};
sizeof(S4)=8

// 换一下位置
struct S5
{
    char a;
    int b;
    char c;
};
sizeof(S5)=12

// 5.包含其他结构体的结构体
struct S6
{
    char a;
    S5 b;
    double c;
};
sizeof(S6)=24
 
// *6.包含指针的结构体
struct S7
{
    char *a;
};
sizeof(S7)=4
sizeof(结构体)有三条规则:
        1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
        2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
        3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
 
根据我个人的理解,有下列几条:
        1) 最宽基本类型成员,指的是占的字节最多的内置类型。S1-5中最宽基本类型都是int。再看S6中,在计算最宽基本类型的时候要分解S5,其中最宽的是int,比double小,所以取double,即8个字节作为单元。(注意string类型其实也是复合的,可以看成char数组)
        2) 填充字节,我们可以用 _  表示一个空的字节。填充字节会发生在三种地方,对应上述三条规则,等下分析。
        3) 对结构体首地址的偏移量,可以用数格子的方法来判断,例如S3,a _ _ _ b b b b,b的偏移量就是4,;S5 ,a _ _ _    b b b b    c _ _ _ ,c的偏移量就是8
        4) 分析下填充字节出现的三个场景:
                1. 第一个“结构体变量的首地址能够被其最宽基本类型成员的大小所整除”,参考S6中的S5来看,如果首地址为0,那么a就占了0的位置,那么b呢?你会不会想到b占的是第8个位置,即a _ _ _    _ _ _ _    b ……。如果是这样那么可能你理解错一句话,“其最宽基本类型成员的大小”,这里指的是S5中最宽基本类型成员 int 而不是S6中的double,即a _ _ _ b …
这里就发生了符合规则一的填充字节。为了验证正确性,如下: 
S6 s;
cout<<(long)&(s.a)<<endl; // 1767000 a的首地址
cout<<(long)&(s.b)<<endl; // 1767004 b的首地址
cout<<(long)&(s.c)<<endl; // 1767016 c的首地址
// a和b之间差了4个字节
                2. 第二个“结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding”,参考S5来看,a占0的位置,那么b呢?按照这个规定,应该是a _ _ _    b b b b。如果这里将b从int 改为short,那么应该是a _ b b  …… 。举例如下:
S5 s;
cout<<(long)&(s.a)<<endl; // 1833640 a的首地址 
cout<<(long)&(s.b)<<endl; // 1833644 b的首地址
cout<<(long)&(s.c)<<endl; // 1833648 c的首地址
               3. 第三个“结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)”,参考S5来看,我们之前分析了a _ _ _    b b b b    c ,那么一共需要9个字节,最宽基本类型成员是int,根据这个规则需要在末尾填充3个字节,即a _ _ _   b b b b    c _ _ _ 。不举例了,看总大小就好。
         试试下面的例子:
// 最宽基本类型成员为short ,大小为2
struct A1{
    char a;
    short b;
    char c;
};
sizeof(A1)=6   //(a _ b b c _ )
// 最宽基本类型成员为short ,大小为2
struct A2
{
    char a;
    S5 b;
    char c;
};
sizeof(A2)=10  //( a _ ( a _ b b c _ ) c _ )
// 最宽基本类型成员为double ,大小为8
struct A3
{
    char a;
    A1 b;
    double c;
};
sizeof(A3)=16  //( a _ ( a _ b b c _ ) c _ _ _ _ _ _ _ )
 
现在来看下几个比较特殊的举例:
// *7.包含static的结构体
struct S7
{
    char* a;
};
sizeof(S7)=4

// *8.包含static的结构体
struct S8
{
    static int a;
};
sizeof(S8)=1

// *9.包含array的结构体
struct S9
{
    char a[10];
    int b;
};
sizeof(S9)=4
    
        5) 参考S7,对于结构体内有指针的情况,无论指针是什么类型的,都是一个字的长度(因为他只是存储一个内存地址,跟具体类型没关系),对于32位系统来说就是4个字节(我记得是)。所以关于指针的大小可以类似参考int型。

        6) 参考S8,对于含有static的结构体来说,sizeof是不算在大小里面的。我在网上看到一种说法,说sizeof只计算栈内的元素,而静态变量不计算在栈中,所以不计算在内。(空结构体的大小不是0,而是1,“空结构体”变量也得被存储,这样编译器也就只能为其分配一个字节的空间用于占位了。)

        7) 参考S9,对于含有array数组的结构体来说,其最宽基本类型成员为数组的类型,大小为 number * sizeof( 数组类型 )。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值