10

以下显然有效的代码使用 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 [...]

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

4

1 回答 1

9

由于对齐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();
}

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

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

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

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

现在很容易看到Cinside的偏移量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
             ^ 

这里每个零是 1 个字节。

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

于 2017-09-28T17:54:11.137 回答