Трюки от Криса Крис Касперски Хакер, номер #090, стр. 090-128-2 Хак №4 – скрытые возможности цикла for Вот еще один типичный пример кода, написанный "правильным" программистом: Инициализация переменной перед входом в цикл (широко распространенный вариант) x = 0; for (a = 0; a < n; a++) { ... if (f()) x++; ... } Инициализация переменной x перед входом в цикл режет глаза, занимает целую строку и придает листингу некоторую небрежность. И это притом, что в каждом учебнике написано, что си допускает множественную инициализацию в циклах. Ну и что с того, что x не является параметром цикла? Компилятору ведь все равно, и никакой хакер не успокоится, пока не перепишет этот код так: Инициализация переменной перед входом в цикл (хакерский вариант) for (a = 0, x = 0; a < n; a++) { ... if (f()) x++; ... } При большом количестве переменных это здорово выручает! А вот еще более конкретный пример. В "каноническом" виде он выглядит так: Подсчет суммы ("канонический" вариант) sum = 0; for (a = 0; a < n; a++) sum += f(a); А что, если избавиться от тела цикла, поместив весь код в заголовок? Любой хакер без труда сделает это: for (a = 0, sum = 0; a < n; sum += f(a), a++); А вот еще более оптимизированный вариант (инкремент, совмещенный с передачей аргумента): for (a = 0, sum = 0; a < n; sum += f(a++)); Хак №5 – со знаком или без Общеизвестно, что ((unsigned char) 0xFF == (signed char) -1). Соответственно, на 32-разрядных платформах ((unsigned int) 0xFFFFFFFF == (signed int) -1). Естественно, написать -1 намного быстрее и надежнее, чем пересчитывать F'ы, рискуя пропустить один из них или переборщить. Вот конкретный пример хакерского кода: Гибридный хакерский цикл со знаковыми и без знаковых параметров unsigned int a; for (a = 0; a < -1UL; a++) printf("%x\n",a); С точки зрения "нормального" прикладного программиста, этот код вообще не должен работать, поскольку (0 > -1), и цикл ни разу не выполнится. Но ведь это не обычный -1, а с суффиксом UL, что равносильно конструкции ((unsigned int) -1), после преобразования которой мы получим 0xFFFFFFFF. Развиваю мысль дальше: можно не только сократить исходный текст, но и оптимизировать машинный код. Возьмем конструкцию вида ("правильный" вариант проверки диапазона): signed char x, y; if ((x > 0) && (x < y)) ... Для хакеров очевидно, что проверка на положительное значение x избыточна и от нее легко избавиться, переписав код так: if ((unsigned int) x < y)) ... Хак №6 – лишние аргументы Си не требует соблюдения прототипов функций, что открывает большие возможности для трюкачества. Поскольку аргументы заносятся справа налево (то есть на вершине стека оказывается крайний левый аргумент), а стек чистит материнская функция, то лишние аргументы попросту игнорируются. И вот тут начинается самое интересное. Поскольку в качестве аргументов допускается использовать выражения (а выражением в си, как уже говорилось, может быть практически все что угодно), мы получаем мощный инструмент для борьбы с фигурными скобками. Вот, например: if (a) { x += f(a); if (n < MAX) n++; } Первым делом освобождаемся от оператора if, преобразуя его в выражение: ((n < MAX) && n++), и передаем его функции f() как "сверхплановый" аргумент: |