Выровненный адрес с использованием виртуального наследования

Следующий, по-видимому, допустимый код вызывает ошибку времени выполнения со смещением адреса с использованием средства защиты 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 [...],

Я хотел бы понять, как и почему происходит ошибка.

6

Решение

С раскладом 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
  • 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
^

Здесь каждый ноль равен 1 байту.

ОБНОВИТЬ:
Где и как разместить отступы, зависит от компилятора C ++. Стандарт не указывает это. Похоже, с размером отступа, Clang не может выровнять все в D, Один из способов уменьшить смещение — тщательно спроектировать классы, чтобы они имели одинаковое выравнивание (например, 8 байт).

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]