Я вижу два литерала '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";
@ luk32 Я обсуждаю флаги компилятора, которые влияют на это здесь
Для MSVC параметр компилятора / GF управляет этим поведением. См. docs.microsoft.com/en-us/cpp/build/reference/…
К вашему сведению, это может случиться и с функциями.
Также работает для "некоторой строки" и "строки" на некоторых компиляторах.
Интересно, что поведение в GCC при нескольких разных оптимизациях выглядит одинаково!
Возможный дубликат Гарантировано ли одинаковое хранение одних и тех же строковых литералов содержимого?





Это не неопределенное поведение, а неопределенное поведение. Для строковые литералы,
The compiler is allowed, but not required, to combine storage for equal or overlapping string literals. That means that identical string literals may or may not compare equal when compared by pointer.
Это означает, что результатом A == B может быть true или false, на которые вам не следует полагаться.
Из стандарта [lex.string] / 16:
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.
Решает ли компилятор использовать одно и то же расположение строки для A и B, зависит от реализации. Формально можно сказать, что поведение вашего кода - неопределенные.
Оба варианта правильно реализуют стандарт C++.
Поведение кода - либо генерировать исключение, либо ничего не делать, выбрано до первого выполнения кода неопределенным образом. Это не означает, что поведение в целом не определено - просто компилятор может выбрать любое поведение любым способом, который он сочтет подходящим, до первого наблюдения за поведением.
Другие ответы объясняли, почему нельзя ожидать, что адреса указателей будут разными. Тем не менее, вы можете легко переписать это так, чтобы гарантировать, что 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] и т. д. Так делают профессионалы, по крайней мере, когда они хотят запутать людей, родившихся после языка программирования C.
Это интересно, и у меня возникло желание добавить ссылку на FAQ по C, но я понял, что есть много связанных вопросов, но ни один, похоже, не подходит к сути этого вопроса здесь.
Это оптимизация для экономии места, часто называемая «объединением строк». Вот документы для MSVC:
https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx
Поэтому, если вы добавите / GF в командную строку, вы должны увидеть такое же поведение с MSVC.
Кстати, вам, вероятно, не следует сравнивать строки с помощью таких указателей, любой достойный инструмент статического анализа пометит этот код как дефектный. Вам нужно сравнивать то, на что они указывают, а не фактические значения указателя.