Язык
Порядок вычислений.
В С и C++ порядок вычислений операндов выражений, побочных эффектов и аргументов функций не определен. Например, в присваивании:
? n = (getcharQ " 8) getchar();
Второй getchar может быть вызван первым: порядок, в котором выражение записано, не обязательно соответствует порядку, в котором оно исполняется. В выражении:
? ptr[count] = name[++count];
Значение count может быть увеличено как до, так и после использования его в качестве индекса ptг, а в выражении:
? .printf("%c %c\n", getchar(), getchar());
Первый введенный символ может быть распечатан на втором месте, а не на первом. В выражении:
? printf("%f %s\n", logC-1.23), strerror(errno));
Значение errno может оказаться вычисленным до вызова log.
Для некоторых выражений существуют четкие правила вычисления. По определению все побочные эффекты и вызовы функций должны быть завершены до ближайшей точки с запятой или при вызове функции. Операторы && и | | выполняются слева направо и только до тех пор, пока это требуется для определения их значения (включая побочные эффекты). В операторе ?: сначала вычисляется условие, включая возможные побочные эффекты, и только после этого вычисляется одно из двух завершающих оператор выражений.
В Java порядок вычислений описан более жестко. В нем предусмотрено, что выражения, включая побочные выражения, вычисляются слева направо; правда, в одном авторитетном руководстве дан совет не писать кода, который бы "критично" зависел от этого порядка. Прислушаться к этому совету просто необходимо при создании программ, у которых есть хотя бы призрачный шанс конвертироваться в С или C++: как мы уже сказали, там нет никаких гарантий соблюдения такого же порядка. Конвертирование из одного языка в другой – экстремальный, но иногда весьма полезный тест на переносимость программы.
Наличие знака у char.
В С и C++ не определено, является ли char знаковым или беззнаковым типом данных. Это может привести к проблемам при использовании комбинаций char и int, как, например, в приводимом коде, где вызывается функция getchar(), возвращающая значение типа int. Если вы напишете:
?char с; /* должно было быть int */ ? с = getchar();
То значение с будет в диапазоне от 0 до 255, если char – беззнаковый тип, и в диапазоне от – 128 до 127, если char – знаковый тип; речь идет о практически стандартной конфигурации с 8-битовыми символами на машинах с дополнительным до двух кодом. Это имеет особый смысл, если символ должен использоваться как индекс массива или для проверки на EOF, который в stdio обычно представляется значением -1. Например, представим, что мы разработали этот код из параграфа 6.1 после исправления некоторых граничных условий в начальной версии.
Сравнение s[i] == EOF никогда не будет истиной, если char – беззнаковый тип:
Когда getchar возвратит EOF, в s[i] будет сохранено значение 255 (OxFF, результат преобразования – 1 в unsigned char). Если s[i] беззнаковое, то при сравнении с EOF его значение останется 255, и, следовательно, проверка не пройдет.
Однако, даже если char является знаковым типом, код все равно некорректен. В этом случае сравнение с EOF будет проходить нормально, но при вводе вполне допустимого значения OxFF оно будет воспринято как EOF и цикл будет прерван. Так что вне зависимости от того, знаковый или беззнаковый у вас char, хранить значение, возвращаемое getchar, вы должны в int, и тогда проверка на конец файла будет осуществляться нормально. Вот как должен выглядеть наш цикл в переносимом виде:
В языке Java вообще нет спецификатора unsigned; порядковые типы данных являются знаковыми, а тип char (16-битовый) – беззнаковым.