免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: embeddedlwp
打印 上一主题 下一主题

struct 对齐 [复制链接]

论坛徽章:
0
11 [报告]
发表于 2011-11-21 15:01 |只看该作者
本帖最后由 Aquester 于 2011-11-23 09:35 编辑
回复  windy2335

咦, 不对, 我做了个实验,

  2 #include
  3 #include
  4
  5 struct A{ ...
zylthinking 发表于 2011-11-21 12:37



没说要是按4字节对齐的,那个最大不是指那个最大,而是按以下两个最小的对齐:

成员中最大的一个类型(alignof值,不是sizeof值)
编译指标器(#pragma pack(X),x86机器上默认X一般是机器字长4,但每个成员的类型如果都小于4,那则不是按4了,而是按成员中最大的一个

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
12 [报告]
发表于 2011-11-21 15:29 |只看该作者
没说要是按4字节对齐的,那个最大不是指那个最大,而是按以下两个最小的对齐:

成员中最大的一个类 ...
Aquester 发表于 2011-11-21 15:01


 你的理解和我之前的一样, 呵呵, 仔细看看六楼吧

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
13 [报告]
发表于 2011-11-21 19:09 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
14 [报告]
发表于 2011-11-21 19:47 |只看该作者
按照字长对齐,32位机器就是4 Bytes.
至于int c[0]的意思是最后有多少到下一个能开始给int的位置之前Padding上.

论坛徽章:
0
15 [报告]
发表于 2011-11-21 20:43 |只看该作者

论坛徽章:
0
16 [报告]
发表于 2011-11-21 21:49 |只看该作者
回复 11# Aquester


    这是gnu的c用法,0长度数组是不占内存的~!具体你可以搜索网上的具体用法!

论坛徽章:
0
17 [报告]
发表于 2011-11-21 22:23 |只看该作者
本帖最后由 keytounix 于 2011-11-22 23:33 编辑

我也来说说看法,不对的地方请大家指出来。谢谢
1. sizeof 一个 结构体,说的的数据 和
结构体的布局是有很大的关系的
典型的 问题 就是 成员对换了一个 位置,
sizeof计算的结果就不对了
百度一下
正好能搜索到这方面的一个例子
http://bbs.pfan.cn/post-232854.html

2.
strcut 结构体关于 type  array[0]的问题
这个是gnu c的扩展,
他意味着
struct拥有对其后 type型数据访问的权限,

一般
struct  T
{
typea arg1;
typeb arg2;
.....
typex  x[0];
};

像这个结构体,x[0]位于最后
在编译器编译的时候
会不会为x[0]分配空间呢?

如果x[0]位于中间,
会不会为其奉陪空间呢?
这个我们在最后讨论

但是 会为 T考虑空间的布局问题
-----即 T后面会紧跟一个typex型数据

struct T
{
char a;
char b;
int a[0];
}  test;

经过该语句后
printf("addr(T)=0x%p\n",&test);


该语句打印出来的 数据 将会是 4的倍数
假设为
addr(T)=0xffab04;

则存储图如下----
请别纠结大小端,只是举个列子而已!

0xffab04: a
0xffab05:   b
0xffab06:   ?
0xffab07:   ?
0xffab08:   ?

现在的问题是
test能访问 其后的 一个的地址,
这个地址存放的是整形变量
那么这个整形变量该从什么地方开始存储?


这里有一个原则,就是 默认 __attribute__(aligned(x))
x为4,在没有对
struct T
{
char a;
char b;
int a[0];
};
进行形如
struct T
{
char a;
char b;
int a[0];
}__attribute__(aligned(x))
这般显式指定的情况下
另外
A)
__attribute__(aligned(x)) 中 x 必须为 2的冥 次方

B)
__attribute__(aligned(x)) 意味着组织存储单元的时候,
最小的块是 4个字节

要考虑一个数据存储的时候
首先考虑4字节存储单位中
空闲的空间,
如果装不下,那么就另外从一个存储单元开始


比如
test 后面需要考虑布置一个 int型变量

那么首先考虑 0xffab04开始的4 字节以内的存储 空间

发现只剩2字节了,装不下 int型,于是只得 重新 开始一个内存单元了
struct 把那空闲的2字节也就占为己有了

作为检测 如上 分析的正确性
可以对如下结构体进行检测

struct T
{
char a;
char b;
short c[0];
} t;
这个 sizeof(t)=2;
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. int main(int n,char *v[])
  4. {
  5. struct T
  6. {
  7. char a;
  8. char b;
  9. int c[0];
  10. } test;

  11. printf("addr(T)=0x%p\n",&test);
  12. printf("sizeof(T)=%d\n",sizeof(test));
  13. return 0;

  14. }
复制代码



struct T1
{
char a;
char b;
char c;
}
;
struct T0
{
char a;
char b;
struct T1 c[0];
}
这个

sizeof(struct T0)=2;
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. int main(int n,char *v[])
  4. {
  5. struct T0
  6. {
  7. char a;
  8. char b;
  9. char c;
  10. }t0;
  11. struct T
  12. {
  13. char a;
  14. char b;
  15. struct T0 c[0];
  16. } test;

  17. printf("addr(T)=0x%p\n",&test);
  18. printf("sizeof(T)=%d\n",sizeof(test));
  19. return 0;

  20. }
复制代码
3.  对于 __attribute__((aligned(x)))属性修饰的

struct T
{
char a;
char b;
short c[0];
}__attribute__((aligned(4)))
这个 sizeof(T) = 4


struct T
{
char a;
char b;
}__attribute__((aligned(4)))
这个 sizeof(T) = 4

struct T
{
char a;
char b;
}__attribute__((aligned())

代码如下
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. int main(int n,char *v[])
  4. {
  5. /*
  6. struct T0
  7. {
  8. char a;
  9. char b;
  10. char c;
  11. }t0;
  12. struct T
  13. {
  14. char a;
  15. char b;
  16. struct T0 c[0];
  17. } test;
  18. */
  19. struct T
  20. {
  21. char a;
  22. char b;
  23. }__attribute__((aligned(8))) test;

  24. printf("addr(T)=0x%p\n",&test);
  25. printf("sizeof(T)=%d\n",sizeof(test));
  26. return 0;

  27. }
复制代码
这个说明 按照 aligned(x)属性赋值的,sizeof() 的值都为x

4. 神奇的 array[0]

array[0]的空间是不自主的
这个是什么意思呢?他的意思是说他的空间大小是被动的
比如如下所示结构体中,
array[0]位于中间,
那么可以是sizeof可以是0
可以是1
还可以是2

在如下所示代码中

#include <stdlib.h>
#include <stdio.h>
#define dbg_adr(st,m) printf("addr :0x%p\n",&st.m)
int main(int n,char *v[])
{

struct T0
{
char a;
char b;
char c;
};
struct T
{
char a;
char b;
//char c;
//char d;
struct T0 e[0];
int f;
} test;
dbg_adr(test,a);
dbg_adr(test,b);
//dbg_adr(test,c);
//dbg_adr(test,d);
dbg_adr(test,e);
dbg_adr(test,f);

return 0;
}
所得结果类似如下

to@ubuntu:~/Desktop$ gcc t.c -o t
to@ubuntu:~/Desktop$ ./t
addr :0x0xbfd2f6f8
addr :0x0xbfd2f6f9
addr :0x0xbfd2f6fa
addr :0x0xbfd2f6fc


取消注释
所得结果如下
addr :0x0xbfd260a8
addr :0x0xbfd260a9
addr :0x0xbfd260aa
addr :0x0xbfd260ab
addr :0x0xbfd260ac
addr :0x0xbfd260ac   -------------注意最后的这俩行这个意味着 地址重叠,也就是sizeof为0

2.jpg (11.16 KB, 下载次数: 0)

2.jpg

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
18 [报告]
发表于 2011-11-22 02:43 |只看该作者
本帖最后由 zylthinking 于 2011-11-22 14:20 编辑
我也来说说看法,不对的地方请大家指出来。谢谢
1. sizeof 一个 结构体,说的的数据 和
结构体的布局是有 ...
keytounix 发表于 2011-11-21 22:23


被这个基本问题真的搞昏头了, 而且这个概念甚至是多年前就有的, 居然一直都是迷迷瞪瞪状态
做了几个不全面的实验, 有几个结论
1. 首先 gcc 这个 __attribute((aligned(x))) 和 VC 的 #pragma pack(x) 不是仅仅是拼写上的区别
    微软规则是结构体自身的对齐原则是 x 与成员对齐值取其小者
    因此这样的结构
#pragma pack(4)
  4 struct A{
  5     char a;
  6     char b;
  7     char c;
  8 };
#pragma pack()
结构体自身对齐值仍为 1, sizeof 的结果照旧是 3, 因此微软的存在一个默认 aligin 的概念, 32位上一般是 4

gcc 同样支持 #pragma pack, 而且语意相同, 但不同的是, 不存在默认 align(x) 这个论断

  4 struct A{
  5     char a;
  6     char b;
         char c;
  7 //};---------------------------------------------sizeof == 3
  8 }__attribute__((aligned(4)));   -----------sizeof == 4
  9

这个__attribute__((aligned(4))) 似乎强调的是整个结构体就是4字节对齐, 或者至少是4 << N 对齐, 这样其实也是 4 字节对齐, 只不过恰好是倍数而已。
如果 aligned(x) 和 #prama pack 合用, 区别就很明显了

16 #pragma pack(2)
17
18 struct C{
19     char c;
20     double d;
21 }__attribute__((aligned(4)));
22 #pragma pack()
23

经验证,  d 确实2字节对齐的, 但 sizeof(C) 确实 12, 意味着, double d 后存在两个 padding

考虑到数组的地址连续需要保证, 因此对结构体长度的要求也应该是保证数组中下一个元素也能达到其自身对齐要求。
如果数组中每一项都达到对齐要求, 需要的是每一项长度应该都是对齐值的整数倍, 也就是说, sizeof(A) % alignof(A) == 0
再考虑A自身对齐, 如果其中某个成员 m, 则要求 (N * alignof(A) + offsetof(m)) % alignof(m) == 0;
因为 offsetof(m) 是固定的, 如果 offsetof(m) % alignof(m) != 0,
则 offsetof(m) > 0, (k * alignof(A) + offsetof(m)) % alignof(m) == 0; (k >= 1)
并且  ((k + 1) * alignof(A) + offsetof(m)) % alignof(m) == 0; 因此 alignof(A) % alignof(m) == 0
因此 offsetof(m) % alignof(m) == 0, 和假设矛盾, 因此假设错误,
因此必有 offsetof(m) % alignof(m) == 0, 那么则 N * alignof(A) % alignof(m) == 0, 设 N == 1,
结论1: 则alignof(A) % alignof(m) == 0,既然 m 是 alignof(m) 是任意的 (2, 4, 8 ..) 要达到要求, 必然 alignof(A) 是结构体中最大成员对齐值, 并且 sizeof(A) 必然是 alignof(A) 的整数倍


下面分析一个例子, 看看是不是, 两个 sizeof 都是输出 8, 对齐值没计算(头已经晕了):
  3
  4 struct x{
  5     char a;
  6     char b;
  7     char c;
  8 //};
  9 }__attribute__((aligned( 8 )));
10

根据之前对 __attribute__ 的理解, 他的align 值是 8;(或者16, 32.... 这个需要再琢磨一个例子出来验证), sizeof 是8的整数倍, 并满足 >= 3, 则也是 8

11 struct A
12 {
13     char a;
14     char b;
15     struct x c[0];
16 //};
17 }__attribute__((aligned(4)));
18

A 按理说应该是 4对齐(或者8, 16.... 这个需要再琢磨一个例子出来验证),
按结论1: A对齐值需要是成员中最大的, 不过不考虑c, 则很显然 4即满足; 但c[0] 要求后面跟着的x按x自身对齐, 而 b 和 c 之间因为 c对齐存在空洞应该算在A上面, 其实就是和

11 struct A
12 {
13     char a;
14     char b;
15     struct x c;
16 //};
17 }__attribute__((aligned(4)));
18

一个意义, 既然将 c 看作其中一个成员, 那么按结论1,  A 的对齐值应该是x 的对齐值, 也就是 8, sizeof(A) 是 sizeof(X)的整数倍, 返回 c[0], 即减去一个sizeof(X), 那么剩下的还是 sizeof(X)的整数倍, 因此是8

那么
11 struct A
12 {
13     char a;
14     char b;
15     struct x c[0];
16 };
sizeof 是多少呢, 按上面分析, 应该和上面一样, 实际验证是正确的。

验证环境: ubuntu 8.04 x86 32位

论坛徽章:
0
19 [报告]
发表于 2011-11-22 09:05 |只看该作者
 你的理解和我之前的一样, 呵呵, 仔细看看六楼吧
zylthinking 发表于 2011-11-21 15:29



没看懂,请问理解哪儿出错了?我还没发现这个理解有错误,通过代码验证也是正确的。

论坛徽章:
0
20 [报告]
发表于 2011-11-22 09:37 |只看该作者
跟编译器优化有关
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP