Возможная плохая реализация strcmp

Я нашел реализацию функции strcmp. Я показал ее другу, и он сказал следующее: «Стоит отметить, что она не всегда возвращает разницу между двумя разными символами; на самом деле разрешено возвращать любое целое число при условии, что знак так же, как разница между байтами». затем не дал мне никаких дополнительных объяснений, код такой

int
strcmp(s1, s2)
    register const char *s1, *s2;
{
    while (*s1 == *s2++)
        if (*s1++ == 0)
            return (0);
    return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1));
}

Может кто-нибудь объяснить, в чем ошибка? и какая строка может вызвать сбой?


person Kevin    schedule 15.03.2015    source источник
comment
Что из того, что сказал ваш друг, заставило вас поверить, что в этом коде есть ошибка?   -  person Sneftel    schedule 15.03.2015
comment
Это не ошибка, это просто стоит отметить. См. описание результата, в котором также используется слово подписать. (Стоит отметить: на этой странице упоминаются неподписанные сравнения.)   -  person Jongware    schedule 15.03.2015
comment
Что меня беспокоит, так это использование древнего и устаревшего способа объявления параметров функции.   -  person glglgl    schedule 15.03.2015
comment
Один из ваших друзей сказал, что он просто возвращает разницу, но другой друг сказал, что это не обязательно верно.   -  person P.P    schedule 15.03.2015
comment
@glglgl: согласен - и что еще более интригует, так это наличие const (функция стандарта C) в объявлении с использованием достандартной (K&R) нотации определения/объявления функции.   -  person Jonathan Leffler    schedule 15.03.2015
comment
На самом деле реализации могут различаться в зависимости от уровня оптимизации.   -  person Shafik Yaghmour    schedule 15.03.2015
comment
@Jonathan Leffler C89 представил const, а также разрешил этот стиль объявления функции. Это просто похоже на стиль кодирования, который был популярен в начале 90-х и пытался работать со стандартными и предстандартными компиляторами.   -  person chux - Reinstate Monica    schedule 15.03.2015
comment
@chux: я не оспариваю его законность. Просто кажется эксцентричным добавлять функцию (const) к определению старого стиля.   -  person Jonathan Leffler    schedule 15.03.2015
comment
@Jonathan Leffler Как бы вы закодировали это для двойной компиляции C89 / pre-standard? Включая const, код защищает тело функции от ошибки кодирования, которая изменила *s1 и т. д. Для компиляции до C89 const было бы просто устранено с помощью #define const. Не такой эксцентричный, но определенно старый стиль.   -  person chux - Reinstate Monica    schedule 15.03.2015


Ответы (3)


Ваш друг имеет в виду следующее: strcmp возвращает целое число, которое больше, равно или меньше 0. Не обязательно возвращать фактическую разницу между двумя символами. Однако это не ошибка.

Основная проблема этой реализации заключается в том, что она использует K&R C, т.е. предстандарт C, использованный в первом издании Язык программирования C Брайана Кернигана и Денниса Ритчи. Вместо этого вы всегда должны использовать стандартный C.

person Yu Hao    schedule 15.03.2015
comment
C89 допускал этот стиль объявления функций — он соответствует этому стандарту. - person chux - Reinstate Monica; 15.03.2015

Этот strcmp не обязательно возвращает реальную разницу между двумя строками. Он либо возвращает положительное целое число, либо отрицательное целое число, либо ноль.

Эта путаница вызвала серьезные уязвимости безопасности в таких программах, как MySQL.

«Проблема в том, что значение, возвращаемое этими функциями сравнения, иногда неправильно понимается разработчиками, поэтому они делают ошибки, например, думают, что эти функции могут возвращать только -1, 0 или 1. Или они могут думать, что возвращаемое значение можно безопасно привести к меньший тип, такой как char, но они не понимают, что усечение значения может привести к тому, что две области памяти будут считаться равными, когда это не так». [1]

Взгляните на этот патч из репозитория Wine:

+    ret = strcmp(file1, file2);
+    if (ret < 0) return -1;
+    if (ret > 0) return  1;
+    return  0;

Ссылки: [1]

person Arjun Sreedharan    schedule 15.03.2015
comment
Спецификация strcmp гласит: Функция strcmp возвращает целое число, большее, равное или меньшее нуля, соответственно, поскольку строка, на которую указывает s1, больше, равна или меньше строки, на которую указывает s2. поэтому любое положительное или отрицательное значение допустимо. Код, предполагающий, что диапазон будет {-1, 0, +1}, непростительно (но не безвозвратно) нарушен. Код, предполагающий, что возвращаемое значение будет мерой расстояния между двумя разными кодами символов, также непростительно (но не безвозвратно) нарушен. Вы не можете предполагать ни одно из этих действий: оба варианта допустимы. - person Jonathan Leffler; 15.03.2015
comment
Патч, таким образом, применяет пластырь к непростительно сломанному коду. Люди, которые писали код, предполагая, что strcmp() возвращает {-1, 0, +1}, просто не знали, как инструменты, которые они используют, определены и предназначены для работы. Я не говорю, что патч не был нужен в контексте — возможно, это было самое быстрое и, возможно, даже лучшее исправление. Но исходный код был написан плохо, иначе он не мог столкнуться с проблемами. - person Jonathan Leffler; 15.03.2015
comment
@Jonathan Leffler, как правильно написать эту функцию - person Kevin; 15.03.2015

Вот яблочная реализация strcmp()

int strcmp(const char *s1, const char *s2)
{
    for ( ; *s1 == *s2; s1++, s2++)
        if (*s1 == '\0')
            return 0;
    return ((*(unsigned char *)s1 < *(unsigned char *)s2) ? -1 : +1);
}

вот версия от jbox

int strcmp(const char *s1, const char *s2)
{
    int ret = 0;

    while (!(ret = *(unsigned char *) s1 - *(unsigned char *) s2)
           && *s2) ++s1, ++s2;

   if (ret < 0) ret = -1;
   else if (ret > 0) ret = 1 ;

   return ret;

}

вот реализация вики

int strcmp(const char* s1, const char* s2)
{
    while(*s1 && (*s1==*s2))
        s1++,s2++;
    return *(const unsigned char*)s1-*(const unsigned char*)s2;
}

вот реализация charsharp.com

int strcmp_ptr(char *src1, char *src2)
{
    int i=0;
    while((*src1!='\0') || (*src2!='\0'))
    {
        if(*src1 > *src2)
            return 1;
        if(*src1 < *src2)
            return -1;
        src1++;
        src2++;
    }
    return 0;
}

Обратите внимание, что все они работают, и все они соответствуют требованиям, изложенным в man-странице linux для strcmp. Вот что справочная страница говорит о возвращаемом значении:

"Функции strcmp() и strncmp() возвращают целое число, меньшее, равное или большее нуля, если найдено, что s1 (или его первые n байтов) соответственно меньше, соответствует или больше, чем с2."

person user3629249    schedule 15.03.2015