Можем ли мы использовать указатель в объединении?
Если нет, то почему? Использование союза над структурой?
Ответы
Ответ 1
Вы можете использовать любой тип данных в объединении, нет ограничений.
Что касается использования объединений над структурами, структуры излагают свои данные последовательно в памяти. Это означает, что все их подкомпоненты являются отдельными.
С другой стороны, союзы используют одну и ту же память для всех своих подкомпонентов, поэтому только один может существовать одновременно.
Например:
+-----+-----+
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;
Они полезны для экономии памяти, хотя в наши дни это все меньше и меньше беспокоит (кроме работы на низком уровне, например встроенные системы), поэтому вы не видите ее много.
Ответ 2
Хорошо, согласно 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
. Но я подозреваю, что в этот момент поведение undefined.
Во всяком случае, смысл этого небольшого упражнения состоял в том, чтобы показать вам, что:
- союзы могут использоваться для доступа к одному и тому же местоположению памяти с помощью другого модификатора типа.
- Вы должны быть осторожны, что вы делаете, и понимаете базовый тип, который вы используете. Если вы собираетесь использовать указатели, остерегайтесь их модификации, поскольку вы можете начать указывать, кто знает что.
Я должен указать, что я вижу практическое использование somenewtype
, но не для somepointer
- это был надуманный пример, который, я уверен, сломал бы.