#c #string #pointers #constants
#c #строка #указатели #константы
Вопрос:
Например, почему бы и нет:
char *s= "example";
вместо:
const char *s= "example";
Я понимаю, что const делает его неизменяемым, но почему я получаю сообщение об ошибке при компиляции первого?
Кроме того, как эта концепция применима к
int * x;
против
const int *x;
Я вижу, что второе используется намного чаще, рекомендуется ли использовать «cons int *»?
Комментарии:
1. Потому что вы не можете изменять строковые литералы.
2. @B.Li строковые литералы обычно находятся в зоне памяти, которая отображается прямо с диска в память в режиме только для чтения (в системах Unix это раздел rodata исполняемого файла). Если вы попытаетесь их изменить, вы получите ошибку segfault — и это правильно, поскольку строковые литералы обычно являются общими для всего исполняемого файла, поэтому случайное изменение одного литерала приведет к разрыву всех его экземпляров по всей программе.
3. Зависит от компилятора и используемых параметров. Я только что проверил, и Visual Studio 2015 с радостью принимает любой синтаксис даже на самом высоком уровне предупреждения. Я видел код, который на самом деле изменял строковые литералы (не намеренно — очевидно, ошибка), поэтому цель пометки этого синтаксиса — заставить вас использовать
const
для предотвращения таких ошибок.4. Если вы получаете сообщение об
char *s= "example";
ошибке, вы, вероятно, компилируете свой код как C .5. Просто: вы не передаете
const
указатель какdest
. Вы можете передать aconst
какsrc
, но нетdest
.
Ответ №1:
Нет никаких требований к использованию const
, но это хорошая идея.
В C строковый литерал является выражением типа char[N]
, где N
длина строки плюс 1 (для завершающего ''
нулевого символа). Но попытка изменить массив, соответствующий строковому литералу, имеет неопределенное поведение. Многие компиляторы организуют хранение этого массива в памяти, доступной только для чтения (не в физическом ПЗУ, а в памяти, которая помечена операционной системой как доступная только для чтения). (Выражение массива в большинстве контекстов преобразуется в выражение указателя, ссылающееся на начальный элемент объекта массива.)
Было бы разумнее создавать строковые литералы const
, но const
ключевое слово не существовало в старых версиях C, и это нарушило бы существующий код. (C действительно создавал строковые литералы const
).
Это:
char *s= "example"; /* not recommended */
на самом деле это вполне допустимо в C, но это потенциально опасно. Если после этого объявления вы делаете:
s[0] = 'E';
затем вы пытаетесь изменить строковый литерал, и поведение не определено.
Это:
const char *s= "example"; /* recommended */
также допустимо; char*
значение, полученное в результате вычисления строкового литерала, безопасно и незаметно преобразуется в const char*
. И это, как правило, лучше, чем первая версия, потому что позволяет компилятору предупреждать вас, если вы пытаетесь изменить строковый литерал (лучше перехватывать ошибки во время компиляции, чем во время выполнения).
Если вы получаете ошибку в своем первом примере, то, скорее всего, вы непреднамеренно компилируете свой код как C , а не как C — или используете -Wwrite-strings
опцию gcc или что-то подобное. ( -Wwrite-strings
создает строковые литералы const
; это может повысить безопасность, но также может привести к тому, что gcc отклонит или, по крайней мере, предупредит о допустимом коде C.)
Комментарии:
1. Возможно, стоит отметить, что при условии, что
char s1[] = "example"; char s2[] = "example";
каждая переменная получит свой собственный массив, содержащий строку «example», и две копии могут быть изменены независимо, но приchar *p1 = "example"; char *p2 = "example";
условии, что оба указателя могут идентифицировать один и тот же массив; даже если память не защищена от записи, изменение цели одного указателя может также изменитьцель другой (или любой другой строки, которая заканчивается некоторыми или всеми символами в «example»).2. Учитывая контекст некоторой строки. h библиотечные функции, например, char * strcat(char * dest, const char * src), функция, которая объединяет строки, не будут ли возвращаемое значение и первый параметр «небезопасными»?
3. @B.Li : Значение, возвращаемое с помощью
strcat
, — это просто значениеdest
аргумента. Это может быть небезопасно, если любой из аргументов является нулевым или иным недопустимым указателем, или если в целевом массиве нет места. Проблемы с постоянной корректностью нет, если это то, о чем вы думаете.4. Это именно то, о чем я думал. Спасибо за помощь!
5. @B.Li : Однако существуют некоторые стандартные строковые функции C, в том числе
strchr
, которые не являются константно-безопасными. Вы можете передать его aconst char*
и получить обратноchar*
указание на ту же строку. (C , который включает в себя большую часть стандартной библиотеки C, исправляет это, предоставляя две перегруженные версии, однуconst
и одну не-const
.)
Ответ №2:
С Visual Studio 2015 на уровне предупреждения 4 это компилируется и выполняется независимо от того, скомпилировано ли оно как C или C :
#include <stdio.h>
char *s1= "examplen";
const char *s2= "examplen";
int main(int argc, char **argv)
{
printf(s1); // prints "example"
s1[2] = 'x';
printf(s1); // prints "exxmple"
printf(s2);
return 0;
}
Если я добавлю эту строку, она не сможет скомпилироваться как C или C с каждым известным мне компилятором:
s2[2] = 'x'; // produces compile error
Это ошибка const
, которую ключевое слово предназначено для предотвращения. Это просто говорит компилятору не разрешать присваивания объекту, на который указано.
Не имеет значения, указывает ли ваш указатель на char
или int
или что-нибудь еще. const
Ключевое слово оказывает одинаковое влияние на все указатели, и это делает невозможным (ну, очень сложным) присвоение объявленной вещи const
.
Ответ №3:
Строковый литерал, используемый в качестве значения, компилируется в массив char
, который не должен быть изменен. Попытка изменить его вызывает неопределенное поведение. По историческим причинам обратной совместимости его тип — char []
хотя на самом деле должен быть const char []
. Вы можете включить дополнительные предупреждения компилятора, чтобы изменить это, и дать указание компилятору считать такие строки равными const
.