Почему мы используем `const` для строк при определении с помощью указателей в C?

Amirali спросил: 31 июля 2018 в 09:49 в: c

Я слышал от разработчика C, что для определения массивов с указателями мы пишем int *const a;
, потому что массивы имеют значения переменных, но постоянные указатели. Но для строк мы пишем const char *s;.

Я не понимал, почему. Это верно? Действительно ли строки имеют постоянные значения и указатели переменных в C?


3 ответа

Lundin ответил: 31 июля 2018 в 10:00

Код, такой как int *const a;, в большинстве случаев бессмыслен. Основное использование создания указателя const - это когда у вас есть справочная таблица на основе указателей. Или когда вы хотите, чтобы указатель хранился в постоянной памяти во встроенной системе.

Некоторым запутанным программистам нравится писать код вроде void func (int* const ptr), но, на мой взгляд, это только запутанность , поскольку ptr в любом случае является копией исходного указателя, и вызывающей стороне все равно, что функция делает со своей локальной копией внутри.

Не путать с const правильность , что означает, что указатели на данные только для чтения должны быть объявлены как const int* ptr. Это широко признано как хорошая практика программирования, и это канонический C, так как большая часть стандарта C использует константную корректность для стандартной библиотеки C.

Что касается указателей на строковые литералы (например, "hello"), они должны быть объявлены как const char* ptr = "hello"; по той причине, что модификация строкового литерала вызывает неопределенное поведение. Это означает, что его изменение является ошибкой и может привести к сбою программы. Это известный недостаток C - тип самого строкового литерала, даже если нам не разрешено писать в него, это char[]. Это по историческим причинам. Однако в C ++ они исправили этот языковой недостаток, унаследованный от C, поэтому все строковые литералы являются const char[] в C ++.

Stargateur ответил: 31 июля 2018 в 10:11
Чтобы быть плохим, я предпочитаю char const *;) и думаю, что вы заставляете свою точку зрения относительно самой переменной make const, в void func (int* const ptr) нет ничего запутывающего
Lundin ответил: 31 июля 2018 в 10:21
@Stargateur Ты тоже пишешь extern const int func (const int param x, const int param y)? Это тот же аргумент.
Stargateur ответил: 31 июля 2018 в 10:34
Нет, это не так, ваш пример совершенно другой. 1. Поскольку в C по умолчанию используется значение mut, поэтому const необходим вопреки extern 2. const в возвращаемом типе функции игнорируется в отличие от const в переменной параметра
Tim Randall ответил: 03 августа 2018 в 12:26

Учтите следующее:

char* a;
char* const b = "Hello"
const char* c;
const char* const d = "world";

a просто указывает на символ. Это может быть первый символ строки или один байт. Этот символ можно изменить, написав *a='Z' или a[0]='q' или передав a в функцию, принимающую параметр char*. Сам указатель также может быть изменен, чтобы указывать на некоторые другие символы, например, a=b; или a="foo";. Это наиболее гибкая и наименее безопасная форма, поскольку с ней можно делать все что угодно.

b - это постоянный указатель на символ. В этом случае он указывает на массив символов. Эти символы могут быть изменены, например, b[1]='a'; или *b=0;, но сам b является константой, что означает, что он никогда не может указывать на другую часть памяти. Эта форма может использоваться, когда вы выделили буфер или массив, содержимое которого вы хотите изменить, но которое не может быть перемещено, поскольку оно было выделено операционной системой.

c указывает на постоянный характер. Вы можете изменить то, на что он указывает (c="foo"; или c=b;), но вы не можете использовать этот указатель для изменения символов, на которые он указывает - , даже если он указывает на не -константный буфер b. Эта форма обычно используется при обработке предварительно сгенерированных строк. Вы можете использовать эту переменную для хранения найденных вами строк и передачи их адресов, если вы не изменяете сами строки.

d - это постоянный указатель на постоянный характер. Вы не можете изменить символы, на которые он указывает, и вы не можете направить сам указатель на другое место в памяти. Все, что вы можете сделать, это прочитать строку или отдельные символы (char e=d[4]; или puts(d);). Эта форма в основном используется при передаче строк ссылок, например, при именовании реальных объектов.

chqrlie ответил: 01 августа 2018 в 06:50
Боюсь, последнее определение просто избыточно: const char const* d строго эквивалентно const char *d и char const *d. Чтобы d был постоянным указателем на постоянный символ, вы должны определить его как const char * const d; или char const * const d;.
Tim Randall ответил: 03 августа 2018 в 11:22
Correctd. Благодарю.
chqrlie ответил: 03 августа 2018 в 12:21
Хорошо, ответ исправлен, но пропаганда использования переменных-указателей const как обычно используемых не очень полезна. Случаи, когда это необходимо, исчезающе редки. Пометка локальных переменных как const запутывает код без каких-либо преимуществ.
Tim Randall ответил: 03 августа 2018 в 12:28
Я попытался уточнить формулировку. Намерение не предполагать, что эта форма была распространена, но когда эта форма используется, она обычно используется для справочных материалов.
chqrlie ответил: 03 августа 2018 в 12:53
Хорошо, но я все еще хмурится на в основном используется . Эта форма почти никогда не используется, за исключением чрезмерно ограничительных соглашений о кодировании и людей, которые не знают о ее точной семантике. Ссылочные строки могут передаваться и обрабатываться с помощью обычных указателей const char *. Вы действительно использовали эту форму и / или вторую?
veefu new_learner ответил: 01 августа 2018 в 06:01
  1. Когда вы используете этот оператор

    char *str = "Stack Overflow";
    

    , это означает, что str указывает на строку "Stack Overflow", которая хранится в памяти кода, и пользователь не имеет права изменять его. Поэтому для этого оператора запись const перед char *str не имеет никакого эффекта, поскольку строка уже является константой.

    Но const воспроизводится важная роль, когда вы указываете на базовый адрес массива, например

    char arr[20];
    strcpy(arr,"Stack Overflow");
    const char *str = arr;
    

    Использование const означает, что вы не можете изменить строку в arr через указатель str.

  2. Когда вы используете

    char * const ptr;
    

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

Luis Colorado ответил: 01 августа 2018 в 01:11
"запись const перед char *str не имеет никакого эффекта" Это неверно. Если вы введете const char *str, компилятор не позволит вам изменить строку, что приведет к ошибке времени компиляции. Не делать это - неопределенное поведение, так как в зависимости от архитектуры вам удастся изменить строки только для чтения, или вы потерпите неудачу с возможным исключением при попытке записи в память только для чтения.
HolyBlackCat ответил: 01 августа 2018 в 06:08
"запись const перед char *str не имеет никакого эффекта" Это не так.