(gdb) print flags2str(t[i]->flags) /* Вывести значение флага */
$5 = 0x80918a0 "MALLOC|PERM|STRING|STRCUR"
Надеемся, вы согласитесь, что настоящий механизм общего назначения значительно более элегантный и более простой в использовании, чем первоначальный.
Тщательное проектирование и использование массивов структур часто может заменить или слить воедино повторяющийся код.
15.4.1.5. По возможности избегайте объединений
«Не бывает бесплатных обедов»
union
С относительно эзотерическая возможность. Она помогает экономить память, сохраняя различные элементы в одном и том же физическом пространстве; как программа интерпретирует его, зависит от способа доступа:
/* ch15-union.c --- краткая демонстрация использования union. */
#include
int main(void) {
union i_f {
int i;
float f;
} u;
u.f = 12.34; /* Присвоить значение с плавающей точкой */
printf("%f also looks like %#x\n", u.f, u.i};
exit(0);
}
Вот что происходит, когда программа запускается на системе Intel x86 GNU/Linux:
$ ch15-union
12.340000 also looks like 0x414570a4
Программа выводит битовый паттерн, который представляет число с плавающей точкой в виде шестнадцатеричного целого. Оба поля занимают одно и то же место в памяти; разница в том, как этот участок памяти интерпретируется: u.f
действует, как число с плавающей точкой, тогда как эти же биты в u.i
действуют, как целое число.
Объединения особенно полезны в компиляторах и интерпретаторах, которые часто создают древовидные структуры, представляющие структуру файла с исходным кодом (которая называется деревом if
, операторы while
, операторы присваивания и так далее для всех экземпляров более общего типа «оператора». Таким образом, в компиляторе могло бы быть нечто подобное этому:
struct if_stmt { ... }; /* Структура для оператора IF */
struct while_stmt { ... }; /* Структура для оператора WHILE */
struct for_stmt { ... }; /* Структура для оператора */
/* ...структуры для других типов операторов... */
typedef enum stmt_type {
IF, WHILE, FOR, ...
} TYPE; /* Что у нас есть в действительности */
/* Здесь содержатся тип и объединения отдельных видов операторов. */
struct statement {
TYPE type;
union stmt {
struct if_stmt if_st;
struct while_stmt while_st;
struct for_stmt for_st;
...
} u;
};
Вместе с объединением удобно использовать макрос, который представляет компоненты объединения, как если бы они были полями структуры. Например:
#define if_s u.if_st /* Так что можно использовать s->if_s вместо s->u.if_st */
#define while_s u.while_st /* И так далее... */
#define for_s u.for_st
...
На только что представленном уровне это кажется разумным и выглядит осуществимым. В действительности, однако, все сложнее, и в реальных компиляторах и интерпретаторах часто есть несколько уровней вложенных структур и объединений. Сюда относится и gawk
, в котором определение NODE
, значение его флагов и макросов для доступа к компонентам объединения занимают свыше 120 строк![171] Здесь достаточно определений, чтобы дать вам представление о том, что происходит:
typedef struct exp_node {
union {
struct {
union {
struct exp_node *lptr;
char *param_name;
long ll;
} l;
union {
...
} r;
union {
...
} x;
char *name;
short number;
unsigned long reflags;
...
} nodep;
struct {
AWKNUM fltnum;
char *sp;
size_t slen;
long sref;
int idx;
} val;
struct {
struct exp_node *next;
char *name;
size_t length;
struct exp_node *value;
long ref;
} hash;
#define hnext sub.hash.next