2013-06-24 2 views
5

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

Это базовый случай:

struct Point { 
    union { 
     struct { 
      float x, y; 
     }; 
     float v[2]; 
    }; 
}; 

Таким образом, запись в x, а затем чтение из v [0] будет неопределенной, поскольку вы ожидаете, что они будут одинаковыми, но может быть и так.

Не уверен, если это в стандарте, но союзах одного и того же типа ...

union{ float a; float b; };

ли неопределенными писать к, а затем читать из б?

То есть стандарт говорит о двоичном представлении массивов и последовательных переменных того же типа.

+0

Вы можете просто выделить фрагмент кода и нажать Ctrl + K. Используйте только обратные ссылки для короткого кода одной строки. – jrok

+0

Без имени 'struct' in union не являются частью ISO-C++ (хотя они поддерживаются многими компиляторами в качестве расширения). – ComicSansMS

+0

@ComicSansMS Хорошая точка, но ... Дайте 'struct' имя, и его вопрос по-прежнему справедлив. –

ответ

6

В стандарте указано, что чтение из любого элемента в соединении другого , чем последнее, написанное, является неопределенным поведением. Теоретически компилятор мог генерировать код, который каким-то образом отслеживал , считывал и записывал и запускал сигнал, если вы нарушили правило (даже если оба они одного типа). Компилятор может также использовать тот факт, для какого-то оптимизации: если вы пишите a (или x), то можно предположить, что вы не читаете b (или v[0]) при оптимизации.

На практике каждый компилятор, который я знаю, поддерживает это, если объединение отчетливо видно, и есть случаи, во многих (большинство ?, все?) где даже легальное использование потерпит неудачу, если объединение не видно (например:

union U { int i; float f; }; 

int f(int* pi, int* pf) { int r = *pi; *pf = 3.14159; return r; } 

// ... 
U u; 
u.i = 1; 
std::cout << f(&u.i, &u.f); 

Я действительно видел это провал с г ++, хотя в соответствии со стандартом , это совершенно законно.)

Кроме того, даже если компилятор поддерживает запись на Point::x и с Point::v[0], нет гарантии, что Point::y и Point::v[1] даже имеют одинаковый физический адрес.

+0

Спасибо за ответ! Ваш последний пункт о 'y' и' v [1] ', не имеющий гарантии, напомнил мне некоторый код opengl, в частности передавая вершинные данные в gpu. Не уверен, что вы знакомы с glVertexAttribPointer(), но я предполагаю, что код будет выглядеть так: 'struct Vertex { float x, y, z; float u, v; } вершины [10]; glVertexAttribPointer (..., 3, GL_FLOAT, ..., & verticies.x); ' Я предполагаю, что opengl будет рассматривать его как массив, но если нет гарантии, это будет то же самое, то этот код будет считаться неопределенным? – johndoe

+0

@skin Если они действительно обрабатывают '& verticies.x' как указатель на первый элемент массива, поведение не определено (и есть или, по крайней мере, были компиляторы, которые в таких случаях могли бы быть повреждены). –

-4

Я не понял, почему вы использовали float v [2];

Простой союз для точечной структуры может быть определена как:

union{ 

struct { 

    float a; 
    float b; 
}; 

} Point; 

Вы можете получить доступ к значениям в unioin как:

Point.a = 10.5; 

point.b = 12.2; //example 
+1

Вся точка объединения состоит в том, что вы можете получить доступ к x как к «Point.x», так и к «Point.v [0]». Это полезно, например, при работе с различными типами API, которые предпочитают либо массив, либо форму на основе координат. – ComicSansMS

+0

Это не то, о чем идет речь, и ваш 'union' вообще не имеет смысла быть« союзом »вообще,« структура »сделала бы (но опять же, это совершенно не имеет значения для фактического вопроса aynway). –

0

Стандарт требует, чтобы в союзе «[е] член данных, выделяется так, как если бы он был единственным членом структуры ». (9.5)

Он также требует, чтобы struct { float x, y; } и float v[2] должны иметь одинаковое внутреннее представление (9.2), и, таким образом, вы можете безопасно переинтерпретировать литой один как другой

Взятые вместе эти два правила гарантируют, что описанный вами union будет функционировать при условии, что он действительно записан в память. Однако, поскольку стандарт требует только того, чтобы последний член данных был действительным, теоретически возможно иметь реализацию, которая терпит неудачу, если объединение используется только как локальная переменная. Однако я был бы поражен, если это произойдет на самом деле.

+0

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