Стандарт C четко гласит (C11 §7.24.4.2 Функция strcmp
):
Функция strcmp возвращает целое число больше чем, равное, или меньше нуля, соответственно, так как строка, на которую указывает s1, больше, равна или меньше, чем строка, на которую указывает s2.
Она не говорит, насколько больше или меньше чем ноль, результат должен быть; функция, которая всегда возвращает -1
, 0
или +1
, соответствует стандарту; так же как и функция, которая иногда возвращает значения с величиной, превышающей 1
, например -27
, 0
, +35
. Если ваш код должен соответствовать стандарту C, он не должен принимать какой-либо набор результатов; он может только предполагать, что знак результата верен.
Здесь приведена реализация strcmp()
- названная здесь str_cmp()
, так что результат можно сравнить с strcmp()
- который не возвращает -1
или +1
:
#include <string.h>
#include <stdio.h>
static int str_cmp(const char *s1, const char *s2)
{
while (*s1 == *s2 && *s1 != '\0')
s1++, s2++;
int c1 = (int)(unsigned char)*s1;
int c2 = (int)(unsigned char)*s2;
return (c1 - c2);
}
int main(void)
{
printf("%d ", strcmp("a", "a"));
printf("%d ", strcmp("abc", "aAioioa"));
printf("%d\n", strcmp("eer", "tsdf"));
printf("%d ", str_cmp("a", "a"));
printf("%d ", str_cmp("abc", "aAioioa"));
printf("%d\n", str_cmp("eer", "tsdf"));
return 0;
}
При запуске на Ma c (macOS Mojave 10.14.6; G CC 9.2.0; Xcode 11.13.1), я получаю вывод:
0 1 -1
0 33 -15
Я немного изменил ваши данные - "aaioioa"
стал "aAioioa"
. Общий результат ничем не отличается (но значение 33 больше, чем вы получили бы с исходной строкой) - возвращаемое значение меньше, равно или больше нуля, как требуется.
str_cmp()
функция является законной реализацией и слабо основана на исторически распространенной реализации strcmp()
. Он немного больше заботится о возвращаемом значении, но вы можете найти два его небольших варианта на стр. 106 Брайана В. Кернигана и Денниса М. Ричи C Язык программирования, 2nd Edn (1988) - один с использованием индексирование массива, другое с использованием указателей:
int strcmp(char *s, char *t)
{
int i;
for (i = 0; s[i] == t[i]; i++)
if (s[i] == '\0')
return 0;
return s[i] - t[i];
}
int strcmp(char *s, char *t)
{
for ( ; *s == *t; s++, t++)
if (*s == '\0')
return 0;
return *s - *t;
}
Код K & R может не вернуть ожидаемый результат, если простой тип char
подписан и если одна из строк содержит «символы с акцентом», символы из диапазон -128 .. -1 (или 0x80 .. 0xFF при просмотре в виде значений без знака). Приведение в моем коде str_cmp()
обрабатывает данные как unsigned char
(через приведение); (int)
приведение не является действительно необходимым из-за назначений. Вычитание двух unsigned char
значений, преобразованных в int
, дает результат в диапазоне -255
.. +255
. Однако современные версии библиотеки C не используют подобное прямое вычитание, если они возвращают только -1
, 0
или +1
.
Обратите внимание, что стандарт C11 § 7.24.4 Функции сравнения строк говорят:
Знак ненулевого значения, возвращаемого функциями сравнения memcmp
, strcmp
и strncmp
, определяется знаком Разница между значениями первой пары символов (оба интерпретируются как unsigned char
), которые отличаются в сравниваемых объектах.
Вы можете посмотреть Как проверить, соответствует ли значение строка? . Схема там показывает:
if (strcmp(first, second) == 0) // first equal to second
if (strcmp(first, second) <= 0) // first less than or equal to second
if (strcmp(first, second) < 0) // first less than second
if (strcmp(first, second) >= 0) // first greater than or equal to second
if (strcmp(first, second) > 0) // first greater than second
if (strcmp(first, second) != 0) // first unequal to second
Обратите внимание, что при сравнении с нулем используется тот же оператор сравнения, что и при выполнении теста.
Вы могли бы (но, вероятно, не должны 't) напишите:
if (strcmp(first, second) <= -1) // first less than second
if (strcmp(first, second) >= +1) // first greater than second
Вы все равно получите те же результаты, но это не имеет смысла; всегда сравнение с нулем проще и более равномерно.
Вы можете получить результат -1, 0, +1, используя:
unsigned char c1 = *s1;
unsigned char c2 = *s2;
return (c1 > c2) - (c1 < c2);
Для неограниченных целых чисел (вместо целых чисел, ограниченных 0. 255), это безопасно, поскольку позволяет избежать целочисленных переполнений, тогда как вычитание дает неверный результат. Для ограниченных целых чисел, связанных с 8-разрядными символами, переполнение при вычитании не является проблемой.