Каковы правила того, как «близкие» друг к другу переменные хранятся в C, если таковые имеются? - PullRequest
1 голос
/ 15 апреля 2019

Я знаю, что для структуры

struct sequences{
    int a[3];
    int b[3];
} sequence = {{1,2,3},{4,5,6}};

a и b хранятся рядом друг с другом, т.е. если я делаю

int i;
for(i=0; i<6; ++i){
    printf("%d", sequence.a[i]);
}

Я получу вывод 123456.

Я пытался сохранить эти два массива вне основной функции, но не в структуре

int a[3] = {1,2,3};
int b[3] = {4,5,6};

и когда я пытаюсь сделать то же самое,

    for(i=0; i<6; ++i){
        sum = sum + a[i];
    }

Я получаю вывод 123045. Таким образом, очевидно, что они не гарантированно будут храниться рядом друг с другом, если не в структуре.

Если я вместо этого сохраню массив и целое число после него, они, похоже, всегда будут в памяти друг за другом, т.е.

int a[3] = {1,2,3};
int x = 1000;

и

    for(i=0; i<4; ++i){
        printf("%d", a[i]);
    }

дает 1231000.

Я понимаю, что компилятор, вероятно, выбирает, где хранить переменные, основываясь на некоторых сложных факторах, находящихся вне моего контроля, но есть ли правила, которые гарантируют , где две переменные будут храниться относительно друг друга, вроде бы для структур?

Ответы [ 2 ]

3 голосов
/ 15 апреля 2019

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

Что касается конструкций, в разделе 6.7.2.1p15 C стандарта говорится:

Внутри объекта структуры, члены не-битового поля и блоки, в которых находятся битовые поля, имеют адреса, порядок, в котором они объявлены. Указатель на структуру объект, соответствующим образом преобразованный, указывает на его начальный член если этот член является битовым полем, то к единице, в которой он проживает), и наоборот. Там может быть безымянный отступ внутри объект структуры, но не в его начале.

Есть никаких гарантий относительно размещения одной несвязанной переменной в памяти другой.

Кроме того, когда вы делаете это:

struct sequences{
    int a[3];
    int b[3];
} sequence = {{1,2,3},{4,5,6}};
...

int i;
for(i=0; i<6; ++i){
    printf("%d", sequence.a[i]);
}

Вы вызываете неопределенное поведение , потому что индексируете после конца элемента массива a. Реализация не обязана читать элементы b в этом случае. Лучшее, что вы можете при соблюдении стандарта, это сравнить &sequence.a[3] == &sequence.b[0]

0 голосов
/ 15 апреля 2019

Элементы a и b в sequence должны быть смежными. Кроме того, адрес первого элемента a должен совпадать с адресом экземпляра struct.

Реализация может добавить заполнение между a и b и после b.

Вы не можете использовать арифметику указателя, чтобы определить, нет ли заполнения между a и b: поведение арифметики указателя определяется только в пределах массивов. Поэтому поведение представляемого вами кода, который запускает i до 5, не определено, и поэтому вы не можете использовать его для каких-либо надежных целей.

Поскольку допустимо указывать указатель на единицу вне массива (или объекта, но это здесь не актуально), выражение (void*)(&a + 1) является допустимым, поэтому вы можете использовать тест

(void*)(&a + 1) == (void*)&b;

для проверки смежности или иным образом a и b. (Подтвердите @EricPostpischil.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...