А. А. Вылиток
Оказалось, что некоторые студенты при выполнении упражения по реализации библиотечной функции сравнения двух строк int strcmp ( const char *s1, const char *s2 ) забывают учесть машинное представление символьного типа в стандарте языка Си.
Итак, есть задача:
«Реализовать функцию int strcmp ( const char *s1, const char *s2 ), которая сравнивает две строки и возвращает 0, если строки равны; значение больше нуля, если первая строка больше второй; меньше нуля — если, соответственно, меньше. Считать, что коды символов в используемой реализации Си-машины упорядочены по алфавиту».
А вот пример решения:
int strcmp ( const char *s1, const char *s2 ) { for( ; *s1 == *s2; ++s1, ++s2 ) if ( *s2 == '\0' ) return 0; return *s1 - *s2; }
Здесь ошибка в случае, когда в используемой реализации поведение char как целого типа совпадает с signed char (по стандарту char обязан совпадать либо с signed char, либо с unsigned char — это определяется реализацией), и в операторе return *s1 − *s2; код символа *s1 меньше 128, а код символа *s2 больше 127. Тогда при целочисленном расширении малых знаковых целых до int (по стандарту во всех операциях малые целые расширяются до int или unsigned int) получим вычитание из положительного числа отрицательного — результат будет положительный, что не удовлетворяет условию задачи.
Та же ошибка произойдет, если использовать фрагмент вида:
if ( *s1 > *s2 ) return 1; else return -1;
Поскольку при сравнении значений char также происходит целочисленное расширение до int (promotion), слева от > будет неотрицательное число, а справа — отрицательное.
Исправить ситуацию можно явным приведением типа:
return (unsigned char) *s1 - (unsigned char) *s2;
или соответственно
if ( (unsigned char) *s1 > (unsigned char) *s2 ) return 1; else return -1;
Более того, оказывается, опубликованная в книге K&R реализация strcmp обладает таким же недостатком.