Подтвердить что ты не робот

Непоследовательное значение strcmp() при передаче строк как указателей или как литералов

Я играл с strcmp, когда заметил это, вот код:

#include <string.h>
#include <stdio.h>

int main(){

    //passing strings directly
    printf("%d\n", strcmp("ahmad", "fatema"));

    //passing strings as pointers 
    char *a= "ahmad";
    char *b= "fatema";
    printf("%d\n",strcmp(a,b));

    return 0;

}

вывод:

-1
-5

не должен strcmp работать одинаково? Почему мне присваивается другое значение, когда я передаю строки как "ahmad" или как char* a = "ahmad". Когда вы передаете значения функции, они правильно распределены в стеке?

4b9b3361

Ответ 1

Скорее всего, вы видите результат оптимизации компилятора. Если мы проверим код с помощью gcc на godbolt, с уровнем оптимизации -O0, мы можем видеть, что в первом случае он не вызывает strcmp

movl    $-1, %esi   #,
movl    $.LC0, %edi #,
movl    $0, %eax    #,
call    printf  #

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

Во втором случае он вызывает вызов strcmp:

call    strcmp  #
movl    %eax, %esi  # D.2047,
movl    $.LC0, %edi #,
movl    $0, %eax    #,
call    printf  #

Это согласуется с тем фактом, что gcc имеет встроенный файл для strcmp, что будет использоваться gcc при постоянном складывании.

Если мы еще тест с использованием -O1 уровня оптимизации или выше gcc может сбрасывать оба случая, и результат будет -1 для обоих случаев:

movl    $-1, %esi   #,
movl    $.LC0, %edi #,
xorl    %eax, %eax  #
call    printf  #
movl    $-1, %esi   #,
movl    $.LC0, %edi #,
xorl    %eax, %eax  #
call    printf  #

При увеличении числа опций оптимизации оптимизатор может определить, что a и b указывают на константы, известные также во время компиляции, а также могут вычислять результат strcmp для этого случая также во время компиляции.

Мы можем подтвердить, что gcc использует встроенную функцию, создавая с - fno-builtin flag и отмечая, что вызов strcmp будет генерироваться для всех случаев.

clang немного отличается тем, что он вообще не складывается при использовании -O0, но будет складываться при -O1 и выше для обоих.

Обратите внимание, что любой отрицательный результат является полностью совместимым, мы можем видеть, перейдя в проект стандартного раздела C99 7.21.4.2 Функция strcmp, которая говорит (выделение мое):

int strcmp(const char *s1, const char *s2);

Функция strcmp возвращает целое число, большее, чем, или меньше чем нуль, соответственно, поскольку строка, на которую указывает s1, больше, чем, равно или меньше, чем строка, на которую указывает s2.

technosurus указывает, что strcmp указан для обработки строк, как если бы они были составлены из unsigned char, это описано в C99 под 7.21.1, в котором говорится:

Для всех функций в этом подпункте каждый символ должен быть интерпретируется так, как будто у него был тип unsigned char (и, следовательно, каждый возможное представление объекта допустимо и имеет другое значение).

Ответ 2

Я думаю, вы считаете, что значение, возвращаемое strcmp, должно каким-то образом зависеть от входных строк, переданных ему, таким образом, который не определен спецификацией функции. Это неверно. См., Например, определение POSIX:

http://pubs.opengroup.org/onlinepubs/009695399/functions/strcmp.html

После завершения strcmp() возвращает целое число, большее, равное или меньшее, чем 0, если строка, на которую указывает s1, больше, равна или меньше строки, на которую указывает s2, соответственно.

Это именно то, что вы видите. Реализация не требует никаких гарантий относительно точного значения возврата - только то, что меньше нуля, равно нулю или больше нуля, если это необходимо.