Инициализация статического хранилища и именованных членов на языке Си - PullRequest
0 голосов
/ 22 января 2019

Глава 6.7.8.10 стандарта ISO / IEC C9899: 1999 описывает, как инициализируются объединения со статической продолжительностью хранения:

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

  • если он имеет тип указателя, он инициализируется нулевым указателем;
  • если он имеет арифметический тип, он инициализируется нулевым (положительным или без знака);
  • если это агрегат, каждый элемент инициализируется (рекурсивно) в соответствии с этими правилами;
  • если это объединение, первый именованный элемент инициализируется (рекурсивно) в соответствии с этими правилами.

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

typedef struct
{
    uint32_t a;
    uint32_t b;
    uint32_t c; 
    uint32_t d;
} my_structure_t;

typedef union
{
    uint8_t *first_member;
    my_structure_t later_member;  
} my_union_t;

static my_union_t data;

Где-то в стандарте C определено, как будет инициализироваться область памяти, занятая later_member? Поскольку нижеприведенные утверждения, я подозреваю, что это поведение, определяемое реализацией, однако мне нужно подтверждение и, по крайней мере, ссылки на некоторую документацию gcc, clang, ghs, где это описано.

http://www.open -std.org / ОТК1 / SC22 / WG14 / WWW / Docs / n1311.pdf

Затем следует DR_016, с которого (в вопросе 2) начинается с: актуально только для оборудования, на котором либо нулевой указатель, либо плавающий нулевая точка представлена ​​/ не / представлена ​​как все нулевые биты. Имеет дело с

union { char *p; int i; } x;

и затем заявляет:

Если нулевой указатель представлен как, скажем, 0x80000000, то не существует способа неявно инициализировать этот объект. Либо член p содержит ноль указатель или член i содержит 0, но не оба. Итак, поведение этой единицы перевода не определено. Это плохое состояние Дела. Я полагаю, что Комитет не намеревался запрещать большой класс неявно инициализированных объединений; это сделало бы большая часть существующего кода не соответствует.

Вопрос в основном нацелен на стандарт C99, однако сравнения с другими стандартами C более чем приветствуются.

Ответы [ 3 ]

0 голосов
/ 22 января 2019

Поскольку инициализируется только первый именованный элемент union, любые дополнительные оставшиеся байты, принадлежащие другим элементам, остаются неинициализированными и имеют неопределенные значения , а любой конечный отступ равен 0.

То же предложение, которое вы упомянули выше, сформулировано несколько иначе в C11:

, если это объединение, первый именованный элемент инициализируется (рекурсивно) в соответствии с этими правилами, и любымзаполнение инициализируется нулевыми битами;

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

0 голосов
/ 23 января 2019

Согласно стандарту C99, объекту объединения, который имеет статическую длительность хранения и не инициализирован явным образом, компилятор должен инициализировать первый именованный элемент (рекурсивно) 1) . Следовательно, вы не должны делать никаких предположений о значении членов, отличных от первого члена, если их размер больше, чем у первого именованного члена union, потому что согласно стандарту компилятор инициализирует только первый именованный член union и оставшиеся байты членов, размер которых превышает имя первого члена, неинициализирован.

C Стандарты не упоминают ничего о сегменте данных (инициализированный / неинициализированный), стеке, куче и т. Д. Все это зависит от архитектуры / платформы. Для инициализации объекта (в случае статической длительности хранения) в стандартах C указывается только то, что нужно инициализировать до 0 / NULL, а что нет, и не указывается, какой объект продолжительности хранения входит в какой сегмент. Стандартные спецификации предназначены для компиляторов, и ожидается, что за ними последует хороший компилятор. Обычно инициализированные статические данные 0 заносятся в .BSS (блок, начатый символом), а инициализированные данные 0 поступают в .DATA (сегмент данных). Таким образом, вы можете найти later_member структура (которая является вторым членом union my_union_t) членов значение 0, но это не всегда так.


Стандарт C11 включает в себя спецификацию о байтах заполнения для объединения (согласно 6.7.9p10) [выделение добавлено]:

10 Если объект, имеющий автоматическую продолжительность хранения, не инициализируется явно, его значение является неопределенным. Если объект со статической или потоковой длительностью хранения не инициализирован явно, то: ......

......

  • если это объединение, первый именованный элемент инициализируется (рекурсивно) в соответствии с этими правилами, а любое заполнение инициализируется нулевыми битами ;
0 голосов
/ 22 января 2019

В теории обнуляется только указатель, но обычно весь раздел .bss обнуляется. Чтобы убедиться, просто измените порядок членов.

...