Есть ли накладные расходы на структуру памяти в clang? Обычно размер структуры — это всего лишь общий размер всех ее членов. Но это не похоже на случай clang:
#include <iostream>
using namespace std;
struct Obj {
int x;
int y;
};
int main() {
int a = 42;
int b = 43;
Obj obj = {100, 101};
int c = 44;
cout << *(&a - 0) << endl; // 42
cout << *(&a - 1) << endl; // 43
cout << *(&a - 2) << endl; // On clang this prints garbage value instead of 101, how strange...
cout << *(&a - 3) << endl; // 101
cout << *(&a - 4) << endl; // 100
cout << *(&a - 5) << endl; // 44
return 0;
}
Я собрал с clang++ program.cpp
, без оптимизации. Версия:
Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)
Target: x86_64-pc-linux-gnu
Thread model: posix
Расположение памяти на (&a - 2)
кажется, не используется вообще. Я вставил следующий код между объявлением int a
а также int b
:
*(&a - 2) = -1234;
Это будет позже напечатать -1234
вместо значения мусора на *(&a - 2)
, указывая, что clang не записал в это значение после создания Obj obj
, что заставляет меня задуматься, почему Clang зарезервировал его в первую очередь.
На gcc это ведет себя по-другому. Во-первых, кажется, что стек увеличивается в сторону увеличения адресов памяти на gcc на моей машине. Кроме того, GCC взял на себя, чтобы поместить переменную c
сразу после b
так это выглядит так:
cout << *(&a + 0) << endl; // 42
cout << *(&a + 1) << endl; // 43
cout << *(&a + 2) << endl; // 44 gcc moved c here even without optimization
cout << *(&a + 3) << endl; // 100
cout << *(&a + 4) << endl; // 101
Составлено с g++ program.cpp
, Версия:
g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
То же самое происходит, когда я портировал программу на C. Все работает на 64-битной Linux Mint 17.1. Может кто-нибудь объяснить, есть ли более важная цель — потратить впустую 4 байта памяти?
Тест, который вы запустили, не имеет ничего общего со структурой и не имеет ничего общего со стеком. Неопределенное поведение начинать чтение из произвольных областей памяти стека, компилятор имеет полное право делать с ними все, что захочет. Без оптимизаций очевидно, что компилятор не потрудится эффективно их использовать. Никому нет дела до нескольких байтов, потерянных без оптимизации.
Кроме того, структура и расположение классов определяются стандартом, который является общим как для GCC, так и для Clang, поэтому я заверяю вас, что они идентичны (по модулю ошибок) в Linux.
По сути, вы предполагаете, что компилятор размещает ваши локальные переменные в точном порядке в стеке, и ничего другого там нет, но нет абсолютно никаких оснований полагать, что это правда. Компилятор не обязан каким-либо образом размещать стек.
Clang просто выравнивает структуру на 8-байтовой границе, чтобы ее можно было более эффективно копировать. GCC тоже это делает, но компоновка стека GCC отличается, поэтому структура уже выровнена.
Вот что разрешено и не разрешено компилятору: не разрешено дополнять до первого члена. Компилятор не разрешено переупорядочивать элементы (хотя gcc / clang поддерживает некоторый флаг командной строки, который явно разрешает это).
Компилятору разрешено заполнять между любыми элементами, и большинство будет дополнять так, чтобы все элементы были естественно выровнены (&member% sizeof (member) == 0):
struct foo {
char a;
int b; // pad 3 bytes before b
}
Компилятору также разрешено заполнять после последнего члена. Большинство компиляторов дополняют так, чтобы sizeof (T)% sizeof (самый большой член T) == 0, например так:
struct foo {
int a;
char b; // pad 3 bytes after b
}
Для переменных стека (ваши a, b, obj и c) компилятор может свободно переупорядочивать и дополнять, как он хочет.
Размер типов элементов, а также возможность использования упаковки не зависит от архитектуры, версии компилятора и флагов компилятора, следовательно, разные результаты люди получают при запуске вашего кода.
Конечно, есть выравнивание. Это 32 байта в 32-битных программах, я не знаю о 64-битных.
Eсть #pragma
для этого на самом деле:
#pragma pack(push) /* push current alignment to stack */
#pragma pack(1) /* set alignment to 1 byte boundary */
struct MyPackedData
{
char Data1;
long Data2;
char Data3;
};
#pragma pack(pop) /* restore original alignment from stack */
Таким образом, когда у вас есть массив структур, доступ к ним не будет постоянно генерировать исключения из-за выравнивания, которые необходимо обработать. Увидеть Каково реальное влияние успешного доступа без выравнивания на x86?
Увидеть https://en.wikipedia.org/wiki/Data_structure_alignment для получения дополнительной информации.
Я понятия не имею, так ли это для вас, потому что 2 дюйма должны быть выровнены в любом случае, но, возможно, они не на 64-битных архитектурах? Целые 32-битные даже для 64-битных процессоров, тогда как выравнивание, вероятно, 64-битное, или 8 байтов.
Конечно, поскольку структура находится в стеке, другие переменные и их выравнивание также имеют значение.
Также см выравнивание структуры C / C ++, а также Выравнивание памяти в C-структурах