Разница между char * str = "STRING" и char str [] = "STRING"?
При кодировании простой функции для удаления определенного символа из строки я попал в эту странную проблему:
void str_remove_chars( char *str, char to_remove)
{
if(str && to_remove)
{
char *ptr = str;
char *cur = str;
while(*ptr != '\0')
{
if(*ptr != to_remove)
{
if(ptr != cur)
{
cur[0] = ptr[0];
}
cur++;
}
ptr++;
}
cur[0] = '\0';
}
}
int main()
{
setbuf(stdout, NULL);
{
char test[] = "string test"; // stack allocation?
printf("Test: %s\n", test);
str_remove_chars(test, ' '); // works
printf("After: %s\n",test);
}
{
char *test = "string test"; // non-writable?
printf("Test: %s\n", test);
str_remove_chars(test, ' '); // crash!!
printf("After: %s\n",test);
}
return 0;
}
Я не понимаю, почему второй тест терпит неудачу?
Для меня это похоже на то, что первое обозначение char *ptr = "string";
эквивалентно этому: char ptr[] = "string";
.
Разве это не так?
Ответы
Ответ 1
Два объявления не совпадают.
char ptr[] = "string";
объявляет массив char размера 7
и инициализирует его символами s
, t
, r
, i
, n
, g
и \0
. Вы можете разрешить изменять содержимое этого массива.
char *ptr = "string";
объявляет ptr
как указатель char и инициализирует его с адресом строкового литерала "string"
, который доступен только для чтения. Изменение строкового литерала - это поведение undefined. То, что вы видели (seg fault), является одним из проявлений поведения undefined.
Ответ 2
Строго говоря, объявление char *ptr
только гарантирует вам указатель на тип символа. Необязательно, чтобы строка составляла часть сегмента кода скомпилированного приложения, которая была бы установлена только для чтения некоторыми операционными системами. Проблема заключается в том, что вы делаете предположение о характере предопределенной строки (которую можно записать), когда на самом деле вы никогда явно не создали память для этой строки самостоятельно. Возможно, некоторые реализации компилятора и операционной системы позволят вам выполнить то, что вы пытались сделать.
С другой стороны, декларация char test[]
, по определению, фактически распределяет читабельную и записываемую память для всего массива символов в стеке в этом случае.
Ответ 3
char *test = "string test";
неверно, оно должно быть const char*
. Этот код компилируется только из-за соображений обратной задачи. Память, обозначенная символом const char*
, является постоянной памятью, и всякий раз, когда вы пытаетесь записать ее, она вызывает поведение undefined. С другой стороны, char test[] = "string test"
создает записываемый массив символов в стеке. Это похоже на любую другую локальную переменную regualr, которую вы можете написать.
Ответ 4
Насколько я помню
char ptr[] = "string";
создает копию из "string"
в стеке, поэтому этот файл является изменяемым.
Форма
char *ptr = "string";
просто обратная совместимость для
const char *ptr = "string";
и вам не разрешено (с точки зрения поведения undefined) изменять его содержимое.
Компилятор может помещать такие строки в раздел памяти только для чтения.
Ответ 5
Хороший ответ @codaddict.
Кроме того, sizeof(ptr)
даст разные результаты для разных объявлений.
Первый, объявление массива, вернет длину массива включая завершающий нулевой символ.
Второй, char* ptr = "a long text...";
вернет длину указателя, обычно 4 или 8.
Ответ 6
char *str = strdup("test");
str[0] = 'r';
является правильным кодом и создает изменяемую строку. str назначается память в куче, значение "test" заполняется им.