Указатель на строку const в C
char *p = "string"; //creates pointer to constant string
char p[] = "string"; //just an array with "string"
Я немного смущен тем, почему в первом примере создается указатель на постоянную строку? Разве это не просто указатель на место в памяти с "строкой"?
Ответы
Ответ 1
Это, к сожалению, законно в C (и в С++ 03, для совместимости). Но любая попытка изменить строковый литерал с помощью указателя приведет к поведению Undefined. Поэтому лучше всегда присваивать строковый литерал const char*
const char * cp = "Hello"; //OK
char* p = "Hello"; //OK (unfortunately)
cp[0] = 'Y'; //Compile-time error, good
p[0] = 'Y'; //no compiler error, undefined behavior
Ответ 2
В первом случае "string"
может быть сохранен в области только для чтения процесса, поэтому попытка изменить память, на которую указывает p
, приведет к поведению undefined.
Во втором случае фактическая память выделяется и инициализируется в стеке во время выполнения (или в соответствующем разделе процесса при запуске программы, если это не локальная переменная), поэтому изменение памяти в порядке.
Первый случай можно проиллюстрировать следующим образом:
+---+ +---+---+---+---+---+---+---+
| | ----> | s | t | r | i | n | g | \0|
+---+ +---+---+---+---+---+---+---+
p
В то время как второй случай:
+---+---+---+---+---+---+---+
| s | t | r | i | n | g | \0|
+---+---+---+---+---+---+---+
p
Два объявления имеют существенную разницу, хотя вы, возможно, слышали, что они одинаковы из низкокачественных книг программирования C.
Первый - это указатель типа char *
, а второй - массив типа char []
. Идентификаторы массива распадаются на указатель в некоторых контекстах, но это не делает их указателями.
Указатель - это всего лишь адрес, а массив - "все" - в первом случае sizeof(p)
дает размер указателя (обычно 4
или 8
в зависимости от целевой машины) и в во втором случае он дает 7 * sizeof(char)
, длину фактической строки.
Ответ 3
Первый создает указатель и устанавливает его на адрес постоянной строки (предположительно в области, на которой нет защиты от записи на страницах). Запись на этот указатель является незаконной (и, вероятно, сбой).
Второй создает массив и копирует в него символы. Запись в этот массив будет записываться в определенное место в вашем стеке и вполне законна.
Ответ 4
В первом случае "строка" может быть сохранена в области только для чтения процесса, поэтому попытка изменить память, на которую указывает p, вызовет поведение undefined.
Это можно доказать, повторяя указанную выше строку кодов повторно.
char *p="string";
вы заметите, что содержимое p (то есть адрес "string" ) остается постоянным.
char p[] = "string";
для этой памяти выделяется каждый раз при запуске содержимого изменений p.