Этот код гарантирован стандартом C?

Я прочитал, что если вы объявляете две такие структуры:

struct Node { int a, b, c; }; struct DerivedNode { struct Node base; int d, e, f; }; 

Затем вы можете использовать указатели для них следующим образом:

 struct DerivedNode myDerivedNode; struct Node *regularNode = (struct Node *) &myDerivedNode; regularNode->a = 3; 

Другими словами, смещения адресов для a, b, c одинаковы в struct Node и struct DerivedNode . Таким образом, вы можете получить от него какой-то polymorphism, где вы можете передать принудительно (struct Node *) -cast DerivedNode указатель везде, где обычно указывается указатель узла.

Мой вопрос заключается в том, гарантировано ли это поведение. Я знаю, что есть некоторые странные проблемы с выравниванием памяти и что компилятор иногда переупорядочивает поля, чтобы добиться лучшей упаковки в памяти. Будет ли base поле когда-либо размещаться в любом месте, кроме начала struct DerivedNode ?

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

Соответствующие выдержки из стандарта ANSI C:

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

Это говорит о том, что участники излагаются последовательно.

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

Средство означает, что первый элемент помещен со смещением 0.

Примечание. Стандартные выдержки из раздела 6.7.2.1 проекта ISO / IEC 9899: TC3, сентябрь 2007 года.

Как утверждает Дэвид, это гарантируется, поскольку base остается первым элементом в DerivedNode .

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

 struct Node *regularNode = &myDerivNode.base; 

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

Это не ответит на ваш вопрос, но тот, кто заботится о написании стандартного ANSI (ISO) C, может скомпилировать свой код с помощью gcc -pedantic или -pedantic-errors . Эти варианты должны поднять компиляцию предупреждений / ошибок на нестандартные строки кода.

Обратите внимание, что это не на 100% эффективнее, от man gcc :

[-pedantic] находит некоторые методы, отличные от ISO, но не все – только те, для которых ISO C требует диагностики, и некоторые другие, для которых была добавлена ​​диагностика.