'const int' vs. 'int const' як параметри функції в C ++ та C


116

Поміркуйте:

int testfunc1 (const int a)
{
  return a;
}

int testfunc2 (int const a)
{
  return a;
}

Ці дві функції однакові у кожному аспекті чи є різниця?

Мене цікавить відповідь на мові С, але якщо в мові С ++ є щось цікаве, я також хотів би це знати.


Чи є зараз ключове слово const в C? Раніше не було, але я не так знайомий зі стандартом C 99.
Оноріо Катенач

8
Вам не потрібно бути. С90 достатньо. Не було в оригіналі K&R C, хоча.
Марк Бейкер

3
Це ключове слово в C89 та ANSI. Я не знаю, чи це було ключове слово ще за часів Кернінгема та Річі.
Нілс Піпенбрінк

7
Цей сайт перекладає "C gibberish" на англійську мову cdecl.org
Motti

5
Я б сказав "C gibberish to English гибberish", але все одно приємно :)
Кос

Відповіді:


175

const Tі T constоднакові. З типами вказівників це ускладнюється:

  1. const char* є вказівником на константу char
  2. char const* є вказівником на константу char
  3. char* const є постійним вказівником на (змінний) char

Іншими словами, (1) і (2) тотожні. Єдиний спосіб скласти вказівник (а не pointee) const- використовувати суфікс- const.

Ось чому багато людей вважають за краще завжди ставити constправоруч від типу (стиль "Східний конст"): це робить його розташування відносно типу послідовним і легким для запам'ятовування (це також анекдотично, здається, полегшує навчання початківцям ).


2
C має const, заданий: статичний const char foo [] = "foo"; вам краще не змінювати foo.
Джеймс Антілл

4
K&R C не мала const; C90 (і C99) робить. Це трохи обмежено порівняно з C ++, але це корисно.
Марк Бейкер

це правда також для довідок?
Кен

1
@Ken Так, це те саме.
Конрад Рудольф

1
@ étale-когомологія Добре, додано. Повинні були бути там весь час.
Конрад Рудольф

339

Хитрість полягає в тому, щоб прочитати декларацію назад (справа наліво):

const int a = 1; // read as "a is an integer which is constant"
int const a = 1; // read as "a is a constant integer"

Обидва - це одне і те ж. Тому:

a = 2; // Can't do because a is constant

Трюк зворотного читання особливо корисний, коли ви маєте справу зі складнішими заявами, такими як:

const char *s;      // read as "s is a pointer to a char that is constant"
char c;
char *const t = &c; // read as "t is a constant pointer to a char"

*s = 'A'; // Can't do because the char is constant
s++;      // Can do because the pointer isn't constant
*t = 'A'; // Can do because the char isn't constant
t++;      // Can't do because the pointer is constant

5
що з "char const * u"? Чи читається це "Вказівник на постійний символ" або "Вказівник, який є постійним для символу"? Здається, неоднозначно. У стандарті йдеться про перше, але щоб дізнатися це, потрібно враховувати правила пріоритетності та асоціативності.
Панайотис Карабассіс

5
@PanayiotisKarabassis До всіх слід ставитися як до ланцюжка прикметників, не змінюючи місцями. char const *, зчитується зліва направо: "вказівник, const, char". Це вказівник на складання символів. Коли ви говорите «покажчик, який є постійним», прикметник «константа» знаходиться на вказівнику. Отже, у такому випадку ваш список прикметників повинен був насправді бути: "const, pointer, char". Але ти маєш рацію, в цьому фокусі є неоднозначність. Це справді "хитрість", більш ніж остаточне "правило".
Атес Горал

5
Коли ви оголошуєте дику комбінацію масиву, функції, вказівника та вказівника функції, читання назад не працює більше (на жаль). Однак ви можете читати ці безладні декларації за спіральним малюнком . Інші їх так розчарували, що вигадали Го.
Мартін Дж

@Martin JH: Чи не можна їх розбити за допомогою typedefs? І / або використання посилань для усунення непрямих?
Пітер Мортенсен

14

Різниці немає. Вони обидва оголошують "а" цілим числом, яке неможливо змінити.

Місце, де починають з'являтися відмінності, - це коли ви користуєтеся покажчиками.

І те й інше:

const int *a
int const *a

оголосити "а" вказівником на ціле число, яке не змінюється. "a" можна призначити, але "* a" не може.

int * const a

оголошує "a" постійним вказівником на ціле число. "* a" можна призначити, але "a" не може.

const int * const a

оголошує "a" постійним вказівником на постійне ціле число. Ні "a", ні "* a" не можуть бути призначені.

static int one = 1;

int testfunc3 (const int *a)
{
  *a = 1; /* Error */
  a = &one;
  return *a;
}

int testfunc4 (int * const a)
{
  *a = 1;
  a = &one; /* Error */
  return *a;
}

int testfunc5 (const int * const a)
{
  *a = 1;   /* Error */
  a = &one; /* Error */
  return *a;
}

Останній приклад - найпростіше пояснення, чудово!
exru

7

Пракаш правильний, що декларації однакові, хоча, можливо, трохи більше пояснень у випадку покажчика.

"const int * p" - це вказівник на int, який не дозволяє змінювати int через цей покажчик. "int * const p" - це вказівник на int, який неможливо змінити, щоб вказати на інший int.

Дивіться https://isocpp.org/wiki/faq/const-correctness#const-ptr-vs-ptr-const .


Якір ("faq-18.5.)" Зламаний. На який він повинен посилатися (їх декілька з "const" і "*")?
Пітер Мортенсен

@PeterMortensen: Так, гниття посилань. Дякую. Я оновив посилання.
Фред Ларсон

5

const intідентичний int const, як це стосується всіх скалярних типів у C. Взагалі, оголошення параметра скалярної функції constне потрібне, оскільки семантика виклику за значенням C означає, що будь-які зміни змінної є локальними для її функції, що укладає.


4

Це не пряма відповідь, а відповідна порада. Щоб все було прямо, я завжди використовую конвекцію "поставлену constназовні", де під "зовні" я маю на увазі крайній лівий або крайній правий. Таким чином не виникає плутанини - const застосовується до найближчого (або типу, або типу *). Наприклад,



int * const foo = ...; // Pointer cannot change, pointed to value can change
const int * bar = ...; // Pointer can change, pointed to value cannot change
int * baz = ...; // Pointer can change, pointed to value can change
const int * const qux = ...; // Pointer cannot change, pointed to value cannot change

6
Можливо, вам краще скористатися правилом "const робить const все, що від нього залишилося". Наприклад, "int * const foo" робить вказівник "const", оскільки вказівник залишений на нього. Однак ви б написали другий рядок "int const * bar", робить int const, тому що він залишений до нього. "int const * const * qux", робить і те, і int, і покажчик const, тому що або один з них один раз залишається.
Мецькі

4

Вони однакові, але в C ++ є вагомий привід завжди використовувати const праворуч. Ви будете послідовні скрізь, оскільки функції члена const повинні бути оголошені таким чином:

int getInt() const;

Він змінює thisвказівник у функції з Foo * constна Foo const * const. Дивіться тут.


3
Це зовсім інший вид const.
Джастін Майнерс

1
Чому це зовсім інше? Достатньо відрізняється, щоб заробити потік.
Нік Вестгейт,

1
Так, питання про різницю між "const int" і "int const", ваша відповідь не має нічого спільного.
Джастін Майнерс

1
Я сказав, що вони однакові. І все ж прийняті та голосовані відповіді також дають додаткову інформацію про типи вказівників. Ви теж їх бракували?
Нік Вестгейт


3

Я думаю, що в цьому випадку вони однакові, але ось приклад, коли порядок має значення:

const int* cantChangeTheData;
int* const cantChangeTheAddress;

2
Дійсно, але int const * - це те саме, що і перше, тому порядок int і const не має значення, це лише порядок * і const.
Марк Бейкер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.