Разница между 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" заполняется им.