19 смертных грехов, угрожающих безопасности программ - страница 37

Шрифт
Интервал


В отличие от Visual Basic, Java поддерживает лишь подмножество всего диапазона целочисленных типов. Хотя 64–разрядные целые поддерживаются, но единственным беззнаковым типом является char, и он представляется в виде 16–разрядного значения без знака.

Поскольку в Java есть только знаковые типы, проверка переполнения становится непростым делом, и по сравнению с C/C++ удается лишь избежать затруднений, связанных со смешанными операциями над знаковыми и беззнаковыми величинами.

Греховность Perl

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

$h = 4294967295;

$i = 0xffffffff;

$k = 0x80000000;

print "$h = 4294967295 – $h + 1 = ".($h + 1)."\n";

print "$i = 0xffffffff – $i + 1 = ".($i + 1)."\n";

printf("\nИспользуется printf со спецификатором %%d\n");

printf("\\$i = %d, \$i + 1 = %d\n\n", $i, $i + 1);

printf("\nТестируется граничный случай деления\n");

printf("0x80000000/-1 = %d\n", $k/-1);

print "0x80000000/-1 = ".($k/-1)."\n";

В результате печатается следующее:

[e:\projects\19_sins\perl foo.pl

4294967295 = 4294967295 – 4294967295 + 1 = 4294967296

4294967295 = 0xffffffff – 4294967295 + 1 = 4294967296

Используется printf со спецификатором %d

$i = -1, $i + 1 = -1

Тестируется граничный случай деления

0x80000000/-1 = -2147483648

0x80000000/-1 = -2147483648

На первый взгляд, результат выглядит странно, особенно когда используется printf с форматной строкой (в отличие от обычной функции print). Первым делом в глаза бросается то, что мы можем присвоить переменной максимально возможное значение без знака, но после прибавления к нему 1 она либо увеличивается на единицу, либо – если печатать с помощью %d – вообще не изменяется. Загвоздка в том, что на самом деле вы оперируете числами с плавающей точкой, а спецификатор %d заставляет Perl преобразовать double в int. В действительности никакого переполнения нет, но при печати результатов создается впечатление, будто оно произошло.

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