Разница между char * и const char *?


177

какая разница между

char* name

которая указывает на постоянный строковый литерал, и

const char* name

что вы подразумеваете под " константным строковым литералом" в C (не C ++)
gbulmer

1
... имя char * может указывать на постоянный строковый литерал
Iceman

константа в «константе строкового литерала» является избыточной, поскольку все строковые литералы в теории являются постоянными сущностями. Это содержимое переменной, которое можно сделать постоянным или изменяемым. Объявление «const» просто выдаст ошибку времени компиляции, если вы попытаетесь изменить содержимое символа, на который указывает «имя»
Cupcake

Просто: имя "char * name" - это указатель на char, то есть оба можно изменить здесь. "const char * name" name это указатель на const char, т.е. указатель может меняться, но не char.
АКД

Прочитайте эти вещи справа налево.
Цзяпэн Чжан

Ответы:


406

char*является изменяемым указатель на изменяемый символ / строка.

const char*является изменяемым указатель на непреложный символ / строка. Вы не можете изменить содержимое местоположения, на которое указывает указатель. Кроме того, компиляторы обязаны выдавать сообщения об ошибках, когда вы пытаетесь это сделать. По той же причине преобразование из const char *в char*не рекомендуется.

char* constявляется неизменным указателем (он не может указывать на любое другое местоположение), но содержимое местоположения, на которое он указывает, является изменяемым .

const char* constявляется непреложным указатель на непреложный символ / строка.


4
Путаница может быть устранена с использованием переменной после операторов, упомянутых выше, и путем ссылки на эту переменную.
ankit.karwasra

3
@ ankit.karwasra, Вы пропустили еще один:char const *
Pacerier

Я предлагаю два варианта с изменяемым символом / строкой, которые очень опасны, так как вы можете создать память ошибок сегментации, а если вы действительно умны, вы можете взломать компьютер. Вот почему я думаю, что компиляторы всегда показывали предупреждения в этих реализациях
Даниэль Н.

1
Не будет ли мутация char *давать ошибку сегментации во время работы?
Divyanshu Maithani

1
Поэтому я использую, constесли я хочу, чтобы компилятор выдавал ошибку, если я забыл и изменил данные по ошибке, верно?
Бухгалтер م

43
char *name

Вы можете изменить символ, на который nameуказывает, а также символ, на который он указывает.

const char* name

Вы можете изменить символ, на который nameуказывает, но вы не можете изменить символ, на который он указывает.
Исправление: Вы можете изменить указатель, но не символ, на который nameуказывает ( https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspx , см. «Примеры» ). В этом случае constуказатель относится к charзвездочке, а не к ней.

Согласно странице MSDN и http://en.cppreference.com/w/cpp/language/declarations , constbefore *является частью последовательности спецификатора decl, а constafter *является частью декларатора.
За последовательностью спецификатора объявления могут следовать несколько объявлений, поэтому const char * c1, c2объявляется c1как const char *и c2как const char.

РЕДАКТИРОВАТЬ:

Судя по комментариям, ваш вопрос, похоже, касается разницы между двумя объявлениями, когда указатель указывает на строковый литерал.

В этом случае вы не должны изменять символ, на который nameуказывает, так как это может привести к неопределенному поведению . Строковые литералы могут быть размещены в областях памяти, доступных только для чтения (реализация определяется), и пользовательская программа не должна изменять их в любом случае. Любая попытка сделать это приводит к неопределенному поведению.

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

Пример онлайн-примера:

#include <string.h>
int main()
{
    char *str1 = "string Literal";
    const char *str2 = "string Literal";
    char source[] = "Sample string";

    strcpy(str1,source);    //No warning or error, just Undefined Behavior
    strcpy(str2,source);    //Compiler issues a warning

    return 0;
}

Вывод:

cc1: предупреждения обрабатываются как ошибки
prog.c: в функции 'main':
prog.c: 9: error: при передаче аргумента 1 'strcpy' отбрасывает классификаторы из целевого типа указателя

Обратите внимание, что компилятор предупреждает о втором случае, но не о первом.


Спасибо .. я смешивался с константным строковым литералом, который определяется как: char * name = "String Literal"; Изменение «Строкового литерала» не определено ..
Iceman

@ user1279782: Ой, подожди! Вы говорите о пуантах, указывающих на строковые литералы здесь? В этом случае Вы не должны изменять символ, на который nameуказывает пункт в любом случае. Это может привести к UB.
Alok Save

Да, это было главное. Так что в этом случае char * name и const char * name ведут себя одинаково, верно?
Iceman

4
Этот ответ либо крайне двусмысленный, либо просто неверный. Я бы сказал: «Вы не можете изменить символ, на который указывает имя, но Вы можете изменить символ, на который он указывает». Как не будучи в состоянии изменить сам указатель, но , будучи в состоянии изменить расположение памяти, на которую он указывает, что неверно: ideone.com/6lUY9s в качестве альтернативы для чистого C: ideone.com/x3PcTP
shroudednight

1
@shroudednight: Вам нужно узнать немного больше о неопределенном поведении, и нужно различать: разрешено и не должно быть сделано. :)
Alok Save

16
char mystring[101] = "My sample string";
const char * constcharp = mystring; // (1)
char const * charconstp = mystring; // (2) the same as (1)
char * const charpconst = mystring; // (3)

constcharp++; // ok
charconstp++; // ok
charpconst++; // compile error

constcharp[3] = '\0'; // compile error
charconstp[3] = '\0'; // compile error
charpconst[3] = '\0'; // ok

// String literals
char * lcharp = "My string literal";
const char * lconstcharp = "My string literal";

lcharp[0] = 'X';      // Segmentation fault (crash) during run-time
lconstcharp[0] = 'X'; // compile error

// *not* a string literal
const char astr[101] = "My mutable string";
astr[0] = 'X';          // compile error
((char*)astr)[0] = 'X'; // ok

1
Ни один из ваших указателей не указывает на «константные строковые литералы» в соответствии с вопросом.
Кафе

Стоит отметить, что изменение char *значения приводит к ошибке сегментации, поскольку мы пытаемся изменить строковый литерал (который присутствует в постоянной памяти)
Divyanshu Maithani

10

Ни в том, ни в другом случае вы не можете изменить строковый литерал независимо от того, объявлен ли указатель на этот строковый литерал как char *или const char *.

Однако различие заключается в том, что если указатель равен, const char *то компилятор должен выдать диагностику, если вы пытаетесь изменить значение, на которое указывает указатель, но если указатель равен, char *то это не так.


1
«Ни в том, ни в другом случае вы не можете изменить строковый литерал, независимо от того, объявлено ли ... [оно] как char * или const char *» Я согласен, что программист не должен пытаться, но вы говорите, что каждый компилятор C на каждом Платформа будет отклонять код, организовывать сбой кода во время выполнения или что-то еще? Я считаю, что один файл может иметь определение и инициализацию, а другой файл может содержать extern ... nameи иметь *name = 'X';. На «правильной операционной системе» это может дать сбой, но на встроенных системах я бы ожидал, что он будет делать что-то конкретное для платформы / компилятора.
gbulmer

@gbulmer: Вы не можете изменить строковый литерал в правильной программе на Си. То, к чему может привести неправильная C-программа, ни здесь, ни там.
Кафе

@gbulmer: Одним из полезных определений является программа, которая не нарушает никаких ограничений, определенных стандартом языка Си. Другими словами, программа, которая изменяет строковый литерал, неверна так же, как и программа, которая разыменовывает нулевой указатель или выполняет деление на 0, неверно.
Кафе

Кафе - я думал, что это может быть то, что вы имели в виду. Тогда "Ни в том, ни в другом случае вы можете модифицировать строковый литерал», кажется, переутомление. Было бы правильно сказать: «В обоих случаях ограничения, указанные стандартом языка C, были нарушены, независимо ... Компилятор или система времени выполнения не могут выявить нарушения стандарта во всех случаях». Я предполагаю, что стандарт занимает позицию, что эффект не определен?
gbulmer

1
Когда стандарт не может утверждать что-либо в любом случае, я думаю, что определение поведения как «неопределенного» представляется точно правильной границей и полезным. Утвердить отношение «правильной C-программы» не может разыменовать нулевой указатель, звучит эквивалентно доказательству проблемы остановки. Но я не против. Я бы этого не сделал и не ожидал, что это сойдет с рук «бесплатно,
Скотт

4

СЛУЧАЙ 1:

char *str = "Hello";
str[0] = 'M'  //Warning may be issued by compiler, and will cause segmentation fault upon running the programme

Выше указано, что str указывает на буквальное значение «Hello», которое жестко запрограммировано в двоичном изображении программы, которое помечено как доступное только для чтения в памяти, означает, что любое изменение в этом строковом литерале является недопустимым, и это приведет к ошибкам сегментации.

ДЕЛО 2:

const char *str = "Hello";
str[0] = 'M'  //Compile time error

ДЕЛО 3:

char str[] = "Hello";
str[0] = 'M'; // legal and change the str = "Mello".

2

Первое, что вы можете изменить, если хотите, второе, которое вы не можете. Читайте о constправильности (есть несколько хороших руководств о разнице). Там также, char const * nameгде вы не можете переписать это.


Что именно может измениться?
Антти Хаапала

2

Вопрос в том, в чем разница между

char *name

которая указывает на постоянный строковый литерал, и

const char *cname

Т.е. дано

char *name = "foo";

и

const char *cname = "foo";

Существует не так много различий между 2 и оба могут рассматриваться как правильные. Из-за долгого наследия C-кода строковые литералы имели тип « char[]нет» const char[], и существует множество более старых кодов, которые также принимают char *вместо const char *, даже если они не модифицируют аргументы.

Принципиальное отличие от 2 в общем состоит в том, что *cnameили cname[n]будет вычисляться для l-значений типа const char, тогда как *nameили name[n]будет оцениваться для l-значений типа char, которые являются модифицируемыми l-значениями . Соответствующий компилятор необходим для создания диагностического сообщения, если целью назначения не является изменяемое значение lvalue ; не нужно выдавать никаких предупреждений о присвоении lvalues ​​типа char:

name[0] = 'x'; // no diagnostics *needed*
cname[0] = 'x'; // a conforming compiler *must* produce a diagnostics message

Компилятор не обязан останавливать компиляцию в любом случае; достаточно, чтобы он выдал предупреждение для назначения cname[0]. Полученная программа не является правильной программой. Поведение конструкции не определено . Может произойти сбой или, что еще хуже, может не произойти сбой и может изменить строковый литерал в памяти.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.