Что должно произойти, когда мы пытаемся изменить строковую константу?
#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";
Тогда только поведение компилятора может быть предсказано.