Что должно произойти, когда мы пытаемся изменить строковую константу?

#include<stdio.h>
#include<string.h>

int main()
{
    int i, n;
    char *x="Alice"; // ....... 1
    n = strlen(x);   // ....... 2
    *x = x[n];       // ....... 3
    for(i=0; i<=n; i++)
    {
        printf("%s ", x);
        x++;
    }
    printf("\n");
    return 0;
}

Строковая константа не может быть изменена. В приведенном выше коде * х означает "А". В строке 3 мы пытаемся изменить строковую константу. Правильно ли написать это заявление? Когда я запускаю этот код в Linux, у меня возникает ошибка сегментации. Но на www.indiabix.com они дали ответ:

Если вы скомпилируете и запустите эту программу на платформе Windows с Turbo C, это приведет к появлению вшей, что может привести к разным результатам на других платформах (зависит от компилятора и машины). Онлайновый компилятор C, представленный на этом сайте, выдаст Алисе вшей лед в качестве вывода (он работает на платформе Linux).

4 ответа

Решение

Ваш анализ верен. Линия

*x = x[n];

пытается изменить строковый литерал, так что это неопределенное поведение.


Кстати, я проверил сайт, на который вы ссылались. Просто просматривая его в течение двух минут, я уже нашел несколько неправильных примеров кода (чтобы назвать несколько, используя gets, с помощью char(не int) присвоить возвращаемое значение getcharи т. д.), поэтому мое предложение не использовать его.

Ваш анализ верен, но не противоречит тому, что вы цитировали.

Код не работает. Ответ уже признает, что он может вести себя по-разному в разных реализациях, и дал два разных вывода в двух разных реализациях. Вы нашли реализацию, которая ведет себя третьим образом. Это прекрасно.

Модификация строкового литерала - неопределенное поведение. Таким образом, поведение, которое вы наблюдаете, и два описанных выше, соответствуют требованиям стандарта C (как, например, отправка электронного письма вашему боссу и вашему супругу или создание демонов из носа). Эти три на самом деле вполне разумные действия (изменить "константу", проигнорировать запись или сообщить об ошибке).

С GCC вы можете попросить, чтобы вас предупреждали, когда вы присваиваете адрес строкового литерала указателю на (доступный для записи) символ:

cc -g -Wall -Wextra -Wwrite-strings   -c -o 27211884.o 27211884.c
27211884.c: In function ‘main’:
27211884.c:7:13: warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]
     char *x="Alice"; // ....... 1
             ^

Это предупреждение включено по умолчанию при компиляции C++, но не для C, потому что char* часто используется для строковых литералов в старых кодовых базах. Я рекомендую использовать его при написании нового кода.

Существует два правильных способа написания кода примера, в зависимости от того, хотите ли вы, чтобы ваша строка была на самом деле постоянной или нет:

const char *x = "Alice";
char x[] = "Alice";

В этом коде память для "Alice" будет находиться в разделе данных только для чтения исполняемого файла, а x - указатель, указывающий на это место только для чтения. Когда мы пытаемся изменить раздел данных только для чтения, он не должен позволять. Но char *x="Alice"; сообщает компилятору, что x объявлен как указатель на character.ie, x указывает на символ, который можно изменить (не только для чтения). Таким образом, компилятор будет думать, что он может быть изменен. Таким образом линия *x = x[n]; будет вести себя по-разному на разных компиляторах. Так что это будет неопределенное поведение.
Правильный способ объявления для назначения строкового литерала, как показано ниже

 const char *x ="Alice";

Тогда только поведение компилятора может быть предсказано.

Другие вопросы по тегам