Назначение строкового литерала std:: string
Я знаю, что следующий код создаст массив символов и останется в памяти до окончания программы:
char* str = "this is a string";
Что касается этого оператора, создает локальный массив символов и будет освобожден, когда str выходит из области:
char str[] = "this is a string";
что мне интересно, что происходит, когда я пишу так:
std::string str = "this is a string";
str должен сделать копию строки в своей собственной памяти (локальной), но как насчет самого строкового литерала? Будет ли у него срок службы программы или она будет освобождена, когда str выйдет за рамки?
4 ответов
когда вы пишете это
std::string str = "this is a string";
C++ должен найти конструктор std::string
что происходит const char*
, вызывает его, чтобы сделать временный объект, вызывает конструктор копирования, чтобы скопировать это временное в str
, а затем уничтожает временный объект.
однако существует оптимизация, которая позволяет компилятору C++ пропустить построение и уничтожение временного объекта, поэтому результат такой же, как
std::string str("this is a string");
а как насчет строки буквальный себя? Будет ли у него время жизни программы или он будет освобожден, когда str выйдет из сферы действия?
сам строковый литерал при использовании таким образом недоступен для вашей программы. Как правило, C++ помещает его в тот же сегмент, что и другие строковые литералы, использует его для передачи конструктору std::string
, и забывает об этом. Оптимизатору разрешено устранять дубликаты среди всех строковых литералов, в том числе используемых только при инициализации других объекты.
большая часть ОС разделит вашу память программы на несколько частей
- Стек
- Кучу
- сегмент данных
- сегмент BSS
- сегмент кода
вы уже знаете о стеке и куче, но что насчет остальных?
сегмент кода сохраняет все операции в двоичной форме.
теперь становится интересно: Давайте см. следующий код:
int x;
class Y{ static int y; };
int main(){
printf("hello world");
return 0;
}
где программа выделяет x
и y
? они не являются локальными или динамически распределенными, так где же?
сегмент данных сохраняет все статические и глобальные переменные, когда программа загружается, этот сегмент сохраняет достаточно байтов для хранения всех статических и глобальных переменных. если переменная является объектом, то при запуске программы она выделяет достаточно байтов для всех переменных, включая объекты. до main
программа вызывает каждый глобальный конструктор объектов и после main
завершает вызов каждого деструктора объекта в обратном порядке, который он назвал конструктором.
сегмент BSS является подмножеством сегмента данных, который сохраняет глобальные и статические указатели, которые являются null-intitalized.
поэтому, предполагая, что строковый литерал не был оптимизирован, программа сохраняет его в сегменте данных. Она будет жить, пока жива программа. более того, если это строковый литерал, скорее всего, вы даже можете увидеть его внутри exe! откройте exe в виде текстового файла. в какой-то момент на этом пути вы ясно увидите струну.
теперь насчет
std::string str = "hello world";
?
это фанк ситуация. str
сам живет на стеке. Фактический внутренний буфер живет в куче, но строковый литерал, который использовал для назначения строки, живет в сегменте данных и коде, который делает str
значение превращаются в hello world
живет в сегменте кода. излишне говорить, что если мы будем программировать в сборке, нам нужно будет построить эту экосистему голыми руками.
некоторые из ваших первоначальных утверждений не совсем верны:
на char *
и char []
например, в обоих случаях сама переменная, str
остается в области и доступен до конца программы, если он объявлен в глобальном пространстве имен.
если он объявлен в функции или области метода, он доступен, пока область остается активной. Оба.
что касается того, что на самом деле происходит с памятью, которая используется для хранения фактического буквальные строки, это неопределенно. Конкретная реализация C++ может свободно управлять памятью времени выполнения любым удобным для нее способом, если результаты соответствуют стандарту C++. Что касается C++, вы не получаете доступ к памяти, используемой str
объект, вы ссылаетесь только на str
сам объект.
конечно, вы можете взять родного char *
указатель, указывающий на один из символов в str
. Но будет ли допустимый указатель привязан непосредственно к области базового объекта. Когда соответствующий str
объект выходит за рамки, указатель больше не действителен, и доступ к содержимому указателя становится неопределенным поведением.
обратите внимание, что в случае, когда str
находится в глобальном пространстве имен, рамки str
- это время жизни программы, поэтому вопрос спорный. Но когда!--2--> в локальной области, и он выходит из области видимости, с помощью указателя становится неопределенным поведение. Что происходит с основной памятью, не имеет значения. Стандарт C++ на самом деле не определяет, что должно или не должно происходить с памятью в базовой реализации, но что является или не является неопределенным поведением.
основываясь на этом, вы можете в значительной степени выяснить ответ для std::string
дело сам. Это одно и то же. Вы получаете доступ к std::string
объект, а не базовая память, и применяется тот же принцип.
но обратите внимание, что в в дополнение к проблеме определения области, некоторые, но не все, методы std::string
объект также указан как недействительный для всех существующих прямых указателей и итераторов его содержимого, поэтому это также влияет на то, является ли direct char *
к одному из символов в std::string
остается в силе.
я предложу встречный вопрос: почему вы заботитесь?
стандарт C++ определяет поведение языка, и первый основной принцип, когда дело доходит до реализаций, в основном известен как правило as-if:
§ 1.9 выполнение программы
1/ семантические описания в этом международном стандарте определяют параметризованную недетерминированную абстрактную машину. Этот Международный стандарт не предъявляет никаких требований к структуре соответствующих реализаций. В частности, им не нужно копировать или эмулировать структуру абстрактной машины. Скорее, соответствующие реализации необходимы для эмуляции (только) наблюдаемого поведения абстрактной машины, как описано ниже.
в вашем случае:
std::string str = "this is a string";
существуют различные допустимые сценарии:
- вы не используете
str
потом? затем вся эта часть кода может быть полностью удалена - вы сразу назначить
'T'
tostr[0]
потом? тогда они могли бы объединиться вstd::string str = "This is a string";
- ...
и нет никакой гарантии, что код компилятор будет делать. Это может зависеть от используемого компилятора, используемой стандартной реализации библиотеки, архитектуры/ОС, для которой вы компилируете, и даже аргументов, переданных компилятору...
таким образом, если вы хотите знать в вашем случае, вам придется проверить созданный машинный код. Прошу coliru на следующий код:
#include <string>
int main(int argc, char** argv) {
std::string str = "this is a string";
}
Clang производит следующее В ИК:
@.str = private unnamed_addr constant [17 x i8] c"this is a string", align 1
, который, в свою очередь, дает следующую сборку:
.L.str: .asciz "this is a string" .size .L.str, 17
Итак, у вас есть это, для этих конкретных условий,"this is a string"
будет как есть в двоичном файле и будет загружен в память только для чтения. Оно будет оставайтесь в адресном пространстве процесса до конца, ОС может вывести его на страницу или нет в зависимости от давления ОЗУ.