调试经验--字节对齐

来源:互联网 发布:怪物猎人角色数据 编辑:程序博客网 时间:2024/05/21 10:35
调试经验--字节对齐

        在使用C语言开发时,有时会遇到内存中数据对齐的问题,如果对齐问题没有判断正确,很可能导致结果完全异于预期。
        我们在调试程序时,在数据对齐方面遇到一些问题,也做了一些总结:
        1,C语言默认对齐方式;
        2,C语言强制指定对齐方式;
        3,指针数据类型转换;
        4,一个字节对齐问题的例子。

1,C语言默认对齐方式

        在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。
        结构体成员对齐有一个重要的条件,即每个成员分别对齐.即每个成员按自己的方式对齐.其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(若未指定,就是最大的成员对齐参数)中较小的一个对齐.并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节.
        这里有三点很重要:
1.每个成员分别按自己的方式对齐,并能最小化长度。
2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。
3.对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。
        对于数组,比如:
char a[3];这种,它的对齐方式和分别写3个char是一样的.也就是说它还是按1个字节对齐。
如果写: typedef char Array3[3];
Array3这种类型的对齐方式还是按1个字节对齐,而不是按它的长度。
不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64....中的一个。

2,C语言强制指定对齐方式
        可以指定为按1字节对齐,如下:
        #pragma pack(push,PACK1,1)
        或:#pragma pack(1)
        或指定为按4字节对齐,如下:
        #pragma pack(push,PACK1,4)
        或:#pragma pack(4)
        注意,要恰当的取消对齐方式,否则,强制的对齐方式会辐射影响所有相关文件,带来意料之外的结果,例如,不同文件中,同一个结构体的sizeof结果不同!!
        (包含结构体的头文件在不同文件中包含,又有其他头文件中指定了不同的对齐方式)
        #pragma pack(pop,PACK1)
        或:#pragma pack()
        按照cpu的字长进行对齐,运行效率高。

#pragma pack(8)
struct s1{
 short a;
 long b;
};
struct s2{
 char c;
 s1 d;
 long long e;
};
#pragma pack()
问 
1.sizeof(s2) = ?
2.s2的c后面空了几个字节接着是d?

结果如下:sizeof(S2)结果为24。s2的c后面空了3个字节接着是d。
                         a       b
S1的内存布局:11**,1111,
                         c     S1.a   S1.b        d
S2的内存布局:1***,11**,1111,****11111111

3,指针数据类型转换
        一个int,对于32位的cpu来说,通常是需要4字节对齐的。
        若对字符数组进行强制转换时,没有4字节对齐,则报错信息如下:
        Alignment trap: app.out (1980) PC=0x0002f154 Instr=0xe5932000 Address=0x004a8746 FSR 0x001
例如:
char aa[100];
int bb_used=*(int *)(aa+read_num);

((char *)(aa+read_num)
若read_num不是4的整数倍,则会导致
((char *)(aa+read_num)
不是4字节对齐的,将它强制转换为int,则会遇到对齐错误,取的值也不对,但是可以继续运行。
此种情况下,使用memcpy是安全的:
memcpy(&bb_used, aa+read_num, sizeof(bb_used));

4,一个字节对齐问题的例子

        我定义了一个结构体,包含4个char,4个int。另外开辟了一个字符数组,准备存储100个结构体的内容,开辟的大小是(4+4×4)×100=2000。结果却发现程序运行异常。后来仔细分析,发现是结构体的大小判断错了。结构体如下:

struct st1{
 char a1;
 int b1;
 char a2;
 int b2;
 char a3;
 int b3;
 char a4;
 int b4;
};
在这个结构体中,由于每个char后面紧跟一个int,而int要求4字节对齐,则char后面的3个字节是空着的。这个结构体的实际使用空间应该如下计算:
(1+3)+4+(1+3)+4+(1+3)+4+(1+3)+4=32
存放100个结构体的内容,需要内存大小为32×100=3200。对于2000字节的数组,有严重的越界!由于越界,导致写坏了后面内存中的数据,从而运行异常。通过调整数组大小,解决问题。

0 0
原创粉丝点击