Можем ли мы использовать указатель в объединении? - PullRequest
11 голосов
/ 20 января 2011

Если нет, то почему?Использование объединения над структурой

Ответы [ 2 ]

11 голосов
/ 20 января 2011

Вы можете использовать любой тип данных в объединении, ограничений нет.

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

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

Например:

                                 +-----+-----+
struct { int a; float b }  gives |  a  |  b  |
                                 +-----+-----+
                                    ^     ^
                                    |     |
                 memory location:  150   154
                                    |
                                    V
                                 +-----+
union { int a; float b }  gives  |  a  |
                                 |  b  |
                                 +-----+

Структуры используются там, где «объект» состоит из других объектов, например точечного объекта, состоящего из двух целых чисел, координаты x и y:

typedef struct {
    int x;           // x and y are separate
    int y;
} tPoint;

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

typedef enum { STR, INT } tType;
typedef struct {
    tType typ;          // typ is separate.
    union {
        int ival;       // ival and sval occupy same memory.
        char *sval;
    }
} tVal;

Они полезны для экономии памяти, хотя в настоящее время это все меньше и меньше беспокоит (за исключением низкоуровневой работы, такой как встроенные системы), поэтому вы не видите ее много.

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

Ну, в соответствии с ISO / IEC 9899: TC3 (стандарт C99):

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

Короче говоря, пространство памяти членов объединения перекрывается, и имена, которые вы даете членам объединения, позволяют вам читать память в этом месте по размеру. Рассмотрим:

#include <stdio.h>
#include <stdint.h>

typedef union
{
    struct
    {
        uint8_t a;
        uint8_t b;
        uint8_t c;
        uint8_t d;
    };
    uint32_t x;
} somenewtype;

typedef union
{
    uint32_t* p;
    uint8_t* q;
} somepointer;

int main(int argc, char** argv)
{
    uint32_t r;
    uint8_t s;
    somenewtype z;
    somepointer p;
    r = 0x11223344; s = 0x11;
    z.x = 0x11223344;
    p.p = &r;
    p.q = &s;
    printf("%x%x%x%x\n", z.d, z.c, z.b, z.a);
    printf("%x %x\n", *(p.p), *(p.q));
}

В первом printf мы выводим 8-битные части 32-битного целого числа. Надеемся, конечно, что в этой анонимной структуре нет отступов.

Во втором printf? Мне пришлось пройти через использование GDB, чтобы понять, но я сделал:

p.p = (uint32_t *) 0x7fffffffde5c;
p.q =  (uint8_t *) 0x7fffffffde5b "\021D3\"\021P\337\377\377\377\177";
p.p = (uint32_t *) 0x7fffffffde5b;

Конечно, указатели имеют одинаковый размер, поэтому при присваивании p.q адрес p.p перезаписывается. Я сильно подозреваю, что разыменование адреса 32-разрядного целого числа для 8-разрядного указателя печатает «все, что находится в этом месте + размер 32 бита», что, кстати, для меня случайно оказывается 22334411. Но я подозреваю, что в этот момент поведение не определено.

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

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

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

...