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 за останнє речення. Коректність коректності є багатослівною, але вона того варта.
mskfisher

6
@Xeo: ваша форма є ще більш заплутаною, оскільки це є одним транспозицією від зміни цілого значення. const char *набагато краще, тому що constзнаходиться на протилежній стороні.
R .. GitHub СТОП ДОПОМОГАТИ

7
@R ..: Ну, принаймні для мене це не так. Читаючи справа наліво, я отримую "вказівник на const char". Для мене просто так почувається краще.
Xeo

6
Ну, ти обманюєш себе, бо типи С читаються зсередини, а не зліва направо. :-)
R .. GitHub ЗАСТАНОВИЙ ДІЙ

11
Мене трохи бентежить, я, мабуть, єдиний, хто цього не розуміє ... але яка різниця між "знаком, на який він вказує", і "символом, на який він вказує"?
Відсутність

138
  1. Змінюваний вказівник на змінний символ

    char *p;
  2. Змінна вказівник на постійний символ

    const char *p;
  3. Постійний вказівник на змінний символ

    char * const p; 
  4. Постійний вказівник на постійний символ

    const char * const p;

Чи не повинно бути це так: const char* p; --> constant pointer to mutable characterі char *const p; --> mutable pointer to constant character
PnotNP

2
@ NulledPointer №. C ++ декларації формуються праворуч ліворуч. Таким чином, ви можете прочитати const char * pяк: "p - вказівник на констант символів" або змінений вказівник на постійний символ, як правильно стверджує Джеймс. те ж саме з другим :).
Самідамару

28

const char * constозначає покажчик, а також дані , покажчик вказував на, є як сопзЬ!

const char *означає лише дані, на які вказував вказівник, є const. вказівник сам по собі не 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. 

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 константа (Повна!)

Приклад 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 (Повна!)

Подальше пояснення: http://www.unixwiz.net/techtips/reading-cdecl.html


CMIIW, const char * const foo має бути еквівалентним char const * const foo?
luochenhuan

@luochenhuan Так, справді вони є.
Гаррет

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ключове слово і його опускають, навіть якщо це було б семантично правильним.


2

В останньому ви гарантуєте не змінювати і вказівник, і символ, у першому ви гарантуєте лише, що вміст не зміниться, але ви можете переміщати вказівник навколо


Так, без остаточного const, я міг би фактично встановити вказівник, щоб він вказував на зовсім інший рядок?
малюнок

Так, без цього остаточного const ви можете використовувати вказівник параметра, щоб зробити деяку ітерацію за арифметикою вказівника, де, якби він був, ви повинні створити власний покажчик, який є копією цього параметра.
Ісус Рамос

2

Немає жодної причини, що б і не працювала. Все, що print_string()робити - це надрукувати значення. Він не намагається змінити його.

Це гарна ідея зробити функцію, яка не змінює аргументи знаків як 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.


Оце Так! Я намагався з'ясувати різницю між моїм const char *і підписом char const *- як ви написали свій текст BTW, справді допомогли!
шавлія

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

@ raymai97: Я думаю, що це помилка в компіляторі 2015 року, але я не маю зручності для тестування 2015 року. Я працюю так, як я описав у 2017 році, що, за моїми розмовами з деякими експертами зі стандартів, - це очікувана поведінка.
Адріан Маккарті

-1

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


Я не прошу різниці між char * і const char *. Я запитую між const char * та const char * const
малюнок

Ця відповідь невірна. А const char*може вказувати куди завгодно.
Кубік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.