указатели — C ++ структура памяти в Clang?

Есть ли накладные расходы на структуру памяти в 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 байта памяти?

4

Решение

Тест, который вы запустили, не имеет ничего общего со структурой и не имеет ничего общего со стеком. Неопределенное поведение начинать чтение из произвольных областей памяти стека, компилятор имеет полное право делать с ними все, что захочет. Без оптимизаций очевидно, что компилятор не потрудится эффективно их использовать. Никому нет дела до нескольких байтов, потерянных без оптимизации.

Кроме того, структура и расположение классов определяются стандартом, который является общим как для GCC, так и для Clang, поэтому я заверяю вас, что они идентичны (по модулю ошибок) в Linux.

По сути, вы предполагаете, что компилятор размещает ваши локальные переменные в точном порядке в стеке, и ничего другого там нет, но нет абсолютно никаких оснований полагать, что это правда. Компилятор не обязан каким-либо образом размещать стек.

2

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

Clang просто выравнивает структуру на 8-байтовой границе, чтобы ее можно было более эффективно копировать. GCC тоже это делает, но компоновка стека GCC отличается, поэтому структура уже выровнена.

1

Вот что разрешено и не разрешено компилятору: не разрешено дополнять до первого члена. Компилятор не разрешено переупорядочивать элементы (хотя 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) компилятор может свободно переупорядочивать и дополнять, как он хочет.

Размер типов элементов, а также возможность использования упаковки не зависит от архитектуры, версии компилятора и флагов компилятора, следовательно, разные результаты люди получают при запуске вашего кода.

1

Конечно, есть выравнивание. Это 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-структурах

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