曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 4468|回复: 0
打印 上一主题 下一主题

sizeof() 用法

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2009-8-16 22:00:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
1、结构体大小

测试代码
#include <stdio.h>

struct demo {
    char c;
    int i;
} test;


int main()
{
    test.c = 'X';
    test.i = 8;

    printf("struct demo sizeof : %d\n", sizeof(struct demo));
    printf("struct test sizeof : %d\n", sizeof(test));
   
    return 0;
}
运行及输出
beyes@linux-beyes:~/C/base> ./sizeof.exe
struct demo sizeof : 8
struct test sizeof : 8
说明
按照预想,输出应该为 5 ( 1 个字节的 char 和 4 个字节的 int 类型 ),但输出为 8。究其原因,是涉及到了字节对齐。因为字节对齐可以加快计算机的读取速度,不然就得增加指令和多花指令周期。为此,编译器默认会对结构体进行处理,实际上其他地方的数据变量也是如此。让宽度为 2 的基本数据类型 ( 如 short 类型 )都位于被 2 整除的地址上,让宽度为 4 的基本类型 ( 如 int 类型 )都位于被 4 整除的地址上,以此类推。这样,两个数的中间就有可能需要加入填充字节,所以整个结构体的 sizeof 的值就增长了。对上面的例子,用 GDB 调试跟踪看看:
$1 = (struct demo *) 0x804a01c
(gdb) x/16b 0x804a01c
0x804a01c <test>:    0x58    0x00    0x00    0x00    0x08    0x00    0x00    0x00
在 0x58 后面填充了 3 个字节0x00 .

字节对齐和编译器实现有关,一般情况下满足 3 个准则:
      结构体变量的首地址能够被其最宽基本类型成员的大小整除。
      结构体每个成员相对于结构体首地址的偏移量 (offset) 都是成员大小的整数倍,如果需要编译器会在成员之间加上填充字节( internal adding )。
      结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节 (trailing padding)。
对于上面第  3 条,如果把上面程序中的结构体中的成员 c 和 i 的定义调换一下顺序,sizeof() 的结果同样是 8 ,这是因为在 char c; 后面同样的填充了 3 个字节。

对于上面第 2 条,结构体某个成员相对于结构体首地址的偏移量可以通过宏 offsetof() 来获得,如下代码:
#include <stdio.h>
#include <stddef.h>

struct demo {
    char c;
    int i;
    char name[20];
} test;

int main()
{

    test.c = 'X';
    test.i = 8;

    printf("struct demo sizeof : %d\n", sizeof(struct demo));
    printf("struct test sizeof : %d\n", sizeof(test));
    printf("test.name offset is : %d\n", offsetof(struct demo, name));   

    return 0;
}
运行及输出
beyes@linux-beyes:~/C/base> ./sizeof.exe
struct demo sizeof : 28
struct test sizeof : 28
test.name offset is : 8
关于 offsetof() 宏的详细说明见:http://www.groad.net/bbs/read.php?tid=1040

字节填充例2
#include <stdio.h>
#include <stddef.h>

struct demo {
    char c;
    int i;
    char name[20];
} test;

struct student {
    char name[10];
    int  date;
    //struct demo test;
    double k;
};

int main()
{
    test.c = 'X';
    test.i = 8;
   
    printf("struct demo sizeof : %d\n", sizeof(struct demo));
    printf("struct test sizeof : %d\n", sizeof(test));
    printf("test.i offset is : %d\n", offsetof(struct demo, name));   
    printf("---------------------------------------------------\n");
    printf("struct student sizeof is : %d\n", sizeof(struct student));
    printf("student.k offsetof is : %d\n", offsetof(struct student, k));
    return 0;
}
运行及输出
beyes@linux-beyes:~/C/base> ./sizeof.exe
struct demo sizeof : 28
struct test sizeof : 28
test.i offset is : 8
---------------------------------------------------
struct student sizeof is : 24
student.k offsetof is : 16
上面,对于结构体 student 的大小是 24,其中 k 元素的偏移为 16。这里的填充基于的原则还是数据的存储对齐:k 是 double 类型,8 个字节; date 是 int 型 ,4 个字节;char name[10] 数组,原本是 10 个字节。但 char name[10] 数组可分为两部分来看,(8 + 2) 个字节,其中前面 8 个字节是 4 个字节的 2 倍,而剩下的 2 个字节需要再填充 2 个字节才能是 4 个字节的 1 倍。所以,整个结构体被填充了 2 个字节,从而是 24 个字节,也就是 k 的偏移为 16 。

基本类型有有 char , short , int , float ,double 等内置数据类型,这些数据类型的宽度用 sizeof() 来计算。对于字节填充,涉及到一个“最宽简单类型” 的概念。最宽简单类型可以描述为,是其所有小于它的类型的整数倍,而又能被大于它的数据类型整除。在满足此条件后,在出现的所有符合这个规律的数据类型中,最宽的那个。比如在只有 char 与 short 两个类型的结构体中,最宽简单类型是 short ,如下测试代码:
#include <stdio.h>
#include <stddef.h>

struct max {
        char c;
        short x;
        char u;
};

int main()
{

        printf("struct max sizeof is : %d\n", sizeof(struct max));
        return 0;
}
输出结果
struct max sizeof is : 6
由输出可见,最小类型 char 再填充 1 个字节后,便成最宽简单类型 short 。

再如,在 char, short, int, double 里,最宽简单类型应该是 int (由于是“简单“的,那么表名这个数据类型在所有出现的,既有比它大的也有比它小的类型里,它肯定不会是最小的那个也不会是最大的那个)。如下测试代码:
#include <stdio.h>
#include <stddef.h>

struct demo {
    char c;
    int i;
    char name[20];
    double m;
} test;

struct student {
    char name[10];
    int  date;
    struct demo test;
    double k;
};


int main()
{
    test.c = 'X';
    test.i = 8;
   
    printf("struct demo sizeof : %d\n", sizeof(struct demo));
    printf("struct test sizeof : %d\n", sizeof(test));
    printf("test.i offset is : %d\n", offsetof(struct demo, name));   
    printf("---------------------------------------------------\n");
    printf("struct student sizeof is : %d\n", sizeof(struct student));
    printf("student.k offsetof is : %d\n", offsetof(struct student, k));
    return 0;
}
运行及输出
beyes@linux-beyes:~/C/base> ./sizeof.exe
struct demo sizeof : 36
struct test sizeof : 36
test.i offset is : 8
---------------------------------------------------
struct student sizeof is : 60
student.k offsetof is : 52
上面程序,student 结构体中还包含了demo 型结构体 test。这里比较最宽简单类型不会把 test 这个结构体作为一个整体来看待,而是将其中的各个元素打散来看。 如此以来,student 型结构体中,最宽的数据类型是 double ,共占 8 个字节,而最小的数据类型是 char ,占据 1 个字节,最宽简单类型自然是 int 型,它是 char 的 4 倍,又能被 double 整除。基于此,容易分析出 student 的宽度为 60,student.k 的偏移量为 52:
char name[10] 为 8 + 2 个字节,最后 2 个字节被填充为 4 字节 (最宽简单类型 int 的字节数),共占 12 个字节;
int date 占 4 个字节;
test 中,char c 为 4 个字节,int i 为 4 个字节,char name[20] 为 20 个字节 (20/4 = 5) ,double m 为 8 个字节;
double  k 为 8 个字节。
总共:12 + 4 + 4 + 4 + 20 + 8 + 8 = 60 个字节,k 前面偏移了 52 个字节。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-5-12 20:36 , Processed in 0.079595 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表