Союз памяти доля в C - PullRequest
       1

Союз памяти доля в C

4 голосов
/ 12 января 2011

Edit2: я могу сделать полиморфизм с Союзом? Мне кажется, что я могу изменить структуру данных в зависимости от своих потребностей.

Редактировать: исправить код. Используйте "." вместо "->". Я хочу спросить, как убедиться, что значение хранится правильно, когда есть разные типы данных (например, int и char используют взаимозаменяемо? Поскольку оба имеют разный объем памяти, тот, который требует большего объема памяти, будет выделять пространство памяти для обоих типы переменных для обмена.

Предположим, у меня есть 2 структуры:

typedef struct a{
          int a;
}aType;

typedef struct b{
          char b;
}bType;

typedef union{
         aType a_type;
         bType b_type;
}ab;

int main(void){
         ab v1;
         v1.a_type.a = 5;
         v1.b_type.b = 'a'
}

Насколько я знаю, и aType, и bType будут использовать одну и ту же память. Поскольку int имеет на 3 байта больше (int составляет 4 байта, а char - 1 байт), он будет иметь 4 блока памяти. Первый - самый левый, а последний - самый правый. Когда я назначу 'a' переменной b v1, она останется в первом блоке (крайнем левом) блока памяти. Значение 5 все еще остается в четвертом блоке памяти (самое правое).

Поэтому, когда он будет распечатан, он будет выдавать мусор, не так ли? Если так, как решить эту проблему? Из-за этой проблемы, которая означает, что если я сохраню 'a' в b_type, то в общей памяти должно быть обязательно только это значение 'a', а не предыдущее целое значение 5.

Ответы [ 5 ]

7 голосов
/ 12 января 2011

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

В те времена, когда я почти все программировал на C, было два (переносимых) метода, использующих союзы, на которые я довольно сильно полагался.* помеченный союз .Это здорово, когда вам нужна динамически типизированная переменная.Вы устанавливаете структуру с двумя полями: дискриминант типа и объединение всех возможных типов.

struct variant {
  enum { INT, CHAR, FLOAT } type;
  union value {
    int i;
    char c;
    float f;
  };
};

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

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

typedef union {
  void *v;
  int* i;
  char* c;
  float* f;
} ptr;

Этоособенно полезно для (де) сериализации двоичных данных:

// serialize
ptr *p;
p.v = ...; // set output buffer
*p.c++ = 'a';
*p.i++ = 12345;
*p.f++ = 3.14159;

// deserialize
ptr *p;
p.v = ...; // set input buffer
char c = *p.c++;
int i = *p.i++;
float f = *p.f++;

К вашему сведению: Вы можете упростить свой пример.Структуры не нужны.Вы получите то же поведение с этим:

int main() {

  union {
    int a;
    char b;
  } v1;

  v1.a = 5;
  v1.b = 'a';
}
1 голос
/ 12 января 2011

Поведение, которое вы описываете, зависит от платформы / системы / компилятора. Например, на процессорах Intel x86 5, вероятно, будет первым байтом в int для компилятора gcc.

Интерес union исходит из двух основных углов

  • совместно используют одно и то же пространство памяти для минимизации необходимого выделения памяти (в этом случае первый байт [например] может указывать тип данных в структуре / объединении).
  • для анализа некоторой структуры данных, без необходимости использования приведения и указателей. Например, объединение между double и char[8] на некоторых платформах - это простой способ получить представление в расчете на символ / байт структуры double.

Если нет смысла использовать union, не делайте этого.

0 голосов
/ 12 января 2011

Ну, во-первых, мы должны знать, используете ли вы процессор Big Endian od Little Endian.В Windows & Linux используется формат с прямым порядком байтов, что означает, что значение 0x00000005 фактически записывается как 05-00-00-00, как будто вы пишете его справа налево.
Итак, сначала вы ставите 5 в часть, что означаетпервый байт - 05, а все остальные - 00. Когда вы помещаете «a» в часть b, вы перезаписываете 05 соответствующим значением ascii, что означает 0x61.Когда вы посмотрите на результирующее число должно быть ... 97, это значение 0x61.

Выравнивание объединения должно начинаться с начала, но порядок байтов зависит от платформы.Что вы сказали правильно в архитектуре Big Endian, как Sun Solaris или любой процессор Risc.

Я не прав?

HTH

0 голосов
/ 12 января 2011

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

Редактировать: Люди упоминали отмеченные союзы. Вот еще один стиль, который SDL использует для своей структуры событий.

enum union_tag {
    STRUCT_A,
    STRUCT_B
};

typedef struct {
    enum union_tag tag;
    int a;
} aType;

typedef struct {
    enum union_tag tag;
    char b;
} bType;

typedef union{
    enum union_tag tag;
    aType a_type;
    bType b_type;
} ab;

Чтобы получить доступ к элементу, вы должны сделать что-то вроде этого:

int result;

switch(my_union.tag){
    case STRUCT_A:
         result = my_union.a_type.a;
         break;
    case STRUCT_B:
         result = my_union.b_type.b;
         break;
}
0 голосов
/ 12 января 2011

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

struct mystructA {
    int data;
};
struct mystructB {
    char data;
};
enum data_tag {
    TAG_STRUCT_A,
    TAG_STRUCT_B
};
struct combined {
    enum data_tag tag;
    union {
        struct mystructA value_a;
        struct mystructA value_b;
    } data;
};

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

...