Понимание C-строк и строковых литералов в C ++

У меня есть несколько вопросов о строковых литералах и C-строках.

Итак, если у меня есть что-то вроде этого:

char cstr[] = "c-string";

Насколько я понимаю, строковый литерал создается в памяти с завершающим нулевым байтом, например, начиная с адреса 0xA0 и заканчивая 0xA9, и оттуда адрес возвращается и / или приводится к типу char [], который затем указывает на адрес.

В таком случае законно выполнить это:

for (int i = 0; i < (sizeof(array)/sizeof(char)); ++i)
    cstr[i] = 97+i;

В этом смысле можно ли изменять строковые литералы, если они приведены к типу char []?

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

char * p = "const cstring";
*p = 'A'; // illegal memory write

Я предполагаю, что я пытаюсь понять, почему типам char * не разрешено указывать на строковые литералы, как это делают массивы, и изменять их константы? Почему строковые литералы не преобразуются в char *, как в char []? Если у меня здесь неправильное представление или я полностью отключен, не стесняйтесь поправлять меня.


person Bobby Barjasteh    schedule 06.10.2011    source источник
comment
char * p = "const cstring"; должен выдать ошибку компиляции, поскольку "const cstring" относится к типу const char* (в частности, чтобы вы не использовали его так, как вы используете его в своем примере)   -  person tylerl    schedule 06.10.2011
comment
@tylerl "const cstring" не относится к типу const char*.   -  person Lightness Races in Orbit    schedule 20.01.2019
comment
@LightnessRacesinOrbit в C ++ 11 строковые литералы имеют тип const char[N], что, по сути, во многом эквивалентно const char*. Можно спорить о педантичных деталях, но это только добавляет путаницы, а не ясности.   -  person tylerl    schedule 27.01.2019
comment
@tylerl Напротив, это совершенно разные типы, и притворство в противном случае - вот что вносит путаницу.   -  person Lightness Races in Orbit    schedule 27.01.2019


Ответы (4)


В первом случае вы создаете фактический массив символов, размер которого определяется размером литерала, которым вы его инициализируете (8 + 1 байт). Переменной cstr выделяется память в стеке, и содержимое строкового литерала (который в коде находится где-то еще, возможно, в части памяти, доступной только для чтения) копируется в эту переменную. .

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

Таким образом, поскольку строковый литерал может находиться в постоянной памяти, в общем случае небезопасно пытаться изменить его с помощью указателя p (вы можете поладить с ним, а можете нет). С другой стороны, вы можете делать что угодно с массивом cstr, потому что это ваша локальная копия, которая случайно была инициализирована из литерала.

(Только одно примечание: переменная cstr относится к типу array of char, и в большинстве случаев это преобразуется в указатель на первый элемент этого массива. Исключение из этого может быть, например, оператором sizeof: он вычисляет размер всего массива, а не только указателя на первый элемент.)

person Vlado Klimovský    schedule 06.10.2011
comment
Ах я вижу. Таким образом, разница в том, что один на самом деле является просто указателем на память RO, в то время как другой является переменной массива char (или константным указателем, другими словами) на локальную копию этого литерала в памяти плюс нулевой байт, и, таким образом, доступ к копии есть RW. Спасибо за разъяснение - person Bobby Barjasteh; 06.10.2011
comment
КСТАТИ, не всегда использовалась память обратного осмоса. Изменение постоянной строки было старым приемом программистов на языке C до того, как они начали помещать строки в раздел, доступный только для чтения. - person Hot Licks; 06.10.2011

То, что вам не хватает, - это небольшая магия компилятора, где это:

char cstr[] = "c-string"; 

Собственно выполняется так:

char *cstr = alloca(strlen("c-string")+1);
memcpy(cstr,"c-string",strlen("c-string")+1);

Вы не видите этого бита, но это более или менее то, во что компилируется код.

person tylerl    schedule 06.10.2011
comment
Это определенно то, чего мне не хватало! Ответ, который я выбрал в качестве выбранного ответа, в основном выразил это словами, но код еще чище;) так что на самом деле cstr - это const char * для локально выделенной памяти в стеке (или, возможно, куче), которая является копией и может быть изменена , в отличие от значений буквальной строки. Большое спасибо за то, что показал мне это. - person Bobby Barjasteh; 06.10.2011
comment
Стоит отметить, что в данном случае cstr не const char*, а скорее char*. const char* - это то, что вы получаете со строковыми литералами. Это означает, что вы не можете изменять его содержимое. Напротив, char* означает, что данные можно изменять. Кроме того, он определенно находится в стеке, а не в куче, поэтому вам не нужно вызывать free() на нем и почему вы не можете return передать его вызывающему. - person tylerl; 07.10.2011

char cstr[] = "something"; объявляет автоматический массив инициализированный байтами 's', 'o', 'm', ...

char * cstr = "something";, с другой стороны, объявляет указатель символа, инициализированный на адрес буквального "чего-то".

person Hot Licks    schedule 06.10.2011
comment
Спасибо за понимание этого. Теперь я вижу, что массивы, будучи стандартным типом, буквально инициализируются в своем конструкторе строковыми литералами, но являются только копиями самого литерала. - person Bobby Barjasteh; 06.10.2011

char cstr[] = "c-string";

Это копирует "c-строку" в массив символов в стеке. Запись в эту память разрешена.

char * p = "const cstring";
*p = 'A'; // illegal memory write

Литеральные строки, такие как «c-string» и «const cstring», находятся в сегменте данных вашего двоичного файла. Эта область предназначена только для чтения. Выше p указывает на память в этой области, и запись в это место является незаконной. Начиная с C ++ 11 это применяется более строго, чем раньше, и вы должны сделать это const char* p вместо этого.

Связанный вопрос здесь.

person m-sharp    schedule 06.10.2011