Сравнение указателей

Почему в следующем коде

int main(int argc, char* argv[])
{
    char* a = (char*)"string";
    char* b = (char*)"string";

    if (a == b) std::cout << "Hello world";
    return 0;
}

При сравнении мы получаем положительный результат? По идеи, мы сравниваем указатели, разве не так?

Но если объявить массив, то все должно правильно работать

int main(int argc, char* argv[])
{
    char a[] = "string";
    char b[] = "string";

    if (a == b) std::cout << "Hello world";
    return 0;
}

Тут уже не выведет сообщение. Почему так выходит?


Ответы (3 шт):

Автор решения: nyekitka

Потому что в первом примере вы присваиваете так называемый строковый литерал "string" в указатель, причём два раза в разные указатели. У вас два указателя ссылаются на один и тот же литерал. Во втором примере вы создаёте массив из char'ов, а под него компилятором выделяется статическая память, куда данная строка копируется.

→ Ссылка
Автор решения: Harry

Возьмем, например, Visual C++ 2019 и скомпилируем первый код

test.cpp

#include <iostream>
int main()
{
    char* a = (char*)"string";
    char* b = (char*)"string";
    if (a == b) std::cout << "Hello world";
}

следующим образом:

cl /EHsc test.cpp 

Результат — никакого вывода на экран... Добавим ключик /GF:

cl /EHsc /GF test.cpp 

и получим приветствие. И первое, и второе поведение вполне допустимы, потому что стандарт ничего не говорит о том, где именно хранятся строковые литералы. Даже не надо все эти переменные и приведения, достаточно кода

std::cout << ("a" == "a");

Так что в определенном смысле вопрос основан на неверной посылке... По сути, основываясь на представлении о равенстве/неравенстве указателей двух строковых литералов мы получаем в некотором смысле неопределенное поведение (не говоря уж об UB, вызванном приведением строкового литерала к типу char*).

А вот во втором случае дела обстоят совсем иначе, потому что здесь

char a[] = "string";
char b[] = "string";

имеются два разных массива в памяти, а строка — не более чем инициализатор... Изменяя содержимое a, вы никак не меняете тем самым содержимое b. Так что и адреса их различны.

→ Ссылка
Автор решения: HolyBlackCat

Строковые литералы с одинаковым текстом могут иметь одинаковые адреса, а могут и не иметь - это зависит от компилятора.

[lex.string]/9

Whether all string-literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified.

А во втором примере вы сравниваете не литералы, а обычные массивы, созданные из них. На них (как и на остальные объекты) это исключение не действует, и они обязаны иметь разные адреса.

→ Ссылка