扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
VC和GCC默认的都是4字节对齐,编程中可以使用#pragma pack(n)指定对齐模数。出现以上差异的原因在于,VC和GCC中对于double类型的对齐方式不同。
成都创新互联坚持“要么做到,要么别承诺”的工作理念,服务领域包括:网站设计制作、成都做网站、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的孟连网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!
Win32平台下的微软VC编译器在默认情况下采用如下的对齐规则: 任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始。
Linux下的GCC奉行的是另外一套规则:任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如long,double)都以4为对齐模数。
复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。
struct{char a;double b;}
在VC中,因为结构中存在double和char,按照最长数据类型对齐,char只占1B,但是加上后面的double所占空间超过8B,所以char独占8B;而double占8B,一共16Byte。
在GCC中,double长度超过4字节,按照4字节对齐,原理同上,不过char占4字节,double占两个4字节,一共12Byte。
现象1: 提示gas gld 比识别
措施: gnu编译器发展到后来,越来越流行,更多使用别名为 as ld gcc等.
现象2: 提示字节对齐需要是 2的倍数
措施: 具体解决方法: 利用命令 sed -i 's/align 2/align 4/g' filename 替换align 2 为 align 4(align 3 替换为 align 8)
sed -i 's/align 2/align 4/g' boot/head.s
sed -i 's/align 3/align 8/g' boot/head.s
现象3: -fcombine-regs -mstring-insns选项不识别
措施: 此两个选项已经过时,直接去掉即可
现象4: warning 特别多
措施: 将-Wall 替换为 -w
现象5: __stack_chk_fail 未定义
措施: 去网上搜了一下,在Makefile中的$(CFLAGS)后面加上-fno-stack-protector,即不需要栈保护
现象6: main.c 中_syscall0重复定义
措施: main.c static inline _syscall0(int, fork) 去掉static即可
现象7: 提示内嵌汇编不符合语法限制
措施: 类似的问题在后面编译中出现好多,C内嵌汇编的格式 asm (汇编语句:输入寄存器:输出寄存器:可能被修改的寄存器),最新的GCC规定 输入或输出寄存器不能出现在可能被修改的寄存器中,目前看到网上的方法是把所有类似问题的可能被修改的寄存器全部删掉 解决方案:find -type f -exec sed -i 's/:"\w{2}"(,"\w{2}") )/:) /g' {} ; 其中's/:"\w{2}"(,"\w{2}") /:/g'
现象8: 在 control.c 中清楚定义了 static unsigned char attr = 0x70 ,而在链接 control.o 时,却爆出 attr未定义。
措施: 用 nm -C control.o 查看其符号,发现attr确实处于未定义状态。故单独编译一个小程序定义静态变量,查看其 .o 文件中,发现静态变量定义正常。故考虑为编译选项差异导致,最终发现因为 -O 编译优化选项导致,目前处理方式是去掉该选项。
现象9: build.c:(.text+0xde): undefined reference to `MAJOR'
措施: 通过分析编译打印信息,发现编译时没有加入头文件路径 -Iinclude
现象10: fs/fs.o: In function check_disk_change':(.text+0x1b2f): undefined reference to invalidate_buffers'
措施: 查找发现此函数定义在buffer.c 中,且为内联函数, 故尝试将其更改为普通函数, 然后编译通过.
现象11: 编译 build.c 时报错:/usr/include/i386-linux -gnu/bits/stdio2.h:57:8: error: unknown type name ‘__gnuc_va_list’
措施: 分析发现时此系列错误均由 -Iinclude 选项导致, 而该选项在 想象9 中加入, 故考虑去掉该选项, 直接在build.c 中加入 MAJOR 宏定义.
#pragma pack(n) n的取值可以为1、2、4、8、16,在编译过程中按照n个字结对齐 没听说过有n=3的情况...
16的话,大概是因为lz的gcc默认是64位的编译吧,如果是64位的话,可以看一下int的字节数,应该是8吧,那当然是以8为模了 我的是32位的,所以对齐模数默认是4,12没有问题
1.平台原因:不是所有的硬件平台都能访问任意地址上的任何数据;某些硬件平台智能在某些地址处取特定类型的数据,否则抛出硬件异常。
2.性能原因:数据结构(尤其是栈)应该尽可能在自然边界上对齐,原因在于,为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问。
规则:
数据成员对齐规则:结构(struct)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
结构(struct)的整体对齐规则:自数据成员完成各自对齐后,结构本身也要对齐,对齐将按照#pragma pack指定的数值和结构最大数据成员长度中,比较小的那个进行
结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储
假设CPU要读取一个4字节大小的数据到寄存器中(假设内存读取粒度是4)
1.数据从0字节开始(内存对齐)
2.数据从1开始(内存不对齐)
当数据从0字节开始的时候,直接将0-3四个字节完全读取到寄存器,结算完成
当数据从1开始的时候,问题很复杂,首先将前4个字节读到寄存器,再次读取4-7字节的数据进寄存器,接着把0字节,567字节的数据剔除,最后合并1234字节的数据进寄存器,对一个内存未对齐的寄存器进行了这么多额外操作,大大降低了CPU的性能。
这还属于乐观情况(性能原因),还有平台的移植原因,因为只有部分CPU肯干,其他部分CPU遇到未对齐边界就直接罢工了
字节对齐:
1.第一个成员在与结构体变量偏移量(offset)为0的地址处。
2.其他成员变量要对齐到对齐数的整数倍的地址处
(1)对齐数=对齐系数与该成员大小的较小值。
(2)如果有宏定义 #pragma pack(n); 则它中的n 就是对齐系数。
(3)VS中默认的对齐系数为8,linux中的默认为4.
3.结构体总大小为最大对齐数(每个成员变量除了第一个都有对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,
结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流