Подтвердить что ты не робот

Могут ли массивы C содержать элементы между элементами?

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

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

Итак, в коде, слух:

{
    // Given this:
    struct { int values[20]; } foo;
    int values[20];

    // This may be true:
    sizeof(values) != sizeof(foo.values);
}

Я вполне уверен, что sizeof(values) всегда будет равен sizeof(foo.values). Тем не менее, я не смог найти что-либо в стандарте C (в частности, C99), который явно подтверждает или отрицает это.

Кто-нибудь знает, адресован ли этот слух в любом стандарте C?

edit. Я понимаю, что между окончанием массива foo.values и концом struct foo может быть отступы и что в стандарте указано, что между начало foo и начало foo.values. Однако есть ли у кого-нибудь цитата или ссылка на стандарт, где говорится, что между элементами foo.values?

4b9b3361

Ответ 1

Нет, между элементами массива никогда не будет прокладки. Это специально не допускается. Стандарт C99 вызывает типы массивов "Тип массива описывает смежно распределенный непустой набор объектов...". Для контраста структура "последовательно", а не "смежно" выделена.

Может существовать дополнение до или после массива внутри структуры; это другое животное целиком. Компилятор может сделать это, чтобы помочь выравниванию структуры, но стандарт C ничего не говорит об этом.

Ответ 2

Осторожно. Заполнение может быть добавлено в конце структуры, но не будет добавлено между элементами массива, когда вы укажете в своем вопросе. Массивы всегда будут ссылаться на непрерывную память, хотя массив структур может иметь дополнение к каждому элементу как часть самой структуры.

В вашем примере массивы values и foo.values будут иметь одинаковый размер. Любое дополнение будет частью структуры foo.

Ответ 3

Здесь объяснение того, почему структуре может потребоваться прокладка между ее членами или даже после ее последнего члена, и почему массив не работает:

Различные типы могут иметь разные требования к выравниванию. Некоторые типы должны быть выровнены на границах слов, другие - на двойных или четных границах слова. Для этого структура может содержать пробельные байты между ее членами. Может потребоваться чередование байтов заполнения, поскольку местоположение памяти непосредственно из структуры должно также соответствовать требованиям выравнивания структуры, т.е. Если bar имеет тип struct foo *, то

(struct foo *)((char *)bar + sizeof(struct foo))

возвращает действительный указатель на struct foo (т.е. не прерывается из-за неправильного выравнивания).

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

Ответ 4

Да, вроде. В зависимости от переменной переменные часто выравниваются с некоторой границей. Возьмите, например:

typedef struct
{
    double d;
    char c;
} a_type_t;

double и char равны 8 и 1 байтам в моей системе соответственно. Всего 9. Эта структура, однако, будет 16 байтов, так что удвоения всегда будут выровнены по 8 байт. Если бы я только что использовал ints, chars и т.д., То выравнивание могло бы быть 1, 2, 4 или 8.

Для некоторого типа T, sizeof(T) может быть или не быть равно sizeof(T.a) + sizeof(T.b) + sizeof(T.c) ... и т.д.

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

Ответ 5

Рассмотрим:

struct {
  short s;
  int i;
} s;

Предполагая, что шорты составляют 16 бит, а вы на 32 бита, размер, вероятно, будет 8 байтов, так как каждый член структуры, как правило, выравнивается по слову (32 бит в этом случае). Я говорю "возможно", потому что это специфическое поведение реализации, которое может быть изменено флагами компилятора и т.п.

Стоит подчеркнуть, что это поведение реализации, которое не обязательно определяется стандартом C. Подобно размеру шорт, ints и longs (стандарт C просто говорит, что шорты не будут больше, чем int, а longs не будет меньше, чем ints, что может закончиться как 16/32/32, 16/32/64, 32/32/64 или ряд других конфигураций).