C++ 堆上已分配内存的总线错误
在这样的代码中,我有总线错误:C++ 堆上已分配内存的总线错误,c++,solaris,bus-error,C++,Solaris,Bus Error,在这样的代码中,我有总线错误: char* mem_original; int int_var = 987411; mem_original = new char [250]; memcpy(&mem_original[250-sizeof(int)], &int_var, sizeof(int)); ... const unsigned char* mem_u_const = (unsigned char*)mem_original; ... const unsigned ch
char* mem_original;
int int_var = 987411;
mem_original = new char [250];
memcpy(&mem_original[250-sizeof(int)], &int_var, sizeof(int));
...
const unsigned char* mem_u_const = (unsigned char*)mem_original;
...
const unsigned char *location = mem_u_const + 250 - sizeof(int);
std::cout << "sizeof(int) = " << sizeof(int) << std::endl;//it's printed out as 4
std::cout << "byte 0 = " << int(*location) << std::endl;
std::cout << "byte 1 = " << int(*(location+1)) << std::endl;
std::cout << "byte 2 = " << int(*(location+2)) << std::endl;
std::cout << "byte 3 = " << int(*(location+3)) << std::endl;
int original_var = *((const int*)location);
std::cout << "original_var = " << original_var << std::endl;
然后它失败了:
sizeof(int) = 4
byte 0 = 0
byte 1 = 15
byte 2 = 17
byte 3 = 19
Bus Error
它是在Solaris操作系统(C++5.12)上构建和运行的
同样的代码在Linux(GCC4.12)和Windows(msvc-9.0)上运行良好
我们可以看到:
如果我把(…)
location
放在original\u var
的末尾,它就可以工作了。但是*((const int*)位置的问题是什么?
?编译代码时,我收到以下警告:
main.cpp:19:26: warning: cast from 'const unsigned char *' to 'const int *' increases required alignment from 1 to 4 [-Wcast-align]
int original_var = *((const int*)location);
^~~~~~~~~~~~~~~~~~~~
这似乎是总线错误的原因,因为。尽管我现在无法访问SPARC来测试这一点,但根据我在该平台上的经验,我非常确定这条线路是您的问题:
const unsigned char *location = mem_u_const + 250 - sizeof(int);
mem____const
块最初是由new
为字符数组分配的。由于sizeof(unsigned char)
是1,而sizeof(int)
是4,因此您要添加246个字节。这不是4的倍数
在SPARC上,CPU只能读取与4字节边界对齐的4字节字。您试图读取未对齐的单词是导致总线错误的原因
我建议分配一个
struct
,其中包含一个数组unsigned char
,后跟一个int
,而不是一堆指针数学和强制类型转换,就像导致此错误的那样。对于没有对齐限制(如SPARC)硬件经验的开发人员来说,这是一个常见问题。x86硬件非常宽容未对齐的访问,尽管会影响性能。其他类型的硬件<代码>SIGBUS
这行代码:
int original_var = *((const int*)location);
调用未定义的行为。您正在获取一个无符号字符*
,并将它指向的内容解释为int
。你不能安全地那样做。时期这是一种未定义的行为——正是因为你所经历的原因
你违反了严格的别名规则。简而言之,您不能将一种类型的对象引用为另一种类型。char*
没有也不能引用int
Oracle的Solaris Studio编译器实际上提供了一个命令行参数,可以让您在SPARC硬件上不受影响--xmemalign=1i
(请参阅)。尽管对GCC公平,但如果没有该选项,您在代码中所做的强制操作仍将在Studio编译器下SIGBUS
或者,正如您已经注意到的,您可以使用
memcpy()
来复制周围的字节,不管它们是什么-只要您知道源对象可以安全地复制到目标对象中-是的,在某些情况下这是不正确的。即使我知道一行中有4个字节?你想说,这样的*((const int*)从语言的角度来看,位置
代码是不正确的,即使开发人员知道位置
指向足够的字节数?除了一行有4个字节外,内存访问必须正确对齐int
。执行访问的机器指令需要这样做,正如链接的answ中指出的那样呃,C++也需要。你不知道我如何从<代码> char */COD>正确的方式获得<代码> int */COD>,如果我知道有一个数组<代码> int */COD>?@ @ AkADy,当你强制<代码> char < /C>地址>引用<代码> int >代码>时,你调用未定义的行为。@为什么一行有4个字节还不够好。除了我的答案之外,正确的做法是在调试器中运行程序,查看哪行代码使程序崩溃,以及导致总线错误的指针包含什么。然后,您可以再次运行并单步执行,以查看它设置为该值的位置。@Davidslor,不幸的是并非总是发生这种情况。它可能在总线错误之前进行多次迭代,并且只在Solaris上显示,因此很难调试。另一种调试技术是添加运行时检查。定义一个内联函数,如:template inline bool is_aligned(const T*p){return(uintpttr_T)(void*)(p)%alignof(T)==0;}
。(如果没有C++11,只需跳过模板并使用常量4,而不是alignof(t)
)然后,每当你做指针数学和转换时,你可以断言指针是对齐的。如果不是,你会立即用错误发生的诊断程序崩溃。哎呀,写下type
,我的意思是class
或typename
。你想说,mem\u\u const+240会吗读起来没有问题,因为240是4的倍数?我不认为new
保证在分配char
对象时返回4字节对齐的地址,如果可移植性是一个问题,那么当您重新编译64位代码或在另一个体系结构上时,您不希望代码中断。而不是分配字节块,您应该分配一个struct
。如果内存块需要以几种不同的方式解释,请分配struct
s的union
。更可读、优雅、可移植且更安全。但是,如果您确实不想分配一个struct,您可以分配一个max\u align\t 。其中一个的存储保证正确对齐,以用作指向任何标量类型的指针。或者您可以使用alignas
。如果我有两个“已接受”,谢谢您的帮助和回答第二个是你的。@BenVoigt你确定吗?他们对SIGBUS
免疫?其中只有一个可以正确地作为SPARC硬件上int
的地址。我在SPARC上使用GCC的经验(承认不久前…)是,他们可能会获得SIGBUS
,但这可能是b
int original_var = *((const int*)location);