Назначение строкового литерала 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' to str[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" будет как есть в двоичном файле и будет загружен в память только для чтения. Оно будет оставайтесь в адресном пространстве процесса до конца, ОС может вывести его на страницу или нет в зависимости от давления ОЗУ.