Чтение онлайн

ЖАНРЫ

Linux программирование в примерах

Роббинс Арнольд

Шрифт:

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

#define hname sub.hash.name

#define hlength sub.hash.length

#define hvalue sub.hash.value

...

 } sub;

 NODETYPE type;

 unsigned short flags;

 ...

} NODE;

#define vname sub.nodep.name

#define exec_count sub.nodep.reflags

#define lnode sub.nodep.l.lptr

#define nextp sub.nodep.l.lptr

#define source_file sub.nodep.name

#define source_line sub.nodep.number

#define param_cnt sub.nodep.number

#define param sub.nodep.l.param_name

#define stptr sub.val.sp

#define stlen sub.val.slen

#define stref sub.val.sref

#define stfmt sub.val.idx

#define var_value lnode

...

В

NODE
есть объединение внутри структуры внутри объединения внутри структуры! (Ой.) Поверх всего этого многочисленные «поля» макросов соответствуют одним и тем же компонентам
struct
/
union
в зависимости от того, что на самом деле хранится в
NODE
! (Снова ой.)

Преимуществом такой сложности является то, что код С сравнительно ясный. Нечто вроде '

NF_node->var_value->slen
' читать просто.

У такой гибкости, которую предоставляют объединения, конечно, есть своя цена. Когда отладчик находится глубоко во внутренностях вашего кода, вы не можете использовать симпатичные макросы, которые имеются в исходном коде. Вы должны использовать развернутое значение. [172]для этого придется найти в заголовочном файле соответствующее определение.)

Например, сравните '

NF_node->var_value->slen
' с развернутой формой: '
NF_node->sub.nodep.l.lptr->sub.val.slen
'! Чтобы увидеть значение данных, вы должны набрать последнее в GDB. Взгляните снова на это извлечение из приведенного ранее сеанса отладки GDB:

172

Опять-таки, GCC 3.1 или более новый и GDB 5 дают возможность непосредственного использования макросов, но только лишь если вы используете их совместно, с определенными опциями. Это было описано ранее в разделе 15.4.1.2 «По возможности избегайте макросов с выражениями». — Примеч. автора.

(gdb) print *tree /* Вывести NODE */

$1 = {sub = {nodep =

 {1 = {lptr = 0x8095598, param_name = 0x8095598 "xU\t\b",

 ll = 134829464}, r = {rptr = 0x0, pptr = 0, preg = 0x0,

 hd = 0x0, av = 0x0, r_ent =0), x = {extra = 0x0, xl = 0,

 param_list = 0x0}, name = 0x0, number = 1, reflags = 0},

 val = { fltnum = 6.6614606209589101e-316, sp = 0x0,

 slen = 0, sref = 1, idx = 0),

 hash = {next = 0x8095598, name = 0x0, length = 0,

 value = 0x0, ref = 1}}, type = Node_K_print, flags = 1}

Это куча вязкой массы. Однако, GDB все же несколько упрощает ее обработку.

Вы можете использовать выражения вроде '
($1).sub.val.slen
', чтобы пройти через дерево и перечислить структуры данных.

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

ch15-union.c
, в котором доступ к обоим «элементам» объединения осуществлялся одновременно.

Вторая причина, связанная с первой, заключается в осторожности с перекрытиями вложенных комбинаций

struct
/
union
. Например, в предыдущей версии
gawk
[173] был такой код.

/* n->lnode перекрывает размер массива, не вызывайте unref, если это массив */

if (n->type != Node_var_array && n->type != Node_array_ref)

unref(n->lnode);

Первоначально

if
не было, был только вызов
unref
, которая освобождает
NODE
, на которую указывает
n->lnode
. Однако, в этот момент
gawk
могла создать аварийную ситуацию. Можете себе представить, сколько времени потребовало отслеживание в отладчике того факта, что то, что рассматривалось как указатель, на самом деле было размером массива!

173

Эта часть кода была с тех пор пересмотрена, поэтому там больше нет этих строк из примера. — Примеч. автора.

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

Рекомендация: по возможности избегайте объединений (

union
). Если это невозможно, тщательно проектируйте и программируйте их!

15.4.2. Отлаживаемый код времени исполнения

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

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

15.4.2.1. Добавляйте отладочные опции и переменные

Простейшей методикой является наличие опции командной строки, делающих возможным отладку. Такая опция может быть условно откомпилированной для отладки. Однако более гибким подходом является оставить опцию в готовой версии программы. (Вы можете также решить, оставлять или не оставлять эту опцию не документированной. Здесь есть различные компромиссы: ее документирование может дать возможность вашим покупателям или клиентам больше изучить внутренности вашей системы, чего вы можете не хотеть С другой стороны, не документирование ее кажется довольно подлым. Если вы пишете для Open Source или Free Software, лучше документировать опцию.)

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

struct option options[] = {

 ...

 { "debug", required_argument, NULL, 'D' },

 ...

};

int main(int argc, char **argv) {

Поделиться с друзьями: