使用虚拟继承的地址未对齐

Lar*_*ars 6 c++ memory-alignment virtual-inheritance sanitizer clang++

以下显然有效的代码使用UndefinedBehaviorSanitizer sanitiser产生错位的地址运行时错误.

#include <memory>
#include <functional>

struct A{
  std::function<void()> data; // seems to occur only if data is a std::function
} ;

struct B{
  char data; // occurs only if B contains a member variable
};

struct C:public virtual A,public B{

};

struct D:public virtual C{

};

void test(){
  std::make_shared<D>();
}

int main(){
  test();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

在macbook上编译和执行 clang++ -fsanitize=undefined --std=c++11 ./test.cpp && ./a.out 产生输出 runtime error: constructor call on misaligned address 0x7fe584500028 for type 'C', which requires 16 byte alignment [...].

我想了解错误发生的方式和原因.

AMA*_*AMA 5

由于对齐方式std::function<void()>为16,尺寸为48,因此简化了。此代码具有相同的行为,但更易于理解:

struct alignas(16) A
{ char data[48]; };

struct B
{ char data; };

struct C : public virtual A, public B
{};

struct D : public virtual C
{};

int main()
{
    D();
}
Run Code Online (Sandbox Code Playgroud)

我们有以下对齐方式和大小:

                     |__A__|__B__|__C__|__D__|
 alignment (bytes):  |  16 |  1  |  16 |  16 |
      size (bytes):  |  48 |  1  |  64 |  80 |
Run Code Online (Sandbox Code Playgroud)

现在让我们看看它在内存中的样子。关于这个的更多解释可以在这个很好的答案中找到。

  • A: char[48] + no padding == 48B
  • B: char[1] + no padding == 1B
  • C: A* + B + A + 7 bytes of padding (align to 16) == 64B
  • D: C* + C + 8 bytes of padding (align to 16) == 80B

现在可以很容易地看到,C内部的偏移量D是8个字节,但C与16对齐。

00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00
             ^ 
Run Code Online (Sandbox Code Playgroud)

这里每个零是1个字节。

更新:在哪里以及如何放置填充取决于C ++编译器。Standard未指定。看起来像具有的填充大小一样,clang无法将中的所有内容对齐D。减轻不对齐的一种方法是仔细设计类,以使它们具有相同的对齐方式(例如8个字节)。

  • 很好的解释和参考!那么,为什么编译器没有注意到错配并在D内正确对齐C?有没有办法防止这种错误,例如,如果不知道诸如std :: function之类的成员的对齐方式? (2认同)