总线错误的实例
- #include <stdio.h>
- #include <stdlib.h>
- #pragma pack(1)
- struct struct_x
- {
- char a;
- float b;
- char c;
- };
- #pragma pack()
- int main(void)
- {
- struct struct_x test = {0};
- printf("sizeof(struct struct_x) = %ld\n", sizeof(test));
- test.a = 1;
- test.b = 2.0;
- test.c = 3;
- char *a = &test.a;
- float *b = &test.b;
- char *c = &test.c;
- printf("*a = %d, addr = %p\n", *a, a);
- printf("*b = %f, addr = %p\n", *b, b);
- printf("*c = %d, addr = %p\n", *c, c);
- return 0;
- }
#pragma pack 可以改变编译器的对齐方式:
- #pragma pack(n) /* 指定按n字节对齐 */
- #pragma pack() /* 取消自定义字节对齐 */
在pc端,可以正常运行:
因为x86/x64系列CPU都支持不对齐访问,也提供了开关禁用这个机制。x86/x64架构不要求对齐访问的时候,必定会有性能代价。
但是,在arm板上测试:
出现了总线错误,因为结构体变量test的成员b的地址是不对齐的地址。CPU访问地址要求是四字节对齐,访问了*(addr+0x001)就会引发异常。
这时候,在struct_x的成员a、b之前增加个占用3个字节的成员d,看看还会不会报错:
- struct struct_x
- {
- char a;
- char d[3];
- float b;
- char c;
- };
可见,成员b可以正常访问,因为这时候b的地址处于四字节对齐地址。
上面的总线错误,毫无疑问,就是对齐问题导致的。
但是,这里有个疑问。假如,我们把成员b的类型改为int类型,这时候会不会产生总线错误?
- #include <stdio.h>
- #include <stdlib.h>
- #pragma pack(1)
- struct struct_x
- {
- char a;
- int b;
- char c;
- };
- #pragma pack()
- int main(void)
- {
- struct struct_x test = {0};
- printf("sizeof(struct struct_x) = %ld\n", sizeof(test));
- test.a = 1;
- test.b = 2;
- test.c = 3;
- char *a = &test.a;
- int *b = &test.b;
- char *c = &test.c;
- printf("sizeof(float) = %d, sizeof(int) = %d\n", sizeof(float), sizeof(int) );
- printf("*a = %d, addr = %p\n", *a, a);
- printf("*b = %d, addr = %p\n", *b, b);
- printf("*c = %d, addr = %p\n", *c, c);
- return 0;
- }
这里的int类型的b成员可以正常访问。这里的成员b的地址与我们上面发生总线错误的b的成员(float类型)的地址完全一样,float类型与int类型也都是占用4字节,但是int类型b成员却可以支持非对齐访问。
这里,暂时就认为CPU就是这么设计的吧。能解释这个问题的朋友欢迎留言讨论,谢谢!
|