Я вижу два 'some'
литерала в коде ассемблера, сгенерированном MSVC, но только один с clang и gcc. Это приводит к совершенно другим результатам выполнения кода.
static const char *A = "some";
static const char *B = "some";
void f() {
if (A == B) {
throw "Hello, string merging!";
}
}
Может ли кто-нибудь объяснить разницу и сходство между этими результатами компиляции? Почему clang / gcc что-то оптимизирует, даже если оптимизации не требуется? Это какое-то неопределенное поведение?
Я также заметил, что если я изменю объявления на показанные ниже, clang / gcc / msvc вообще не оставит ничего "some"
в коде ассемблера. Почему поведение отличается?
static const char A[] = "some";
static const char B[] = "some";
c++
language-lawyer
string-literals
string-interning
Евгений Косов
источник
источник
Ответы:
Это не неопределенное поведение, а неопределенное поведение. Для строковых литералов ,
Это означает, что результатом
A == B
может бытьtrue
илиfalse
, от которого вы не должны зависеть.Из стандарта [lex.string] / 16 :
источник
Другие ответы объясняли, почему нельзя ожидать, что адреса указателей будут разными. Тем не менее, вы можете легко переписать это так, чтобы гарантировать это
A
иB
не сравнивать равные:static const char A[] = "same"; static const char B[] = "same";// but different void f() { if (A == B) { throw "Hello, string merging!"; } }
Разница в том, что
A
иB
теперь представляют собой массивы символов. Это означает, что они не являются указателями и их адреса должны быть разными, как и адреса двух целочисленных переменных. C ++ сбивает это с толку, потому что делает указатели и массивы взаимозаменяемыми (operator*
и,operator[]
кажется, ведут себя одинаково), но на самом деле они разные. Например, что-то вродеconst char *A = "foo"; A++;
совершенно законно, ноconst char A[] = "bar"; A++;
это не так.Один из способов подумать о разнице - это
char A[] = "..."
сказать: «Дайте мне блок памяти и заполните его символами,...
за которыми следует\0
», тогда какchar *A= "..."
говорит: «Дайте мне адрес, по которому я могу найти символы,...
за которыми следует\0
».источник
*p
иp[0]
не только « кажется, ведут себя так же» , но , по определению , являются идентичными ( при условии , чтоp+0 == p
есть отношение идентичности , потому что0
это нейтральный элемент в указатель целочисленного сложения). В конце концов,p[i]
определяется как*(p+i)
. Тем не менее, ответ является правильным.typeof(*p)
иtypeof(p[0])
оба они,char
так что действительно мало что могло быть по-другому. Я согласен с тем, что «похоже, вести себя одинаково» - не лучшая формулировка, потому что семантика очень разная. Ваш пост напомнил мне о лучшем способе доступа элементов массивов C ++:0[p]
,1[p]
, и2[p]
т.д. Это, как это делают профессионалы, по крайней мере , когда они хотят , чтобы запутать человек , которые родились после того, как на языке программирования Си.Решает ли компилятор использовать одно и то же расположение строки для реализации
A
иB
зависит от реализации. Формально можно сказать, что поведение вашего кода не определено .Оба варианта правильно реализуют стандарт C ++.
источник
Это оптимизация для экономии места, часто называемая «объединением строк». Вот документы для MSVC:
https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx
Поэтому, если вы добавите / GF в командную строку, вы должны увидеть такое же поведение с MSVC.
Кстати, вам, вероятно, не следует сравнивать строки с помощью таких указателей, любой достойный инструмент статического анализа пометит этот код как дефектный. Вам нужно сравнивать то, на что они указывают, а не фактические значения указателей.
источник