Как sizeof рассчитывает размер структур - PullRequest
13 голосов
/ 17 апреля 2010

Я знаю, что char и int вычисляются как 8 байтов на 32-битных архитектурах из-за выравнивания, но недавно я столкнулся с ситуацией, когда структура с 3 шортами сообщалась оператором sizeof как 6 байтов. Код выглядит следующим образом:

#include <iostream>
using namespace std ;

struct IntAndChar
{
    int a ;
    unsigned char b ;
};


struct ThreeShorts
{
    unsigned short a ;
    unsigned short b ;
    unsigned short c ;
};


int main()
{
    cout<<sizeof(IntAndChar)<<endl; // outputs '8'
    cout<<sizeof(ThreeShorts)<<endl; // outputs '6', I expected this to be '8'
    return 0 ;
}

Компилятор: g ++ (Debian 4.3.2-1.1) 4.3.2. Это действительно озадачивает меня, почему не выполняется выравнивание для структуры, содержащей 3 шорта?

Ответы [ 6 ]

22 голосов
/ 17 апреля 2010

Это потому, что int имеет размер 4 байта и должен быть выровнен по границе 4 байта. Это означает, что ЛЮБОЙ struct, содержащий int, также должен быть выровнен как минимум до 4 байтов.

С другой стороны, short имеет размер 2 байта и требует выравнивания только по 2-байтовой границе. Если struct, содержащий short s, не содержит ничего, что требует большего выравнивания, struct также будет выровнен до 2 байтов.

15 голосов
/ 17 апреля 2010

Это действительно озадачивает меня, почему не выполняется выравнивание для t

Какое выравнивание вы хотите иметь?

Шорты могут быть выровнены по 2-байтовым границам без вредных последствий (при условии, что здесь есть общие компиляторы x86). Поэтому, если вы создаете массив struct ThreeeShorts, эта структура, имеющая размер 6, подойдет, так как любые элементы в таком массиве будут начинаться с 2-байтовой границы.

Ваш struct IntAndChar содержит целое число, для int требуется 4-байтовое выравнивание, поэтому, если вы создаете массив struct IntAndChar, размер должен быть равен 8, чтобы следующий элемент был выровнен на границе 4 байта.

Если бы мы не рассматривали массивы, это не имело бы большого значения, если бы struct IntAndChar имел длину 5 байт, компилятор просто выделил бы его, начиная с 4-байтовой границы, когда вы создаете один стек или используете его как составной член в другой структуре.

Вы всегда можете получить количество элементов в массиве, выполнив sizeof (arrayofT) / sizeof (T), и элементы массива гарантированно будут храниться рядом, так что n-й элемент может быть получен путем перехода N * sizeof (arrayelementtype) байтов с начала, и это основная причина, по которой вы увидите, что структуры дополняются в конце.

6 голосов
/ 17 апреля 2010

Я не знаю, откуда вы взяли, что char или int рассчитывается как "8 байтов". Нет, каждый тип рассчитывается в соответствии с его размером: char как 1, int как 4 на 32-битной платформе (не 8, а 4). Требование выравнивания для каждого типа обычно совпадает с его размером (хотя это не обязательно).

По этой причине, когда структура содержит элементы того же типа , общий размер этой структуры обычно будет точной суммой размеров ее элементов: структура 3 char s будет иметь размер 3, а структура из двух int s будет иметь размер 8.

Видимо, на вашей платформе тип short имеет размер 2, поэтому, как ожидается, структура из 3 шорт имеет размер 6, что вы и наблюдаете.

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

Например, структура, которая выглядит следующим образом

struct S {
  char c;
  int i;
};

, скорее всего, будет занимать 8 байт на вашей платформе из-за необходимости 3 байта заполнения после члена char. Обратите внимание, что char считается как 1, int как 4, и дополнительные 3 байта заполнения между ними делают его 8.

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

struct S1 {
  char c1;
  int i;
  char c2;
};

на вашей платформе, вероятно, будет иметь размер 12, тогда как этот

struct S2 {
  int i;
  char c1;
  char c2;
};

будет занимать всего 8 байтов. Этот последний пример предназначен для иллюстрации того, что окончательный размер структуры не может быть выражен с точки зрения количества байтов, на которые «рассчитывает» каждый элемент. Отношения между членами также важны.

2 голосов
/ 17 апреля 2010

Это полностью зависит от реализации, но, по-видимому, если ваша система может получить доступ к любому из трех short в структуре, не заботясь о выравнивании, она может получить доступ к любому short и, следовательно, любому элементу данных в массиве. ThreeShorts, не беспокоясь о выравнивании. Поэтому нет необходимости более строго выравнивать структуры.

Для примера IntAndChar int предположительно имеет размер 4, а реализация связана с его выравниванием . Чтобы каждый элемент int в массиве IntAndChar был правильно выровнен, структура должна быть дополнена.

Массив sizeof a T[n] точно определен как sizeof(T) * n.

1 голос
/ 09 ноября 2010

Да, у меня была такая же проблема. У меня есть следующая структура

struct Node{
    short digit;
    Node* next;
};
    cout<<":"<<sizeof(Node)<<":"<<sizeof(short)<<":"<<sizeof(Node*)<<endl;

Это дает мне :: 8: 2: 4 ?? почему общая сумма для структуры = 8, а отдельные элементы не суммируются ?? Это из-за выравнивания памяти, память дополняется дополнительными 2 байтами для выравнивания. Спасибо

1 голос
/ 17 апреля 2010

Эта ссылка должна помочь: http://en.wikipedia.org/wiki/Data_structure_alignment

В ThreeShorts все члены выровнены на два байта.

...