Понимание C-строк и строковых литералов в C ++ - PullRequest
3 голосов
/ 06 октября 2011

У меня есть несколько вопросов, которые я хотел бы задать о строковых литералах и 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 []?Если у меня возникла неправильная идея или я совершенно не уверен, поправьте меня.

Ответы [ 4 ]

4 голосов
/ 06 октября 2011

Бит, который вам не хватает, это немного волшебства компилятора, где это:

char cstr[] = "c-string"; 

На самом деле выполняется так:

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

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

2 голосов
/ 06 октября 2011

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

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

1 голос
/ 06 октября 2011

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

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

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

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

1 голос
/ 06 октября 2011
char cstr[] = "c-string";

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

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

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

Смежный вопрос здесь .

...