C11 (последний свободно доступный черновик) говорит только «В объекте структуры может быть безымянное дополнение, но не в его начале» (§6.7.2.1, абз. 15) и «Может быть безымянное дополнение в конце структуры или объединения» (§6.7.2.1, абз. 17). . Это не дает никаких дополнительных ограничений на отступы внутри структуры.
Платформа ABI может иметь более строгие требования к заполнению, но в зависимости от этого будет зависеть от платформы, так как другие платформы могут иметь другие требования к заполнению. x86-64 ABI для Unix/Linux дает char
выравнивание по 1 байту и указывает :
Структуры и союзы предполагают выравнивание их наиболее строго выровненного компонента. Каждому элементу назначается наименьшее доступное смещение с соответствующим выравниванием. Размер любого объекта всегда кратен выравниванию объекта.
Массив использует то же выравнивание, что и его элементы, за исключением того, что локальная или глобальная переменная массива длиной не менее 16 байт или переменная массива переменной длины C99 всегда имеет выравнивание не менее 16 байт4
Для объектов структуры и объединения может потребоваться дополнение, чтобы соответствовать ограничениям по размеру и выравниванию. Содержимое любого заполнения не определено.
4Требование к выравниванию позволяет использовать инструкции SSE при работе с массивом. В общем случае компилятор не может вычислить размер массива переменной длины (VLA), но ожидается, что большинству VLA потребуется как минимум 16 байт, поэтому логично предписать, чтобы VLA имели выравнивание по крайней мере по 16 байтам. .
Кажется, это означает, что на этой платформе внутри структуры не будет отступов. Однако бывают случаи, когда переменные массива имеют более строгие ограничения по выравниванию, чтобы их можно было использовать с векторными инструкциями; другие платформы также могут налагать такие ограничения на элементы структуры массива.
Если вы хотите быть переносимым, читая структуру за один вызов, вы можете посмотреть readv
. Это векторная операция ввода-вывода или разброс/сборка, которая позволяет указать массив массивов и длин для чтения. Например, для этого случая вы можете написать:
struct header h;
struct iovec iov[10];
iov[0].iov_base = &h.name;
iov[0].iov_len = sizeof(h.name);
iov[1].iov_base = &h.mode;
iov[1].iov_len = sizeof(h.mode);
/* ... etc ... */
bytes_read = readv(fd, iov, 10);
Обратите внимание, что readv
определяется в спецификации POSIX/Single Unix, а не в стандарте C. В стандартном C проще всего просто прочитать каждый из этих элементов по отдельности (и даже при наличии векторного ввода-вывода простое чтение и запись каждого элемента по отдельности, вероятно, будет более понятным, если только вам абсолютно не нужно использовать один вызов для всю операцию ввода-вывода).
В своем редактировании вы пишете:
Принятый ответ дал бы строгую или максимально строгую переносимую реализацию в соответствии с одним из стандартов C, которая позволяет мне обрабатывать эти поля с помощью стандартных библиотечных строковых функций. Учитывая CHAR_BITS
и все такое. Я думаю, что для этого нужно прочитать массив из 512 uint8_t
, а после этого, возможно, преобразовать их в символы один за другим. Есть ли способ проще?
Спецификация C не гарантирует, что uint8_t
доступен: «Имя typedef uintN_t
обозначает беззнаковый целочисленный тип с шириной N и без битов заполнения... Эти типы являются необязательными». (проект C11, §7.20.1.1, §2–3). Однако, если доступны 8-битные значения, то char
гарантированно будет 8-битным значением, так как оно гарантированно будет не менее 8-битным и гарантированно будет наименьшим объектом, не являющимся битовым полем (§5.2.4.2). .1 ¶1):
Приведенные ниже значения должны быть заменены постоянными выражениями, подходящими для использования в #if
директивах предварительной обработки. Кроме того, за исключением CHAR_BIT
и MB_LEN_MAX
, следующие должны быть заменены выражениями того же типа, что и выражение, являющееся объектом соответствующего типа, преобразованным в соответствии с целочисленными акциями. Их определяемые реализацией значения должны быть равны или больше по величине (абсолютное значение) показанным с тем же знаком.
- — number of bits for smallest object that is not a bit-field (byte)
CHAR_BIT 8
Итак, если у вас нет доступных 8-битных байтов, вы не сможете напрямую прочитать эти поля и получить доступ к октетам из них как к отдельным элементам массива; вам придется вручную разделить отдельные байты, используя сдвиг битов и маскирование. Тем не менее, я не знаю современных архитектур, в которых не хватает 8-битных байтов (для вычислений общего назначения, где файловый ввод-вывод вообще вызывает беспокойство; некоторые DSP могут, но они, вероятно, не будут иметь стандартного файлового ввода-вывода C. ).
Если у вас есть 8-битные байты, то char
гарантированно будет 8-битным, поэтому нет особой пользы, кроме ясности для использования uint8_t
против char
. Если вы действительно обеспокоены, я бы просто удостоверился, что где-то в процессе сборки вы проверили, что CHAR_BIT
равно 8, и назвали бы это хорошим.
person
Brian Campbell
schedule
24.03.2014
char
, это формальность. Единственный гарантированный безопасный способ сделать это — почленно, как на стороне чтения, так и на стороне записи. да, это боль, и да, вы, вероятно, можете обмануть, используя инструкции, специфичные для реализации, для упаковки вашей структуры (с обеих сторон). - person WhozCraig   schedule 24.03.2014typeflag
иtail
. Если бы машина была в основном ориентирована на слова (базовый доступ был 16-битными единицами на четных границах байтов — я не знаю ни одной такой машины в настоящее время), то компилятор мог бы повысить производительность за счет добавления заполнения этих полей. OTOH, даже для таких компов компилятора наверное нет. - person Jonathan Leffler   schedule 24.03.2014