sizeof (), выравнивание в структурах C: - PullRequest
0 голосов
/ 12 июня 2018

Предисловие: Провел ли мое исследование по выравниванию структуры.Посмотрел этот вопрос, этот один, а также этот один - но все еще не нашел мой ответ.

Мой актуальный вопрос:

Вот фрагмент кода, который я создал, чтобы прояснить мой вопрос:

#include "stdafx.h"
#include <stdio.h>

struct IntAndCharStruct
{
    int a;
    char b;
};

struct IntAndDoubleStruct
{
    int a;
    double d;
};

struct IntFloatAndDoubleStruct
{
    int a;
    float c;
    double d;
};

int main()
{
    printf("Int: %d\n", sizeof(int));
    printf("Float: %d\n", sizeof(float));
    printf("Char: %d\n", sizeof(char));
    printf("Double: %d\n", sizeof(double));
    printf("IntAndCharStruct: %d\n", sizeof(IntAndCharStruct));
    printf("IntAndDoubleStruct: %d\n", sizeof(IntAndDoubleStruct));
    printf("IntFloatAndDoubleStruct: %d\n", sizeof(IntFloatAndDoubleStruct));
    getchar();
}

И вывод:

Int: 4
Float: 4
Char: 1
Double: 8
IntAndCharStruct: 8
IntAndDoubleStruct: 16
IntFloatAndDoubleStruct: 16

Я получаювыравнивание видно в IntAndCharStruct и в IntAndDoubleStruct.

Но я просто не понимаю IntFloatAndDoubleStruct один .

Проще говоря: Почему не sizeof(IntFloatAndDoubleStruct) = 24?

Заранее спасибо!

ps: я использую Visual-Studio 2017, стандартное консольное приложение.

Редактировать: По комментариям, проверено IntDoubleAndFloatStruct (другой порядок элементов)и получил 24 в sizeof() - и я буду рад, если ответы примут к сведению и объяснят этот случай тоже.

Ответы [ 4 ]

0 голосов
/ 12 июня 2018

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

Единственное ограничение, которое C накладывает на элементы структуры, состоит в том, что адрес, выделенный для первого члена, должен быть кратнымего выравнивание (так, чтобы вектор этой структуры был доступен как указатель + sizeof () * индекс).В вашем случае IntFloatAndDoubleStruct должен быть выровнен с кратным sizeof (int), так как a имеет тип int.

Внутренним членам в некоторых случаях назначаются адреса, кратные их выравниванию по порядку.работать быстрее (поэтому вставляется некоторое заполнение), но это не накладывается на C. Поэтому компилятор может решить вставить #pragma, чтобы изменить алгоритм, используемый для выделения структур.См. здесь .

Как предполагает CIsForCookies, структура будет выровнена с кратным максимального выравнивания, чтобы вектор таких структур все еще имел выравнивание каждого элемента.Но это не навязывается языком C, а подразумевает производительность реализации, потому что в addr_struct[i]+member_offset оба термина делятся на выравнивание.

0 голосов
/ 12 июня 2018

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

В этом случае int будет смещением = 0 (относительно адреса структурыinstance), float по смещению = 4 и double по смещению = 8, потому что размеры int и float составляют в сумме до 8.

В конце нет отступов - размерструктура уже 16, что кратно размеру double.

0 голосов
/ 12 июня 2018

Ваша структура должна иметь длину 8*N байт, поскольку она имеет член с 8 байтами (double).Это означает, что структура находится в памяти по адресу (A), делимому на 8 (A%8 == 0), и ее конечный адрес будет (A + 8N), который также будет делиться на 8.

Оттуда, вы храните 2 4-байтовые переменные (int + float), что означает, что вы теперь занимает область памяти [A,A+8).Теперь вы храните 8-байтовую переменную (double).Нет необходимости в заполнении с (A+8) % 8 == 0A%8 == 0].Таким образом, без заполнения вы получите 4+4+8 == 16.

Если вы измените порядок на int -> double -> float, вы будете занимать 24 байта, поскольку исходный адрес переменной double не будет делиться на 8 и будетнеобходимо заполнить 4 байта, чтобы получить действительный адрес (а также структура будет иметь заполнение в конце).


|--------||--------||--------||--------||--------||--------||--------||--------|
|   each ||   cell ||  here  ||represen||-ts  4  || bytes  ||        ||        |
|--------||--------||--------||--------||--------||--------||--------||--------|

A        A+4       A+8      A+12      A+16      A+20      A+24                      [addresses]
|--------||--------||--------||--------||--------||--------||--------||--------|    
|   int  ||  float || double || double ||        ||        ||        ||        |    [content - basic case]
|--------||--------||--------||--------||--------||--------||--------||--------|

first padding to ensure the double sits on address that is divisble by 8
last  padding to ensure the struct size is divisble by the largest member's size (8)
|--------||--------||--------||--------||--------||--------||--------||--------|    
|   int  || padding|| double || double || float  || padding||        ||        |    [content - change order case]
|--------||--------||--------||--------||--------||--------||--------||--------|
0 голосов
/ 12 июня 2018

На вашей платформе выполняются следующие условия: Размеры int и float равны 4. Требования к размеру и выравниванию double равны 8.

Мы знаем это из sizeof вывод, который вы показали.sizeof (T) дает число байтов между адресами двух последовательных элементов типа T в массиве.Итак, мы знаем, что требования к выравниванию такие же, как я сказал выше. (Примечание)

Теперь компилятор сообщил 16 для IntFloatAndDoubleStruct.Это работает?

Предположим, что у нас есть такой объект по адресу, выровненному по 16.

  • int a, следовательно, по адресу X выровнен по 16, поэтому он выровнен по 4просто хорошо.Он будет занимать байты [X, X + 4)
  • Это означает, что float c может начинаться с X + 4, который выровнен по 4, что хорошо для float.Он будет занимать байты [X + 4, X + 8)
  • Наконец, double d может начинаться с X + 8, который выровнен по 8, что хорошо для double.Он будет занимать байты [X + 8, X + 16)
  • Это освобождает X + 16 для следующего объекта структуры, снова выровненного по 16.

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


(Примечание) Это не совсем верно: для каждого из них мы знаем, чтои размер и выравнивание <= N, что N кратно требованию выравнивания, и что нет N1 <N, для которого это также будет выполнено.Однако это очень тонкая деталь, и для ясности в ответе просто предполагается, что фактические требования к размеру и выравниванию для примитивных типов являются неопределенными, что в любом случае является наиболее вероятным случаем на платформе OP. </p>

...