Гарантируется ли одинаковое хранилище для одинаковых строковых литералов содержимого? - PullRequest
0 голосов
/ 20 сентября 2018

Безопасен ли приведенный ниже код?Может быть заманчиво написать код, похожий на этот:

#include <map>

const std::map<const char*, int> m = {
    {"text1", 1},
    {"text2", 2}
};

int main () {
    volatile const auto a = m.at("text1");
    return 0;
}

Карта предназначена для использования только со строковыми литералами.

Я думаю, что это совершенно законно и, похоже, работаетЯ никогда не видел гарантии того, что указатель на литерал, используемый в двух разных местах, будет одинаковым.Мне не удалось заставить компилятор генерировать два отдельных указателя для литералов с одним и тем же содержимым, поэтому я начал задумываться о том, насколько устойчиво это предположение.

Меня интересует только, могут ли литералы с одинаковым содержимым иметь разные указатели,Или более формально, может ли приведенный выше код, кроме?

Я знаю, что есть способ написать код, чтобы убедиться, что он работает, и я думаю, что вышеупомянутый подход опасен, потому что компилятор может решить назначить два разных хранилища для литералаособенно если они размещены в разных единицах перевода.Я прав?

Ответы [ 4 ]

0 голосов
/ 20 сентября 2018

Стандарт не гарантирует, что адреса строковых литералов с одинаковым содержимым будут одинаковыми.Фактически, [lex.string] / 16 говорит:

Все ли строковые литералы различны (то есть хранятся ли в неперекрывающихся объектах) и являются ли последовательные вычисления строковый литерал выдают тот же или другой объект не указан.

Во второй части даже сказано, что вы не можете получить тот же адрес, когда функция, содержащая строковый литерал, называется второйвремя!Хотя я никогда не видел, чтобы компилятор делал это.

Так что использование одного и того же объекта массива символов при повторении строкового литерала является дополнительной оптимизацией компилятора.Установив флаги g ++ и флаги компилятора по умолчанию, я также обнаружил, что получаю один и тот же адрес для двух одинаковых строковых литералов в одном и том же модуле перевода.Но, как вы уже догадались, я получаю разные, если одно и то же содержимое строкового литерала появляется в разных единицах перевода.


Связанный интересный момент: для разных строковых литералов также разрешено использовать перекрывающиеся массивы.То есть, учитывая, что

const char* abcdef = "abcdef";
const char* def = "def";
const char* def0gh = "def\0gh";

возможно, вы обнаружите, что abcdef+3, def и def0gh - это все один и тот же указатель.

Кроме того, это правило о повторном использовании или перекрытииСтроковые литеральные объекты применяются только к безымянному объекту массива, непосредственно связанному с литералом, который используется, если литерал немедленно затухает в указателе или связан со ссылкой на массив.Литерал также может использоваться для инициализации именованного массива, как в

const char a1[] = "XYZ";
const char a2[] = "XYZ";
const char a3[] = "Z";

Здесь объекты массива a1, a2 и a3 инициализируются с использованием литерала, но считаются отличными отфактическое литеральное хранилище (если такое хранилище вообще существует) и следуйте обычным правилам объекта, поэтому хранилище для этих массивов не будет перекрываться.

0 голосов
/ 20 сентября 2018

Нет, стандарт C ++ не дает таких гарантий.

Тем не менее, если код находится в одной и той же единице перевода, то будет трудно найти контрпример.Если main() находится в другом переводе, то может быть проще создать контрпример.

Если карта находится в другой динамически связанной библиотеке или совместно используемом объекте, то это почти наверняка не так.

Классификатор volatile - это красная сельдь.

0 голосов
/ 20 сентября 2018

Являются ли два строковых литерала с одинаковым содержимым одним и тем же объектом, не определено, и, на мой взгляд, лучше не полагаться.Чтобы процитировать стандарт:

[lex.string]

16 Оценка строкового литерала приводит к строковому литеральному объекту сстатическая продолжительность хранения, инициализируется из заданных символов, как указано выше.Все ли строковые литералы различны (то есть хранятся ли в неперекрывающихся объектах) и дают ли последовательные вычисления строкового литерала одинаковые или другой объект не указан.

Если вы хотите избежатьПомимо std::string, вы можете написать простой тип представления (или использовать std::string_view в C ++ 17), который является ссылочным типом над строковым литералом.Используйте его для интеллектуального сравнения, а не полагайтесь на буквальную идентичность.

0 голосов
/ 20 сентября 2018

Стандарт C ++ не требует реализации для дедупликации строковых литералов.

Когда строковый литерал находится в другом модуле перевода или в другой совместно используемой библиотеке, для которой требуется, чтобы компоновщик (ld) или runtime-linker (ld.so) выполняли дедупликацию строкового литерала.Что они не делают.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...