Об одной неочевидной ошибке в реализации функции strcmp
А. А. Вылиток
Оказалось, что некоторые студенты при выполнении упражения по реализации библиотечной функции сравнения двух строк
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 обладает таким же недостатком.