C - принудительное выравнивание с помощью спецификатора _Alignas - почему заполнение памяти? - PullRequest
0 голосов
/ 09 мая 2018

У меня есть несколько вопросов о выравнивании памяти на языке C и о том, как выделяется память после принудительного выравнивания переменной с определенным числом байтов с использованием спецификатора _Alignas.

Этот код выводит адреса памяти каждой объявленной переменной:

#include <stdio.h>

int main(void)
{
       unsigned char dx = 1;
       unsigned char ca  = 1;
       unsigned char cx  = 1;
       unsigned char dz = 1;
       unsigned char cb = 1;
       unsigned char _Alignas(double)  cz = 1;

       char * p_begin = (char *) &cz;
       char * p_end = (char * ) &dx + sizeof(dx);

       printf("Addresses   Value\n");
       for (char * p = p_begin; p < p_end; p++)
       {
              printf("%9p   %6X", p, 0xff & *p);

              if (p == (char *) & dx) printf(" <- dx\n");
              else if (p == (char *) & ca) printf(" <- ca\n");
              else if (p == (char *) & cx) printf(" <- cx\n");
              else if (p == (char *) & dz) printf(" <- dz\n");
              else if (p == (char *) & cb) printf(" <- cb\n");
              else if (p == (char *) & cz) printf(" <- cz\n");
              else printf("\n");
       }

       return 0;
}

На моем компьютере вывод выглядит следующим образом:

Addresses   Value
   28ff08        1 <- cz
   28ff09       FF
   28ff0a       28
   28ff0b        0
   28ff0c       CE
   28ff0d       2A
   28ff0e       40
   28ff0f        1 <- cb
   28ff10        1 <- dz
   28ff11        1 <- cx
   28ff12        1 <- ca
   28ff13        1 <- dx

Process returned 0 (0x0)   execution time : 0.016 s
Press any key to continue.

Единственное поведение, которое я ожидал увидеть после принудительного выравнивания переменной cz на 8 байтов, - это то, что cz был назначен адресу, значение которого кратно 8, я не ожидал бы увидеть следующее cz от отступов. Как такое поведение? Также я ожидал, что число байтов, соответствующих заполнению, объединенному с размером данных в 1 байт, приведет к 8, вместо этого заполнение занимает 6 байтов по какой причине?

1 Ответ

0 голосов
/ 09 мая 2018

Стеки обычно растут вниз. Для этого нет существенной причины; это в основном просто результат исторического развития. Область стека выделяется для стека, указатель стека устанавливается так, чтобы он указывал на его верхнюю часть, и запускается основная процедура программы. Поскольку программе требуется больше места в стеке, она уменьшает указатель стека.

В вашем примере происходит то, что компилятор назначает пространство для объектов в том порядке, в котором он их встречает. dx отображается первым, поэтому ему присваивается следующее доступное пространство в стеке 0x28ff13. Затем видно ca и ему присваивается 0x28ff12. Мы видим, что по мере роста стека он переходит от более высоких адресов (с которых он начинался) к более низким адресам.

Когда компилятор получает значение cz, для которого вы запросили восьмибайтовое выравнивание, компилятор переходит к следующему кратному восьми (все еще в направлении вниз), которое равно 0x28ff08.

Конечно, компилятор мог бы взглянуть на всю ситуацию (вместо того, чтобы смотреть на каждый объект по одному) и поместить cz в 0x28ff10 и поместить вокруг него другие объекты, используя меньше места в стеке. Если вы компилируете с включенной оптимизацией, это может сделать компилятор. С другой стороны, компилятор может требовать от вашей платформы, чтобы указатель стека был выровнен до восьми байтов (или более), поэтому перестановка этих конкретных объектов в любом случае не сэкономила бы пространство стека.

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

...