const char * const против const char *?

110

Я просматриваю несколько примеров программ, чтобы заново познакомиться с C ++, и столкнулся со следующим вопросом. Во-первых, вот пример кода:

void print_string(const char * the_string)
{
    cout << the_string << endl;
}

int main () {
    print_string("What's up?");
}

В приведенном выше коде вместо параметра print_string мог быть const char * const the_string. Что для этого будет правильнее?

Я понимаю, что разница в том, что один является указателем на постоянный символ, а другой - постоянным указателем на постоянный символ. Но почему оба эти действия работают? Когда это будет актуально?

рис
источник

Ответы:

244

Последнее мешает вам модифицировать the_stringвнутри print_string. Это действительно было бы здесь уместно, но, возможно, многословие оттолкнуло разработчика.

char* the_string: Я могу изменить, на charкакие the_stringточки, и я могу изменить, charна которые они указывают.

const char* the_string: Я могу изменить, на charкакие the_stringточки, но не могу изменить, charна которые они указывают.

char* const the_string: Я не могу изменить charв которой the_stringточки, но я могу модифицировать , charв которой он указывает.

const char* const the_string: Я не могу изменить charв которой the_stringточки, и я не могу модифицировать , charв которой он указывает.

Кент Бугарт
источник
11
+1 за последнее предложение. Const-корректность многословна, но оно того стоит.
mskfisher
6
@Xeo: ваша форма еще более сбивает с толку, потому что это одна транспозиция от полного изменения ее значения. const char *намного лучше, потому что он constнаходится на противоположной стороне.
R .. GitHub НЕ ПОМОГАЕТ ICE
7
@R ..: Ну, по крайней мере, для меня это не так. Читая справа налево, я получаю «указатель на const char». Для меня это просто лучше.
Xeo
6
Ну, вы обманываете себя, потому что типы C читаются изнутри, а не слева направо. :-)
R .. GitHub НЕ ПОМОГАЕТ ICE
11
Я немного смущен, я, видимо, единственный, кто этого не понимает ... но какая разница между «символом, на который он указывает» и «символом, на который он указывает»?
Lack
138
  1. Изменяемый указатель на изменяемый символ

    char *p;
  2. Изменяемый указатель на постоянный символ

    const char *p;
  3. Постоянный указатель на изменяемый символ

    char * const p; 
  4. Постоянный указатель на постоянный символ

    const char * const p;
Джеймс Майкл Хэйр
источник
const char* p; --> constant pointer to mutable characterchar *const p; --> mutable pointer to constant character
Разве
2
@NulledPointer Нет. Объявления C ++ формируются справа налево. Таким образом, вы можете читать const char * pтак: «p - указатель на символьную константу» или изменяемый указатель на константный символ, как правильно утверждает Джеймс. то же самое со вторым :).
Самидамару
28

const char * constозначает указатель, а также данные , указатель указывал на, являются как сопзом!

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

Пример.

const char *p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //okay, changing the non-const pointer. 

const char * const p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //error, changing the const pointer. 
Наваз
источник
20

(Я знаю, что это старый, но я все равно хотел поделиться.)

Просто хотел уточнить ответ Томаса Мэтьюза. Правило правого-левого объявления типов C в значительной степени говорит: при чтении объявления типа C начинайте с идентификатора и идите вправо, когда вы можете, и уходите, когда вы не можете.

Лучше всего это пояснить парой примеров:

Пример 1

  • Начните с идентификатора, мы не можем пойти направо, поэтому идем налево

    const char* const foo
                ^^^^^

    foo - константа ...

  • Продолжайте движение налево

    const char* const foo
              ^

    foo - постоянный указатель на ...

  • Продолжайте движение налево

    const char* const foo
          ^^^^

    foo - постоянный указатель на char ...

  • Продолжайте движение налево

    const char* const foo
    ^^^^^

    foo - постоянный указатель на константу char (Complete!)

Пример 2

  • Начните с идентификатора, мы не можем пойти направо, поэтому идем налево

    char* const foo
          ^^^^^

    foo - константа ...

  • Продолжайте движение налево

    char* const foo
        ^

    foo - постоянный указатель на ...

  • Продолжайте движение налево

    char* const foo
    ^^^^

    foo - постоянный указатель на char (Complete!)

Пример 1337

  • Начните с идентификатора, но теперь мы можем идти направо!

    const char* const* (*foo[8])()
                            ^^^

    foo - это массив из 8 ...

  • Нажмите на скобку, чтобы больше не могли идти вправо, идите влево

    const char* const* (*foo[8])()
                        ^

    foo - это массив из 8 указателей на ...

  • Закончено в скобках, теперь можно идти вправо

    const char* const* (*foo[8])()
                                ^^

    foo - это массив из 8 указателей на функцию, которая возвращает ...

  • Больше ничего направо, иди налево

    const char* const* (*foo[8])()
                     ^

    foo - это массив из 8 указателей на функцию, которая возвращает указатель на ...

  • Продолжайте движение налево

    const char* const* (*foo[8])()
                ^^^^^

    foo - это массив из 8 указателей на функции, который возвращает указатель на константу ...

  • Продолжайте движение налево

    const char* const* (*foo[8])()
              ^

    foo - это массив из 8 указателей на функции, который возвращает указатель на постоянный указатель на ...

  • Продолжайте движение налево

    const char* const* (*foo[8])()
          ^^^^

    foo - это массив из 8 указателей на функции, который возвращает указатель на постоянный указатель на char ...

  • Продолжайте движение налево

    const char* const* (*foo[8])()
    ^^^^^

    foo - это массив из 8 указателей на функции, который возвращает указатель на постоянный указатель на константу типа char (Complete!)

Дальнейшее объяснение: http://www.unixwiz.net/techtips/reading-cdecl.html

Гарретт
источник
CMIIW, const char * const foo должен быть эквивалентен char const * const foo?
luochenhuan
@luochenhuan Да, действительно.
Garrett
12

Многие предлагают читать спецификатор типа справа налево.

const char * // Pointer to a `char` that is constant, it can't be changed.
const char * const // A const pointer to const data.

В обеих формах указатель указывает на постоянные данные или данные только для чтения.

Во второй форме указатель изменить нельзя; указатель всегда будет указывать на одно и то же место.

Томас Мэтьюз
источник
3

Разница в том, что без дополнительных действий constпрограммист может изменить внутри метода, на который указывает указатель; например:

 void print_string(const char * the_string)
 {
    cout << the_string << endl;
    //....
    the_string = another_string();
    //....

 }

Это было бы незаконно, если бы подпись была void print_string(const char * const the_string)

Многие программисты считают constключевое слово extra слишком многословным (в большинстве случаев) и пропускают его, даже если оно было бы семантически правильным.

Леонблой
источник
2

В последнем вы гарантируете, что не изменяете указатель и символ, в первом вы гарантируете только то, что содержимое не изменится, но вы можете перемещать указатель.

Хесус Рамос
источник
Ах, значит, без последней константы я мог бы установить указатель так, чтобы он указывал на совершенно другую строку?
фото
Да, без этой последней константы вы можете использовать указатель параметра для выполнения некоторой итерации по арифметике указателя, где, если бы эта константа была, вам пришлось бы создать свой собственный указатель, который является копией этого параметра.
Хесус Рамос,
2

Нет причин, по которым ни один из них не работал бы. Все, что print_string()нужно сделать, это распечатать значение. Он не пытается его изменить.

Хорошая идея - создать функцию, которая не изменяет аргументы mark как const. Преимущество состоит в том, что переменные, которые нельзя изменять (или вы не хотите изменять), можно передавать этим функциям без ошибок.

Что касается точного синтаксиса, вы хотите указать, какой тип аргументов «безопасен» для передачи в функцию.

Джонатан Вуд
источник
2

Я думаю, что это редко бывает актуально, потому что ваша функция не вызывается с такими аргументами, как & * the_string или ** the_string. Сам указатель является аргументом типа значения, поэтому, даже если вы измените его, вы не собираетесь изменять копию, которая использовалась для вызова вашей функции. Версия, которую вы показываете, гарантирует, что строка не изменится, и я думаю, что в данном случае этого достаточно.

Эглин
источник
2

const char *означает, что вы не можете использовать указатель, чтобы изменить то, на что он указывает. Однако вы можете изменить указатель, чтобы он указывал на что-нибудь еще.

Рассматривать:

const char * promptTextWithDefault(const char * text)
{
    if ((text == NULL) || (*text == '\0'))
        text = "C>";
    return text;
}

Параметр является неконстантным указателем на const char, поэтому его можно изменить на другое const char *значение (например, на постоянную строку). Если же мы написали по ошибке, *text = '\0'то получим ошибку компиляции.

Возможно, если вы не собираетесь изменять то, на что указывает параметр, вы можете создать параметр const char * const text, но это не является обычным делом. Обычно мы разрешаем функциям изменять значения, передаваемые параметрам (поскольку мы передаем параметры по значению, любое изменение не влияет на вызывающего).

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

TonyR
источник
Вот Это Да! Я пытался выяснить разницу между моей const char *и подписью char const *- то, как вы сформулировали свой BTW, действительно помогло!
sage
1

Почти все остальные ответы верны, но они упускают из виду один аспект: когда вы используете дополнительный constпараметр в объявлении функции, компилятор по существу проигнорирует его. На мгновение давайте проигнорируем сложность вашего примера, являющегося указателем, и просто воспользуемся расширением int.

void foo(const int x);

объявляет ту же функцию, что и

void foo(int x);

Только в определении функции есть дополнительное constзначение:

void foo(const int x) {
    // do something with x here, but you cannot change it
}

Это определение совместимо с любым из приведенных выше заявлений. Вызывающего абонента это не волнует x- constэто деталь реализации, не имеющая отношения к месту вызова.

Если у вас есть constуказатель на constданные, применяются те же правила:

// these declarations are equivalent
void print_string(const char * const the_string);
void print_string(const char * the_string);

// In this definition, you cannot change the value of the pointer within the
// body of the function.  It's essentially a const local variable.
void print_string(const char * const the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // COMPILER ERROR HERE
}

// In this definition, you can change the value of the pointer (but you 
// still can't change the data it's pointed to).  And even if you change
// the_string, that has no effect outside this function.
void print_string(const char * the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // OK, but not observable outside this func
}

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

Адриан Маккарти
источник
«компилятор по существу проигнорирует это» не всегда верно, Visual C ++ 2015 выдаст предупреждение, если вы добавите дополнительный constпараметр в свой параметр функции в определении, но не в объявлении.
raymai97 01
@ raymai97: Я думаю, что это ошибка компилятора 2015 года, но у меня нет под рукой 2015 года для тестирования. Я работаю так, как описал в 2017 году, что, согласно моим беседам с некоторыми экспертами по стандартам, является ожидаемым поведением.
Адриан Маккарти
-1

Разница между ними в том, что char * может указывать на любой произвольный указатель. Const char *, напротив, указывает на константы, определенные в разделе DATA исполняемого файла. И, как таковой, вы не можете изменять значения символов строки const char *.

Maz
источник
Я не спрашиваю разницы между char * и const char *. Я спрашиваю между const char * и const char * const
pict
Это неверный ответ. А const char*может указывать куда угодно.
Cubic