Выполняет ли C инициализацию заполнения структуры до нуля?

Если компилятор C накладывает структуру, чтобы выровнять поля с их собственным выравниванием, и эта структура затем инициализируется, это заполнение, инициализированное нулем?

Например, следующая структура:

typedef struct foo_t_ {
    int  a;
    char b;
    int  c;
    char d;
} foo_t;

Во многих системах эта (плохо спроектированная) структура будет иметь sizeof(foo_t) из 16 с общим количеством байтов в 6 байтов, по 3 байта после каждого из символов.

Если мы инициализируем структуру, например:

foo_t foo = { .a = 1, .b = '2' };

тогда поля foo.a будут установлены в 1, а foo.b будет установлено на символ "2". Неопределенные поля (`foo.c 'и' foo.d ') будут автоматически установлены на 0. Вопрос в том, что происходит с 6 байтами заполнения? Будет ли это также автоматически установлено на 0? или это поведение undefined?

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

foo_t foo = { .a = 1, .b = '2' };
foo_t bar = { .a = 1, .b = '2' };
uint32_t hash_foo = calc_hash(&foo, sizeof(foo));
uint32_t hash_bar = calc_hash(&bar, sizeof(bar));

и я хочу быть уверенным, что hash_foo и hash_bar совпадают. Я мог бы гарантировать это, сначала используя memset(), чтобы очистить структуры, а затем инициализировать их, но вместо этого кажется более чистым использовать инициализацию C.

На практике GCC в моей системе также очищает отступ, но я не знаю, гарантировано ли это.

Ответы

Ответ 1

В общем случае, согласно C11, для любой главы неинициализированного объекта §6.2.6.1/6,

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

Но, если частичная инициализация выполнена, в этом случае для остальной части членов, инициализация происходит так, как если бы объект имел статическую или длительность хранения потоков, а затем, цитируя тот же стандарт, главу §6.7.9/21

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

и относительно неявной инициализации объектов со статической продолжительностью хранения, параграф 10

Если объект, который имеет статическую или длительность хранения потоков, не инициализируется явно, тогда:

  • если он является агрегатом, каждый член инициализируется (рекурсивно) в соответствии с этими правилами, и любое заполнение инициализируется нулевыми битами;

Итак, в вашем случае отступы для остальных объектов гарантированно равны 0, но не для членов, которые получили инициализаторы.

Итак, во всем, вы не должны зависеть от неявной инициализации 0, используйте memset().

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