Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #94, ОКТЯБРЬ 2006 г.

Трюки от Крыса

Крис Касперски

Хакер, номер #094, стр. 094-134-2


Локальные переменные коллективного использования

Некоторые, может быть, еще помнят те времена, когда в СССР существовало такое понятие, как «персональный компьютер коллективного использования». Сейчас персоналки подешевели настолько, что эта проблема отпала сама собой, но вот локальные переменные создают больше проблем, чем все члены политбюро вместе взятые.

Этикет программирования ограничивает предельно разумную длину функций несколькими сотнями строк, рекомендуя дробить функции на элементарные функциональные единицы, которые проще отлаживать, да и компилируются они быстрее. Но это теоретически. Практически же, при «расщеплении» одной большой функции на несколько маленьких возникает проблема с разделом локальных переменных. Да, мы можем обособить фрагмент большой функции в отдельный функциональный фрагмент, но при этом он потянет за собой множество неявных аргументов — например, флагов, управляющих отладочным выводом, дескрипторов файлов, окон, элементов управления, да мало ли еще что!

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

А что если все локальные переменные загнать в структуру, передаваемую всем родственным функциям (по ссылке, конечно, чтобы они могли менять знания как заблагорассудиться).

Например:

foo()

{

int a, flag, x = 0, y = 0;

flag = get_config(is_debug_output_enabled);

for (a = 0; a < 0x669; a++)

{

x ^= a ^ (0-a);if (flag) printf("%d\n", x);

}

for (a = 0; a < 0x999; a++)

{

y ^= x + a >> ( a & 0xF); if (flag) printf("%d\n", y);

}

}

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

zoo(int flag, int x)

{

int a, y = 0;

for (a = 0; a < 0x999; a++)

{

y ^= x + a >> ( a & 0xF); if (flag) printf("%d\n", y);

} return y;

}

bar(int flag)

{

int a, x = 0;

for ( a= 0; a< 0x669; a++)

{

x ^=a ^ (0-a); if (flag) printf("%d\n", x);

} return x;

}

foo()

{

int a, flag;

flag = get_config(is_debug_output_enabled);

zoo(flag, bar(flag));

}

Данный пример не выглядит ужасающим только потому, что код «раскулачиваемой» функции foo() сравнительно невелик, да и переменных там мало. Но все-таки…. Попробуем их загнать в структуру?

struct L{int a; int x; int y; int flag;};

zoo(struct L *l)

{

for (l->a = 0; l->a < 0x669; l->a++)

{

l->y ^= l->x + l->a >> ( l->a & 0xF);

if (l->flag) printf("%d\n", l->y);

}

}

bar(struct L *l)

{

for (l->a = 0; l->a < 0x669; l->a++)

{

l->x ^= l->a ^ (0 - l->a);

if (l->flag) printf("%d\n", l->x);

}

}

foo()

{

struct L l; memset(&l, 0, sizeof(l));

Назад на стр. 094-134-1  Содержание  Вперед на стр. 094-134-3