sizeof () в структуре [дубликат]

Вот простое решение, которое использует forEach (возвращается к IE9):

var funcs = {};
[0,1,2].forEach(function(i) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: " + i); // each should log its value.
    };
})
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

Печатает:

My value: 0
My value: 1
My value: 2
553
задан codeDom 16 May 2016 в 17:37
поделиться

11 ответов

Это связано с добавлением дополнений, чтобы удовлетворить ограничениям выравнивания. Уравнение структуры данных влияет как на производительность, так и на правильность программ:

  • Недопустимый доступ может быть жесткой ошибкой (часто SIGBUS).
  • Неравномерный доступ может быть мягкой ошибкой. Либо исправлено в аппаратном обеспечении, либо для снижения производительности. Или исправлено путем эмуляции в программном обеспечении, для серьезной деградации производительности. Кроме того, атомарность и другие гарантии параллелизма могут быть нарушены, что приводит к незначительным ошибкам.

Вот пример использования типичных настроек для процессора x86 (все используемые 32 и 64-битные режимы):

struct X
{
    short s; /* 2 bytes */
             /* 2 padding bytes */
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 3 padding bytes */
};

struct Y
{
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
    short s; /* 2 bytes */
};

struct Z
{
    int   i; /* 4 bytes */
    short s; /* 2 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
};

const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */

Можно минимизировать размер структур путем сортировки (например, структура Z в приведенном выше примере).

ВАЖНОЕ ЗАМЕЧАНИЕ: В стандартах C и C ++ указано, что выравнивание структуры определяется реализацией , Поэтому каждый компилятор может выбрать выравнивание данных по-разному, что приводит к разным и несовместимым макетам данных. По этой причине при работе с библиотеками, которые будут использоваться разными компиляторами, важно понять, как компиляторы выравнивают данные. Некоторые компиляторы имеют параметры командной строки и / или специальные #pragma операторы для изменения настроек выравнивания структуры.

544
ответ дан 6 revs, 6 users 79% 28 August 2018 в 19:51
поделиться

Если вы хотите, чтобы структура имела определенный размер с GCC, например, используйте __attribute__((packed)) .

В Windows вы можете установить выравнивание в один байт при использовании cl .exe компилятор с опцией / Zp .

Обычно ЦП получает доступ к данным, который является кратным 4 (или 8), зависящей от платформы, а также от компилятор.

Итак, это вопрос выравнивания в основном.

У вас должны быть веские причины для его изменения.

23
ответ дан 3 revs, 2 users 96% 28 August 2018 в 19:51
поделиться

Язык C оставляет компилятору некоторую свободу в отношении расположения структурных элементов в памяти:

  • могут появляться отверстия памяти между любыми двумя компонентами и после последнего компонента. Это было связано с тем, что определенные типы объектов на целевом компьютере могут быть ограничены границами адресации
  • «отверстий в памяти», включенными в результат оператора sizeof. Размер sizeof не включает в себя размер гибкого массива, который доступен в C / C ++
  • . Некоторые реализации языка позволяют вам управлять компоновкой памяти структур через параметры прагмы и компилятора

Язык C предоставляет некоторую уверенность программисту в компоновке элементов в структуре:

  • компиляторы должны назначать последовательность компонентов, увеличивая адреса памяти
  • Адрес первого компонента совпадает с начальным адресом структуры
  • неназванные битовые поля могут быть включены в структуру в требуемые выравнивания адресов соседних элементов

Проблемы, связанные с выравниванием элементов:

  • Различные компьютеры строят ребра объектов по-разному
  • Различные ограничения ширины битового поля
  • Компьютеры отличаются тем, как хранить байты в слове (Intel 80x86 и Motorola 68000)

Как работает выравнивание:

  • T объем, занимаемый структурой, вычисляется как размер выровненного одиночного элемента массива таких структур. Структура должна заканчиваться так, чтобы первый элемент следующей ниже структуры не нарушал требования выравнивания

ps. Более подробную информацию можно найти здесь: «Сэмюэл П. Харбисон, Гай Л. Стейле CA Reference, (5.6.2 - 5.6.7) "

3
ответ дан bruziuz 28 August 2018 в 19:51
поделиться

C99 N1256 стандартная черновик

http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

6.5.3.4 Оператор sizeof :

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

6.7.2.1 Спецификаторы структуры и объединения :

13 ... Там может быть неназванным дополнением внутри объекта структуры, но не в начале.

и:

15 В конце структуры может быть неназванное дополнение или объединение.

Новая функция члена C99 с гибким элементом массива (struct S {int is[];};) также может влиять на заполнение:

16 As в специальном случае последний элемент структуры с более чем одним именованным элементом может иметь неполный тип массива; это называется гибким элементом массива. В большинстве ситуаций гибкий элемент массива игнорируется. В частности, размер структуры выглядит так, как если бы гибкий элемент массива был пропущен, за исключением того, что он мог иметь более длинное дополнение, чем подразумевалось.

Приложение J Проблемы с переносимостью повторяет:

Следующие неуказаны: ...

  • Значение байтов заполнения при хранении значений в структурах или объединениях (6.2.6.1)

C ++ 11 Стандартная черновая версия N3337

http://www.open-std.org/jtc1/sc22/wg21/ docs / papers / 2012 / n3337.pdf

5.3.3 Размер :

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

9.2 Члены класса :

Указатель на объект структуры стандартного макета, соответствующим образом преобразованный с использованием reinterpret_cast, указывает на его начальный член (или если этот элемент является битовым полем, то t o единица, в которой он находится) и наоборот. [Примечание. Таким образом, в рамках объекта структуры стандартного макета может быть указано неназванное заполнение, но не в его начале, по мере необходимости, для достижения соответствующего выравнивания. - end note]

Я знаю достаточно C ++, чтобы понять примечание: -)

5
ответ дан Ciro Santilli 新疆改造中心 六四事件 法轮功 28 August 2018 в 19:51
поделиться

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

struct pixel {
    unsigned char red;   // 0
    unsigned char green; // 1
    unsigned int alpha;  // 4 (gotta skip to an aligned offset)
    unsigned char blue;  // 8 (then skip 9 10 11)
};

// next offset: 12

Архитектура x86 всегда могла извлекать неверные адреса. Тем не менее, он медленнее, и когда несоосность перекрывает две разные строки кэша, тогда он вытесняет две строки кэша, когда выровненный доступ будет вытеснять только один.

Некоторым архитектурам на самом деле приходится ловить ложные недочетые чтения и записи и ранние версии архитектуры ARM (той, которая развивалась во всех современных мобильных процессорах) ... ну, на самом деле они просто вернули для них плохие данные. (Они проигнорировали младшие разряды.)

Наконец, обратите внимание, что строки кэша могут быть сколь угодно большими, а компилятор не пытается угадать их или сделать компромисс между пространством и скоростью. Вместо этого решения выравнивания являются частью ABI и представляют собой минимальное выравнивание, которое в конечном итоге равномерно заполняет линию кэша.

Значения TL; DR: важны.

2
ответ дан DigitalRoss 28 August 2018 в 19:51
поделиться

Расположение упаковки и байт, как описано в C FAQ здесь :

Это для выравнивания. Многие процессоры не могут получить доступ к 2- и 4-байтным количествам (например, ints и long ints), если они переполнены каждым способом.

Предположим, что у вас есть эта структура:

struct {
    char a[3];
    short int b;
    long int c;
    char d[3];
};

Теперь вы можете подумать, что это возможно, чтобы упаковать эту структуру в память следующим образом:

+-------+-------+-------+-------+
|           a           |   b   |
+-------+-------+-------+-------+
|   b   |           c           |
+-------+-------+-------+-------+
|   c   |           d           |
+-------+-------+-------+-------+

Но на процессоре намного проще, если компилятор упорядочивает его как это:

+-------+-------+-------+
|           a           |
+-------+-------+-------+
|       b       |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           |
+-------+-------+-------+

В упакованной версии обратите внимание на то, что вам и мне, как минимум, немного сложно понять, как обтекают поля b и c? В двух словах, это тоже сложно для процессора. Поэтому большинство компиляторов будут заполнять структуру (как будто с дополнительными невидимыми полями) следующим образом:

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+
146
ответ дан Jamal 28 August 2018 в 19:51
поделиться

В дополнение к другим ответам структура может (но обычно не имеет) иметь виртуальные функции, и в этом случае размер структуры также будет содержать пространство для vtbl.

4
ответ дан JohnMcG 28 August 2018 в 19:51
поделиться

Это может быть связано с выравниванием и заполнением байт, чтобы структура выходила на четное количество байтов (или слов) на вашей платформе. Например, в C на Linux следующие 3 структуры:

#include "stdio.h"


struct oneInt {
  int x;
};

struct twoInts {
  int x;
  int y;
};

struct someBits {
  int x:2;
  int y:6;
};


int main (int argc, char** argv) {
  printf("oneInt=%zu\n",sizeof(struct oneInt));
  printf("twoInts=%zu\n",sizeof(struct twoInts));
  printf("someBits=%zu\n",sizeof(struct someBits));
  return 0;
}

У членов, размер которых (в байтах) равен 4 байтам (32 бита), 8 байтам (2x 32 бита) и 1 байт (2 +6 бит) соответственно. Вышеупомянутая программа (в Linux с использованием gcc) печатает размеры 4, 8 и 4, где последняя структура дополняется так, что это одно слово (4 x 8 бит байтов на моей 32-битной платформе).

oneInt=4
twoInts=8
someBits=4
11
ответ дан Kyle Burton 28 August 2018 в 19:51
поделиться

См. также:

для Microsoft Visual C:

http://msdn.microsoft.com/en-us/library/2e70t5y1%28v=vs. 80% 29.aspx

и GCC утверждают совместимость с компилятором Microsoft .:

http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking -Pragmas.html

В дополнение к предыдущим ответам, обратите внимание, что независимо от упаковки в C ++ нет гарантий-распоряжений-членов. Компиляторы могут (и, конечно же, делать) добавлять в структуру элементов виртуальной таблицы и базовых структур. Даже существование виртуальной таблицы не обеспечивается стандартом (реализация виртуального механизма не указана), и поэтому можно сделать вывод, что такая гарантия просто невозможна.

Я вполне уверен, что порядок членов является гарантировано в C, но я не стал бы рассчитывать на это, когда писал кросс-платформенную или кросс-компиляторную программу.

9
ответ дан lkanab 28 August 2018 в 19:51
поделиться

Он может это сделать, если вы неявно или явно устанавливаете выравнивание структуры. Строка, которая выровнена 4, всегда будет кратной 4 байтам, даже если размер ее членов будет чем-то, что не кратно 4 байтам.

Также библиотека может быть скомпилирована под x86 с 32- бит, и вы можете сравнить его компоненты с 64-битным процессом, это даст вам другой результат, если вы делаете это вручную.

5
ответ дан Orion Adrian 28 August 2018 в 19:51
поделиться

Размер структуры больше, чем сумма ее частей из-за того, что называется упаковкой. У конкретного процессора есть предпочтительный размер данных, с которым он работает. Большинство современных процессоров предпочитают размер, если 32 бит (4 байта). Доступ к памяти, когда данные находятся на этом типе границы, более эффективен, чем те, которые охватывают эту границу размера.

Например. Рассмотрим простую структуру:

struct myStruct
{
   int a;
   char b;
   int c;
} data;

Если машина является 32-разрядной машиной, а данные выровнены по 32-битной границе, мы видим немедленную проблему (при отсутствии выравнивания структуры). В этом примере предположим, что данные структуры начинаются с адреса 1024 (0x400 - обратите внимание, что младшие 2 бита равны нулю, поэтому данные выравниваются с 32-разрядной границей). Доступ к data.a будет работать нормально, потому что он начинается на границе - 0x400. Доступ к data.b также будет работать нормально, поскольку он находится по адресу 0x404 - еще одна 32-разрядная граница. Но неуравновешенная структура поставит data.c по адресу 0x405. 4 байта данных.c находятся в 0x405, 0x406, 0x407, 0x408. На 32-битной машине система считывала data.c в течение одного цикла памяти, но получала бы только 3 из 4 байтов (четвертый байт находится на следующей границе). Таким образом, системе потребуется второй доступ к памяти для получения 4-го байта,

Теперь, если вместо того, чтобы поместить data.c по адресу 0x405, компилятор заполнил структуру на 3 байта и поместил данные. c по адресу 0x408, тогда системе потребуется всего 1 цикл, чтобы прочитать данные, сократив время доступа к этому элементу данных на 50%. Заполняет эффективность памяти для эффективности обработки. Учитывая, что компьютеры могут иметь огромные объемы памяти (много гигабайт), компиляторы считают, что своп (скорость над размером) является разумным.

К сожалению, эта проблема становится убийцей при попытке отправить структуры по сети или даже записать двоичные данные в двоичный файл. Прокладка, вставленная между элементами структуры или класса, может нарушить данные, отправленные в файл или сеть. Чтобы написать переносимый код (тот, который будет использоваться для нескольких разных компиляторов), вам, вероятно, придется обращаться к каждому элементу структуры отдельно, чтобы обеспечить надлежащую «упаковку».

С другой стороны, разные компиляторы имеют разные возможности для управления упаковкой структуры данных. Например, в Visual C / C ++ компилятор поддерживает команду #pragma pack. Это позволит вам настроить упаковку и выравнивание данных.

Например:

#pragma pack 1
struct MyStruct
{
    int a;
    char b;
    int c;
    short d;
} myData;

I = sizeof(myData);

Теперь я должен иметь длину 11. Без прагмы я мог бы быть чем угодно из 11 до 14 (а для некоторых систем - до 32), в зависимости от стандартной упаковки компилятора.

6
ответ дан sid1138 28 August 2018 в 19:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: