超凡天赐 发表于 2017-4-29 20:17:15

这个结构体的对齐看了几遍都没看懂

失传的C结构体打包技艺(转)
http://bbs.fishc.com/thread-83418-1-1.html
(出处: 鱼C论坛)
尤其是后来为什么结构体内的变量,位置发生变化,就节省内存了呢?“结构体尾填充的通用法则是:编译器将会对结构体进行尾填充,直至它的跨步地址。这条法则决定了sizeof()的返回值。”这句话到底怎么理解“
@人造人 @zealstar @polt @四十二 @qq1242009750

人造人 发表于 2017-4-29 21:12:22

#include <stdio.h>

struct foo1
{
        char *p;
        char c;
        long x;
};

int main(void)
{
        printf("char * size: %d\n", sizeof(char *));
        printf("char size: %d\n", sizeof(char));
        printf("long size: %d\n", sizeof(long));

        putchar('\n');

        printf("foo1 size: %d\n", sizeof(struct foo1));

        putchar('\n');

        struct foo1 tmp;

        printf("&tmp = 0x%X\n", &tmp);
        printf("&tmp.p = 0x%X\n", &tmp.p);
        printf("&tmp.c = 0x%X\n", &tmp.c);
        printf("&tmp.x = 0x%X\n", &tmp.x);

        return 0;
}


char * size: 4
char size: 1
long size: 4

foo1 size: 12

&tmp = 0x1DFE4C
&tmp.p = 0x1DFE4C
&tmp.c = 0x1DFE50
&tmp.x = 0x1DFE54
请按任意键继续. . .

tmp的地址是 0x1DFE4C
因为p是tmp的第一个元素,所以tmp.p的地址也是 0x1DFE4C
因为p是char *类型,大小是4个字节,所以tmp.c的地址从 0x1DFE50 开始(0x1DFE4C + 4 = 0x1DFE50)
因为c是char 类型 ,大小是1个字节,所以tmp.x从0x1DFE51开始?
不,因为要对齐,所以tmp.x从 0x1DFE54开始(4字节对齐)
0x1DFE51   0x1DFE52   0x1DFE53    这3个内存空间就浪费了

你现在能明白为什么 foo1 size: 12 了吗?

人造人 发表于 2017-4-29 21:15:19

反汇编如下

22:         struct foo1 tmp;
    23:
    24:         printf("&tmp = 0x%X\n", &tmp);
00214CDClea         eax,
00214CDFpush      eax
00214CE0push      216CF0h
00214CE5call      00211352
00214CEAadd         esp,8
    25:         printf("&tmp.p = 0x%X\n", &tmp.p);
00214CEDlea         eax,
00214CF0push      eax
00214CF1push      216D00h
00214CF6call      00211352
00214CFBadd         esp,8
    26:         printf("&tmp.c = 0x%X\n", &tmp.c);
00214CFElea         eax,
00214D01push      eax
00214D02push      216E30h
00214D07call      00211352
00214D0Cadd         esp,8
    27:         printf("&tmp.x = 0x%X\n", &tmp.x);
00214D0Flea         eax,
00214D12push      eax
00214D13push      216E40h
00214D18call      00211352
00214D1Dadd         esp,8
    28:
    29:         return 0;
00214D20xor         eax,eax

超凡天赐 发表于 2017-4-29 22:17:55

人造人 发表于 2017-4-29 21:12
tmp的地址是 0x1DFE4C
因为p是tmp的第一个元素,所以tmp.p的地址也是 0x1DFE4C
因为p是char *类 ...

我的问题其实是这个,为什么结构体仅仅通过变换位置就可以节省内存?代码如下
#include <stdio.h>
struct foo7 {
    char c;
    struct foo7 *p;
    short x;
};
struct foo8 {
    struct foo8 *p;
    short x;
    char c;
};
int main()
{
    printf("%lu\n",sizeof(struct foo7));
    printf("%lu\n",sizeof(struct foo8));
    struct foo7 f7,f8;
    printf("%p\n",&f7.c);
    printf("%p\n",&f7.p);
    printf("%p\n",&f7.x);
    printf("%p\n",&f8.p);
    printf("%p\n",&f8.x);
    printf("%p\n",&f8.c);
    return 0;
}
运行结果
24
16
0x7fff5fbff710
0x7fff5fbff718
0x7fff5fbff720
0x7fff5fbff700
0x7fff5fbff708
0x7fff5fbff6f8

超凡天赐 发表于 2017-4-29 22:18:57

人造人 发表于 2017-4-29 21:15
反汇编如下

22:         struct foo1 tmp;


汇编我刚刚学,现在还看不太懂。

人造人 发表于 2017-4-29 22:38:51

超凡天赐 发表于 2017-4-29 22:17
我的问题其实是这个,为什么结构体仅仅通过变换位置就可以节省内存?代码如下

运行结果

http://blog.csdn.net/liukun321/article/details/6974282

原则一:结构体中元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每一个元素放置到内存中时,它都会认为内存是以它自己的大小来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始(以结构体变量首地址为0计算)。


原则二:在经过第一原则分析后,检查计算出的存储单元是否为所有元素中最宽的元素的长度的整数倍,是,则结束;若不是,则补齐为它的整数倍。

超凡天赐 发表于 2017-4-29 22:56:30

人造人 发表于 2017-4-29 22:38
http://blog.csdn.net/liukun321/article/details/6974282

原则一:结构体中元素是按照定义顺序一个一 ...

这篇文章解释的吼啊

人造人 发表于 2017-4-29 23:05:44

超凡天赐 发表于 2017-4-29 22:56
这篇文章解释的吼啊

能看懂吧

超凡天赐 发表于 2017-4-29 23:10:30

人造人 发表于 2017-4-29 22:38
http://blog.csdn.net/liukun321/article/details/6974282

原则一:结构体中元素是按照定义顺序一个一 ...

甲鱼老湿的那篇文章解释的不好,太啰嗦了,而且神翻译让我逻辑混乱。

超凡天赐 发表于 2017-4-29 23:14:49

人造人 发表于 2017-4-29 23:05
能看懂吧

懂了懂了

人造人 发表于 2017-4-29 23:27:51

超凡天赐 发表于 2017-4-29 23:14
懂了懂了

zealstar 发表于 2017-4-30 09:05:47

很早之前就有这样的感受,变量和变量会对齐,然后浪费一定的空间。
不过已经习以为常,没有深究的问题。

Caleb 发表于 2017-4-30 12:12:21

谢谢分享,学习了

四十二 发表于 2017-6-23 15:25:56

对齐一定有浪费的问题,意义在于方便管理空间里的数据,可以了解一下存储结构的分页机制,这也是快速检索必须的。

我跑题了,因为来晚了,扯点别的,给楼主捧场{:10_256:}
页: [1]
查看完整版本: 这个结构体的对齐看了几遍都没看懂