2013-10-15 2 views
2

Я знаю, что const char * является указателем на const char, а char *const является постоянным указателем на символ. Я проверяю это в следующем коде:Изменение строки char * const

const char *s = "hello"; // Not permitted to modify the string "hello" 
char *const t = "world"; // Not permitted to modify the pointer t 

s = "hello2"; // Valid 
// t = "world2"; // Invalid, gives compilation error 

// *(s + 1) = 'a'; // Invalid, gives compilation error 
*(t + 1) = 'a';  // Why does this not work?  

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

ответ

7

t указывает на строкового литерала это неопределенное поведение, чтобы изменить строка буквального. C++ проект стандарта разделе 2.14.5Строковые литералы пункт говорит (курсив мой ):

ли все строковые литералы различны (то есть, хранятся в объектах непересекающихся) определяется реализацией. Эффект попытки изменить строковый литерал не определен.

Соответствующий раздел из проекта стандарта C99 является 6.4.5Строковые литералы пункт , который говорит (курсив мой):

Не определено ли эти массивы различны при условии их элементы имеют значения . Если программа пытается изменить такой массив, поведение равно undefined.

На типичной современной платформе Unix вы найдете строковых литералов в сегменте только для чтения, которое привело бы к нарушению доступа, если мы пытаемся изменить его. Мы можем использовать objdump осмотреть раздел только для чтения следующим образом:

objdump -s -j .rodata 

мы можем увидеть в следующем live example, что строковый литерал действительно будет найден в только для чтения раздела. Обратите внимание, что мне пришлось добавить printf, иначе компилятор оптимизировал бы строковый литерал.Образец `objdump выход:

Contents of section .rodata: 
400668 01000200 776f726c 64002573 0a00  ....world.%s.. 

Альтернативный подход будет иметь t точку на массив с копией строка буквального как так:

char r[] = "world";  
char *const t = r ; 
+1

Вы указываете стандарт, но на самом деле вы не говорите, почему (как) программа заканчивается ошибкой ... Просто говоря ... Elchonon Edelson дает фактическую причину, которая, по моему мнению, является правильным ответом. –

+0

@AlexisWilke. Стандарт предназначен для независимой от платформы, и такие детали зависят от платформы, поэтому стандарт использует такой язык, как неопределенное поведение, для охвата целого ряда моделей поведения, которые включают в себя работу просто отлично, но на нее нельзя положиться. Я добавил более подробную информацию о типичной современной платформе unix. –

3

Хотя строковые литералы в C официально имеют тип char[] (массив char, а не const), стандарт С конкретно говорится, что они должны рассматриваться как немодифицируемые. Компиляторы обычно ставят строковые литералы в сегменте только для чтения, поэтому попытка их изменения приводит к нарушению доступа.

Строковые литералы описаны в разделе 6.4.5 стандарта C11 (ISO/IEC 9899: 2011).

1

Вы можете обойти компилятор error, переработав его как char*, как и в *((char*)s + 1) = 'a';, но поскольку он уже был задан в других ответах, это неопределенное поведение и, вероятно, приведет к ошибке сегментации, потому что вы редактируете строковый литерал.

1

Если вы хотите проверить его правильно, инициализируйте строки в функции, чтобы инициализация могла быть динамической, и для этого используйте strdup().

int 
main(int argc, char **argv) 
{ 
    char *d1 = strdup("hello"); 
    char *d2 = strdup("world"); 

    const char *s = d1; 
    char *const t = d2; 

    ... 

    free(d1); 
    free(d2); 
} 

Д1 и Д2 переменные в основном используются так, что динамические распределения могут быть должным образом освобождается с помощью free() в конце. Кроме того, как указывают другие ответы, всегда обрабатывайте строковые литералы как const char *.

Смежные вопросы