Как получить доступ к структурам C с помощью объединения внутри структуры - PullRequest
0 голосов
/ 10 января 2019

Я работаю с чужим кодом C, где они определили следующее:

typedef struct {

  union{

    struct{
      int A;    // some data
     } structA;

    struct{
       char B;  // some alternative data
    } structB;

  } myUnion;

} myStruct;

Youch. Ранее в коде эти мега-структуры объявлялись, размещались и заполнялись данными. Я работаю над более поздним разделом кода, где мне передадут указатель на одну из этих структур, и я должен (A) определить, использовался ли тип structA или structB, а затем (B) прочитать фактические данные. Что-то вроде:

void myFunction(myStruct *s){

   if(s->myUnion.structA != NULL)      // compilation error here
      printf("This myStruct uses a structA, internal data is: %d\n", s->myUnion.structA.A);
   else
      printf("This myStruct uses a structB, internal data is: %c\n", s->myUnion.structB.B);

}

Очевидно, что выше не компилируется:

me@Linux:/home/me# gcc -Wall structsUnions.c
structsUnions.c: In function 'myFunction':
structsUnions.c:22:19: error: invalid operands to binary != (have 'struct <anonymous>' and 'void *')
   if(s->myUnion.structA != NULL)
                         ^
me@Linux:/home/me#

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

Должен существовать способ выделения пика в myStruct и определения того, находится ли structA или structB внутри myUnion.

Есть мысли?

Ответы [ 3 ]

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

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

Однако, в зависимости от данных, которые вы получаете, вы можете сделать обоснованное предположение в своем коде. Например, скажем, у меня есть следующее объединение в системе, которая использует IEEE 754 для типов с плавающей запятой:

typedef union _F_or_I
{
    float f;
    int32_t i;
    uint32_t bits;
} F_or_I;

Если первые 9 бит (знак и показатель степени) равны 0, это, вероятно, не число с плавающей запятой:

F_or_I foo = /* some value */;

if(!(foo.bits & 0xFF800000))
{
      // access member 'i'
}
else
{
      // access member 'f'
}

Конечно, этот способ не всегда точен в зависимости от того, какой именно тип (ы) вы используете в объединении (я бы даже не использовал его для типов, использованных в моем примере!), И от правильного способа Для этого нужно включить в родительский элемент struct элемент тега, который указывает, к какому члену объединения предполагается получить доступ.

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

В частности, ошибка компиляции в if(s->myUnion.structA != NULL) заключается в том, что вы не можете проверить, является ли структура нулевой, но только если элемент структуры равен нулю:

if(s->myUnion.structA.A != 0)
0 голосов
/ 10 января 2019

myUnion - это union, а не структура с двумя членами.
Это означает, что structA и structB совместно используют одну и ту же память (в большинстве реализаций C).
Это также означает, что char B и int A совместно используют одну и ту же память (опять же, в большинстве реализаций C), поэтому практически невозможно определить, какая из конструкций используется.
ANSI определяет, что профсоюз, по крайней мере, такой же большой, как и его самый большой член, и что он должен содержать одного из членов.
Он направлен на эффективную реализацию для повторного использования одной и той же памяти для всех членов.
Конечно, в некоторых реализациях проблемы с выравниванием могут привести к тому, что некоторые члены не будут перекрывать другие, но я не могу вспомнить ни одного, который мне известен, который не выравнивается по наибольшему члену.

...