2015-10-26 2 views
13

Я два Структуры, какВыводит указатель на разные структуры, которые могут быть значимыми в C89?

struct X { 
    int x; 
    double *y; 
}; 

struct Y { 
    int a; 
    double *b; 
    char c; 
}; 

бросает указатель struct Y на указатель на struct X гарантированно вести себя последовательно в разумных пределах (т.е. x->x и x->y соответствуют y->a и y->b соответственно) по стандарту C89? Менее важно, но было бы здорово, если бы вы также узнали, что это справедливо и для более поздних стандартов (например, C11) и других языков, которые имеют существенное синтаксическое и семантическое совпадение с C (например, C++ XX, Objective-C)?

+0

Я не знаю о C++, но это похоже на все версии C. Многие программные пакеты полагаются на это. – chqrlie

+2

C++ - это другой язык, а не более поздняя версия C –

+0

@ M.M Справедливая точка. Хотя, я думаю, естественно, что это любопытно об этом поведении на других языках, которые имеют много перекрытий с C. Будет редактировать, чтобы сделать это яснее – math4tots

ответ

7

Это неопределенное поведение. Python relied on this before and had to fix that. Если struct Y включают struct X в качестве первого элемента, вы можете использовать это для подобного эффекта:

struct Y { 
    struct X a; 
    char c; 
}; 

struct Y y; 
struct X *x = (struct X *) &y; /* legal! */ 

Там также особый случай для профсоюзов:

struct X { 
    int x; 
    double *y; 
}; 

struct Y { 
    int a; 
    double *b; 
    char c; 
}; 

union XY { 
    struct X x; 
    struct Y y; 
}; 

union XY xy; 
xy.x.x = 0; 
printf("%d\n", xy.y.a); /* legal! */ 

В более поздних версиях стандарта C , компилятор должен обрабатывать только X и Y объекты, псевдонизирующие друг друга, если такое определение объединения действительно в области видимости, но на C89 оно в основном должно предполагать, что такое определение существует где-то. Тем не менее, это не мешает лить struct Y * на struct X *; если компилятор знает, что конкретный struct Y не является частью союза, он все равно может предположить, что struct X * не может его псевдонимом.

+0

Прохладный спасибо, что это прекрасно! Я ожидал, что мой идеальный ответ будет ссылаться на стандарт C, но, честно говоря, я доверяю парням Python, чтобы они действительно знали их, и если они говорят, что это неопределенное поведение в официальном PEP, я довольно склонен доверять им. – math4tots

+0

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

+0

@FelixPalmen: для того, чтобы 'struct X' и' struct Y' были совместимы, 'struct X' должен был иметь этот элемент' char' в конце. – user2357112

2

C89 разрешает преобразование путем литья указателя на один тип объекта указателю на другой тип объекта.

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

Предполагая, что преобразование действительно приводит к действительному указателю, стандарт не определяет точно, как элементы struct выложены внутри представления объекта struct. Члены должны отображаться в том же порядке, что и в определении struct, и не должно быть никаких отступов до первого, но никакие другие детали не определены. В вашем примере, следовательно, гарантируется, что X->x будет соответствовать Y->a (предположим, что преобразованный указатель действительно в первую очередь), но не определено, будет ли X->y соответствовать Y->b.

+0

Как добавить член 'char', возможно, увеличит требование выравнивания структуры? Комитет должен отказаться от попытки поддержать DS9K. – chqrlie

+0

@chqrlie, дело не в том, должно ли добавление члена 'char' * увеличивать требование выравнивания, а о том, разрешено ли это * разрешать *. Это. Тем не менее, как не слишком надуманный пример, рассмотрим систему, которая выбирает выравнивание 'struct' с 8 байтами значений членов на 8-байтных границах, но для выравнивания больших '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Если такая система работает на машине, на которой 'int' и' double * 'равны 32 битам, то в точности пример OPs будет представлять собой другое требование выравнивания для двух его типов' struct'. –

+0

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