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

Misaligned address using virtual inheritance

以下明显有效的代码使用 UndefinedBehaviorSanitizer 消毒程序产生了未对齐的地址运行时错误。

#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;
}

在 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 [...].

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

由于 std::function<void()> 的对齐方式是 16,大小是 48,让我们简化一下。此代码有 same behavior 但更容易理解:

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();
}

我们有以下对齐方式和尺寸:

                     |__A__|__B__|__C__|__D__|
 alignment (bytes):  |  16 |  1  |  16 |  16 |
      size (bytes):  |  48 |  1  |  64 |  80 |

现在让我们看看它在内存中的样子。可以在 this great answer.

中找到更多解释
  • 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

现在很容易看出CD里面的偏移量是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
             ^ 

这里每个零是1个字节。

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