Вопросы по использованию sizeof - PullRequest
5 голосов
/ 29 марта 2010

Вопрос 1

У меня есть структура, как,

struct foo
{
    int a;
    char c;
};

Когда я говорю sizeof(foo), я получаю 8 на моей машине. Насколько я понимаю, 4 байта для int, 1 байт для символа и 3 байта для заполнения. Это верно? Учитывая структуру, подобную приведенной выше, как я узнаю, сколько байтов будет добавлено для заполнения?

Вопрос 2

Мне известно, что sizeof может использоваться для вычисления размера массива. В основном я видел использование как (foos это массив foo)

sizeof(foos)/sizeof(*foos)

Но я обнаружил, что следующее также даст тот же результат.

sizeof(foos) / sizeof(foo)

Есть ли разница в этих двух? Какой из них предпочтительнее?

Вопрос 3

Рассмотрим следующее утверждение.

foo foos[] = {10,20,30};

Когда я делаю sizeof(foos) / sizeof(*foos), это дает 2. Но массив имеет 3 элемента. Если я изменю заявление на

foo foos[] = {{10},{20},{30}};

это дает правильный результат 3. Почему это происходит?

Любые мысли ..

Ответы [ 3 ]

21 голосов
/ 29 марта 2010

Ответ 1

Да - ваш расчет верен. На вашем компьютере sizeof(int) == 4 и int должны быть выровнены в 4 байта.

Вы можете узнать о заполнении, вручную добавив размеры базовых элементов и вычтя их из размера, указанного функцией sizeof (). Вы можете предсказать заполнение, если вы знаете требования к выравниванию на вашей машине. Обратите внимание, что некоторые машины довольно суетливы и выдают ошибки SIGBUS при доступе к смещенным данным; другие более слабые, но замедляют работу при доступе к выровненным данным (и они могут поддерживать «#pragma packed» или что-то подобное). Часто базовый тип имеет размер, равный степени 2 (1, 2, 4, 8, 16), и такой n-байтовый тип должен быть выровнен по n-байту. Кроме того, помните, что структуры должны быть дополнены так, чтобы массив структур оставил все элементы правильно выровненными. Это означает, что структура обычно будет дополнена размером, кратным размеру наиболее строго выровненного элемента в структуре.

Ответ 2

Вообще, вариант на первом лучше; это остается правильным, когда вы меняете базовый тип массива с 'foo' на 'foobar'. Макрос, который я обычно использую:

#define DIM(x) (sizeof(x)/sizeof(*(x)))

У других людей есть другие имена для той же основной операции - и вы можете отнести имя, которое я использую, к загрязнению из смутного и далекого прошлого и некоторому использованию Бейсика.

Как обычно, есть предостережения. В частности, вы не можете применить это осмысленно к аргументам массива функции или к динамически распределенному массиву (используя malloc() et al или new[]); Вы применили к фактическому определению массива. Обычно значение является константой времени компиляции. Под C99 это могло быть оценено во время выполнения, если массив является массивом VLA - переменной длины.

Ответ 3

Из-за способа инициализации, когда у вас недостаточно скобок. Ваша структура 'foo' должна иметь два элемента. 10 и 20 размещены в первом ряду; 30 и неявный 0 поставляются во второй ряд. Следовательно, размер равен двум. Когда вы задаете подкобы, в массиве есть 3 элемента, первые компоненты которых имеют значения 10, 20, 30, а вторые компоненты имеют нули.

3 голосов
/ 29 марта 2010
  1. Заполнение обычно связано с размером регистров на историческом ЦП - в вашем случае у вас есть 32-битный ЦП, поэтому «естественный» размер типа int составляет 4 байта. Процессору медленнее и труднее получить доступ к количествам памяти, меньшим, чем этот размер, поэтому обычно предпочтительно выравнивать значения по 4-байтовым границам. Таким образом, структура получается кратной 4 байтам. Большинство компиляторов позволяют вам изменять количество используемых отступов (например, с помощью "#pragma"), но это следует использовать только в тех случаях, когда использование структуры памяти абсолютно критично.

  2. "* foos" ссылается на первую запись в массиве foos. «foo» ссылается (единственный экземпляр) на тип. Так что они по сути одинаковы. Я бы сам использовал sizeof (type) или sizeof (array [0]), так как * массив легче читать неправильно.

  3. В первом примере вы неправильно инициализируете записи массива. Ваша структура имеет 2 члена, поэтому вы должны использовать {a, b} для инициализации каждого члена массива. Поэтому вам нужна форма {{a, b}, {a, b}, {a, b}} для правильной инициализации записей.

2 голосов
/ 29 марта 2010

Чтобы узнать, сколько у вас есть отступов, просто сложите sizeof () каждого элемента структуры и вычтите эту сумму из sizeof () всей структуры.

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

Хорошей практикой является явное выравнивание элементов структуры путем ручной вставки дополнительных элементов, чтобы каждый элемент гарантированно был "естественно выровнен". Вы можете повторно использовать эти дополнительные элементы для получения полезных данных в будущем. Если вы когда-нибудь напишите библиотеку, для которой потребуется стабильный ABI, это будет обязательный прием.

...