Указатель на строку 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.