Const char * const versus const char *?

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

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

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

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

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

Ответ 1

Последнее не позволяет вам изменять 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, в котором он указывает.

Ответ 2

  • Уменяемый указатель на изменяемый символ

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

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

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

    const char * const p;
    

Ответ 3

const char * const означает указатель, а также данные, на которые указывает указатель, оба const!

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

Пример.

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. 

Ответ 4

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

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

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

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

Ответ 5

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

Просто хотел уточнить ответ Томаса Мэтьюза. Правило Right-Left Rule объявлений типа 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 постоянный (полный!)

Пример 2

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

    char* const foo
          ^^^^^
    

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

  • Продолжить влево

    char* const foo
        ^
    

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

  • Продолжить влево

    char* const foo
    ^^^^
    

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

Пример 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

Ответ 6

Разница заключается в том, что без дополнительного 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 и опускают его, даже если оно было бы семантически правильным.

Ответ 7

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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.

Ответ 11

Почти все остальные ответы верны, но они пропускают один аспект этого: когда вы используете дополнительный 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 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 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, даже если они могут быть, независимо от того, являются ли эти параметры указателями.

Ответ 12

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