Неоднозначное поведение strcmp () - PullRequest
3 голосов
/ 19 февраля 2020

Обратите внимание, что я проверил соответствующие вопросы к этому названию, но, с моей точки зрения, они не связаны с этим вопросом.

Сначала я думал, что program1 и program2 будут дайте мне тот же результат.

//Program 1

char *a = "abcd";
char *b = "efgh";
printf("%d", strcmp(a,b));


//Output: -4

//Program 2
printf("%d", strcmp("abcd", "efgh"));

//Output: -1

Единственное, что я могу заметить, это то, что в программе2 я передал строковый литерал, а в программе я передал char * в качестве аргумента strcmp() function.

Почему существует разница между поведением этих, казалось бы, одинаковых программ?

Платформа: Linux mint compiler: g ++

Edit : На самом деле программа1 всегда печатает разность кода ascii для первых несовпадающих символов, но программа2 печатает -1, если код ascii для первого несовпадающего символа в строке2 больше, чем для строки1, и наоборот.

Ответы [ 3 ]

5 голосов
/ 19 февраля 2020

Это ваш C код:

int x1()
{
  char *a = "abcd";
  char *b = "efgh";
  printf("%d", strcmp(a,b));
}

int x2()
{
  printf("%d", strcmp("abcd", "efgh"));
}

И это сгенерированный вывод сборки для обеих функций:

.LC0:
        .string "abcd"
.LC1:
        .string "efgh"
.LC2:
        .string "%d"
x1:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], OFFSET FLAT:.LC0
        mov     QWORD PTR [rbp-16], OFFSET FLAT:.LC1
        mov     rdx, QWORD PTR [rbp-16]
        mov     rax, QWORD PTR [rbp-8]
        mov     rsi, rdx
        mov     rdi, rax
        call    strcmp              // the strcmp function is actually called
        mov     esi, eax
        mov     edi, OFFSET FLAT:.LC2
        mov     eax, 0
        call    printf
        nop
        leave
        ret

x2:
        push    rbp
        mov     rbp, rsp
        mov     esi, -1             // strcmp is never called, the compiler
                                    // knows what the result will be and it just
                                    // uses -1
        mov     edi, OFFSET FLAT:.LC2
        mov     eax, 0
        call    printf
        nop
        pop     rbp
        ret

Когда компилятор видит strcmp("abcd", "efgh"), он знает результат заранее, потому что он знает, что "abcd" предшествует "efgh".

Но если он видит strcmp(a,b), он не знает и, следовательно, генерирует код, который на самом деле вызывает strcmp.

С другим компилятором или с другими настройками компилятора все может быть иначе. Вы действительно не должны заботиться о таких деталях, по крайней мере, на начальном уровне.

1 голос
/ 19 февраля 2020

Удивительно, что strcmp возвращает 2 разных значения для этих вызовов, но это не несовместимо с C Standard:

strcmp() возвращает отрицательное значение, если первая строка лексикографически перед второй строкой. И -4, и -1 являются отрицательными значениями.

Как указывалось другими, код, сгенерированный для различных вызовов, отличается:

  • компилятор генерирует вызов библиотечной функции в В первой программе
  • компилятор может определить результат сравнения и генерирует явный результат -1 для второго случая, когда оба аргумента являются строковыми литералами.

В Чтобы выполнить эту оценку времени компиляции, strcmp должен быть определен тонким способом в <string.h>, чтобы компилятор мог определить, что программа ссылается на реализацию библиотеки C, а не на альтернативу, которая может вести себя по-другому. Отслеживание соответствующего прототипа в последних включаемых файлах GNU lib c немного затруднительно, так как количество вложенных макросов в итоге приводит к скрытому прототипу.

Обратите внимание, что более поздние версии g cc и clang будут Выполните оптимизацию в обоих случаях, как это может быть протестировано на Godbolt Compiler Explorer , но ни один из этих вариантов не сочетается с опцией printf для создания еще более компактного кода puts("-1");. Кажется, они конвертируют printf в puts только для строковых литеральных форматов без аргументов.

0 голосов
/ 19 февраля 2020

Я считаю (нужно видеть (и интерпретировать) машинный код) одна версия работает без вызова кода в библиотеке (как если бы вы написали printf("%d", -1);).

...