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

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

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

Хакер, номер #096, стр. 096-144-1


Программисткие трюки и фичи на С\С++ от Криса Касперски

Трюк 1: массив, начинающийся в единицы

В Си массивы начинаются с нуля, а во многих других языках — с единицы, что сильно напрягает при переходе с одного языка на другой и еще больше — при переносе программ. Приходится вносить множество правок в самых разных местах и потом долго отлаживать программу. Да и сами сишники далеко не в восторге от того, что индекс последнего элемента массива размером N равен N-1. Это служит не только источником досадных ошибок, но в некоторых случаях снижает производительность.

Существует хитрый хак, позволяющий создавать массивы, начинающиеся с единицы, или, строго говоря, вообще с любого числа. Это невероятно полезно в тех случаях, когда нам нужно создать массив, проиндексированный так: k, k+1,,,k+n (например, возрастом человека от 16 лет до 100). Смотри:

// задаем размер массива, его тип и индекс первого элемента

#define SIZE_OF_ARRAY 0x1000

#define TYPE_OF_ARRAY int

#define INDEX_OF_FIRST_ELEMENT 1

// объявляем "сырую" переменную-указатель

TYPE_OF_ARRAY *p;

// выделяем блок памяти требуемого размера

p = (TYPE_OF_ARRAY*)malloc(SIZE_OF_ARRAY*sizeof(TYPE_OF_ARRAY));

// проверка на успешность выделения памяти

if (!p) /* обработка ситуации ошибки выделения */

// сдвигаем указатель на нужное кол-во позиций,

// так, чтобы индекс первого элемента стал равен INDEX_OF_FIRST_ELEMENT

p -= INDEX_OF_FIRST_ELEMENT;

…

// работаем с массивом

…

// возвращаем указатель на место

p += INDEX_OF_FIRST_ELEMENT;;

// освобождаем блок памяти

free(p);

Аналогичным путем можно обрабатывать и локальные массивы (в этом случае наша задача даже упрощается, поскольку не нужно заботиться об освобождении памяти):

// задаем размер массива, его тип и индекс первого элемента

#define SIZE_OF_ARRAY 0x1000

#define TYPE_OF_ARRAY int

#define INDEX_OF_FIRST_ELEMENT 1

// выделяем блок памяти требуемого размера

TYPE_OF_ARRAY raw[SIZE_OF_ARRAY];

TYPE_OF_ARRAY *p = raw;

// сдвигаем указатель на нужное кол-во позиций,

// так, чтобы индекс первого элемента стал равен INDEX_OF_FIRST_ELEMENT

p -= INDEX_OF_FIRST_ELEMENT;

Трюк 2: марафон битовых и логических операций

В некоторых руководствах и конференциях можно прочесть, что выражение вида "(a || b || c)" практически всегда быстрее, чем "(a | b | c)". Так ли это? И если так, то почему? Попробуем разобраться. Поскольку все современные компиляторы поддерживают "быстрые булевы операции", они вычисляют значение сложного выражения лишь до первой лжи. То есть, если a == 0, то оставшуюся часть выражения можно не проверять, поскольку уже и так все ясно.

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

Содержание  Вперед на стр. 096-144-2