Почему можно назначить const char * для char *?
Я знаю, что, например, "hello"
имеет тип const char*
. Поэтому мои вопросы:
-
Как мы можем назначить литеральную строку типа "hello"
для не const char*
следующим образом:
char* s = "hello"; // "hello" is type of const char* and s is char*
// and we know that conversion from const char* to
// char* is invalid
-
Литеральная строка, такая как "hello"
, которая будет иметь память во всей моей программе, или это как временная переменная, которая будет уничтожена при завершении оператора?
Ответы
Ответ 1
Фактически, "hello"
имеет тип char const[6]
.
Но суть вопроса по-прежнему правильная: почему С++ позволяет нам назначить ячейку памяти только для чтения типам < const
?
Единственная причина для этого - обратная совместимость со старым кодом C, который не знал const
. Если бы С++ был строгим здесь, это нарушило бы много существующего кода.
Тем не менее, большинство компиляторов могут быть настроены так, чтобы предупреждать о таком коде как устаревшем или даже делать это по умолчанию. Кроме того, С++ 11 вообще запрещает это, но компиляторы еще не могут его выполнить.
Для поклонников Standerdese:
[Ref 1] С++ 03 Стандарт: §4.2/2
Строковый литерал (2.13.4), который не является широким строковым литералом, может быть преобразован в rvalue типа "указатель на char"; широкий строковый литерал может быть преобразован в rvalue типа "указатель на wchar_t". В любом случае результатом является указатель на первый элемент массива. Это преобразование рассматривается только тогда, когда существует явный соответствующий целевой тип указателя, а не когда требуется общая конвертация из lvalue в rvalue. [ Примечание. Это преобразование устарело. См. Приложение D.] Для целей ранжирования в разрешении перегрузки (13.3.3.1.1) это преобразование считается преобразованием от массива к указателю с последующим квалификационным преобразованием (4.4). [Пример: "abc" преобразуется в "указатель на const char" как преобразование от массива к указателю, а затем к "указателю на char" в качестве преобразования квалификации. ]
С++ 11 просто удаляет приведенную выше цитату, которая подразумевает, что это незаконный код в С++ 11.
[Ref 2] C99 standard 6.4.5/5 "Строковые литералы - семантика":
В фазу 7 перевода байта или код нулевого значения добавляется к каждой многобайтовой последовательности символов, которая получается из строкового литерала или литералов. Последовательность многобайтовых символов затем используется для инициализации массива статической продолжительности хранения и длины, достаточной для того, чтобы содержать последовательность. Для символьных строковых литералов элементы массива имеют тип char и инициализируются отдельными байтами многобайтовой последовательности символов; для широких строковых литералов элементы массива имеют тип wchar_t и инициализируются с помощью последовательности широких символов...
Не указано, являются ли эти массивы различными, если их элементы имеют соответствующие значения. Если программа пытается изменить такой массив, поведение undefined.
Ответ 2
- это буквальная строка, подобная "hello", и память во всей моей программе будет полностью похожа на временную переменную, которая будет уничтожена при завершении оператора.
Он хранится в программных данных, поэтому он доступен в течение всей жизни программы. Вы можете вернуть указатели и ссылки на эти данные из текущей области.
Единственная причина, по которой const char*
отбрасывается на char*
, является comatiblity с c, как системные вызовы winapi. И этот актер сделан неявным, в отличие от любого другого каста Const.
Ответ 3
Просто используйте string
:
std::string s("hello");
Это будет С++. Если вы действительно должны использовать char
, вам нужно создать массив и скопировать содержимое поверх него.
Ответ 4
Ответ на ваш второй вопрос заключается в том, что переменная s
хранится в ОЗУ как тип pointer-to-char. Если он глобальный или статический, он выделяется в куче и остается там на всю жизнь запущенной программы. Если это локальная ( "авто" ) переменная, она выделяется в стеке и остается там до тех пор, пока не вернется текущая функция. В любом случае он занимает объем памяти, необходимый для хранения указателя.
Строка "Hello"
является константой и хранится как часть самой программы вместе со всеми другими константами и инициализаторами. Если вы построили свою программу для работы на устройстве, строка будет сохранена в ПЗУ.
Обратите внимание, что поскольку строка является постоянной и s
является указателем, копирование не требуется. Указатель s
просто указывает на то, где хранится строка.
Ответ 5
В вашем примере вы не назначаете, а строите. std::string, например, имеет конструктор std::string(const char *)
(на самом деле он более сложный, но это не имеет значения). И, аналогично, char * (если это был тип, а не указатель на тип), может иметь конструктор const char *, который копирует память.
Я действительно не знаю, как компилятор действительно работает здесь, но я думаю, что он может быть похож на то, что я описал выше: копия "Hello"
построена в стеке, а s
инициализируется этой копией адрес.