выделение из стека - проблемы с выравниванием данных в C

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

char datablock[200];
char *pointer1=datablock;
char *pointer2=datablock+100;

Однако кто-то упомянул, что код может быть несовместим с 64-битными системами, потому что 100 не делится на 8? Я не могу вспомнить, что это было.

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

Итак, в коде это пример того, что я хочу сделать. Я хочу выделить 40 000 байт и дать 4 указателям доступ по 10 000 байт каждый:

char data[40000];
char *string0=data;
char *string1=data+10000;
char *string2=data+20000;
char *string3=data+30000;

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

char string0[10000];
char string1[10000];
char string2[10000];
char string3[10000];

Я считаю, что моя идея верна, но единственное, о чем мне нужно беспокоиться, это то, что для 64-битных систем значение смещения кратно 8, а для 32-битных систем значение смещения кратно 4?

Я не хочу выбирать неправильные номера и получать ошибки сегментации.


person Mike -- No longer here    schedule 31.08.2015    source источник
comment
"Однако кто-то упомянул, что код может быть несовместим с 64-битными системами, потому что 100 не делится на 8?" Кто-то ошибается.   -  person Ry-♦    schedule 31.08.2015
comment
У вас возникнут проблемы только в том случае, если вы используете эту память для элементов, отличных от char. Например, у вас могут возникнуть проблемы, если вы поместите в эту память int или double элементов.   -  person user3386109    schedule 31.08.2015
comment
XY-проблема? Чего вы на самом деле хотите достичь? Как заявил @ user3386109, если вы не попытаетесь неправильно использовать эти массивы для более широких типов данных, проблем не должно быть. Если вы собираетесь очищать кеш, вам нужно быть более конкретным и предоставить больше деталей.   -  person too honest for this site    schedule 31.08.2015
comment
Но как насчет ваших указателей?! Где они хранятся? Вместо выделения 4 объектов теперь вы выделяете 5.   -  person Bo Persson    schedule 31.08.2015
comment
Может быть, мне лучше со структурой   -  person Mike -- No longer here    schedule 01.09.2015


Ответы (2)


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

Это не ваш случай. Вы не храните указатели в невыровненных адресах, вы просто сохраняете адреса в выровненных указателях.

Просто чтобы было понятно:

char *pointer2=datablock+100;

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

Проблема может возникнуть, когда вы делаете что-то вроде:

int* asInteger = (int*) (datablock+1);
*datablock = 10;

В этой ситуации вы пытаетесь сохранить значение, имеющее требование выравнивания (int), по адресу, который может не соответствовать требованию int.

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

person Jack    schedule 31.08.2015
comment
Я пытаюсь визуализировать второй фрагмент кода... Означает ли это, что в 64-битной системе вы выделяете 8-байтовый указатель, в котором хранится значение размера int, так что зарезервированы смещения от 1 до 1+int size для целого числа? и число 10 перезаписывает первый байт зарезервированного целочисленного пространства, тем самым искажая целое число? - person Mike -- No longer here; 31.08.2015
comment
Во втором фрагменте кода выделяется указатель на int (в стеке), а не блок памяти, на который он указывает. Блок, на который указывает asInteger, будет начинаться через 1 байт после блока данных и иметь размер int (возможно, 4 байта). Вторая строка *datablock = 10; на самом деле не испортит int, на который указывает asInteger! - person Manos Nikolaidis; 31.08.2015
comment
@Mike: Во втором фрагменте кода вы говорите компилятору взять адрес блока данных, добавить 1 и обработать полученный адрес как адрес для хранения int. Теперь конкретная архитектура может потребовать, чтобы этот адрес был выровнен по определенному размеру, но, поскольку вы заставляете его считаться int*, это условие может быть неверным. - person Jack; 01.09.2015

Независимо от того, является ли система 32- или 64-разрядной, код, который вы упомянули, не приведет к ошибке сегментации. В этом примере арифметики указателя:

char *pointer2=datablock+100;

вы говорите: с адреса, на который указывает блок данных, в 100 раз больше размера символа. Число, которое вы продвигаете, не обязательно должно быть кратным любому другому числу.

Что касается последнего фрагмента кода, вероятно, 4 раздела памяти будут расположены в стеке последовательно, но.

Вы можете проверить и посмотреть, что происходит, напечатав адреса указателей. Например.

printf("string0 %p\n", string0);
printf("string1 %p\n", string1);
printf("string2 %p\n", string2);
printf("string3 %p\n", string3);
person Manos Nikolaidis    schedule 31.08.2015