'const int' против 'int const' как параметры функции в C ++ и C

116

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

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

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

Эти две функции одинаковы во всех аспектах или есть разница?

Меня интересует ответ для языка C, но если есть что-то интересное в языке C ++, я бы тоже хотел знать.

Нильс Пипенбринк
источник
Есть ли сейчас в C ключевое слово const? Раньше не было, но я не так хорошо знаком со стандартом C 99.
Онорио Катеначчи,
8
Тебе не нужно быть. С90 хватит. Однако этого не было в оригинальном K&R C.
Марк Бейкер,
3
Это ключевое слово в C89 и ANSI. Я не знаю, было ли это ключевым словом во времена Кернингема и Ричи.
Нильс Пипенбринк, 02
7
Этот сайт переводится «C тарабарщину» на английском cdecl.org
Моти
5
Я бы сказал: «От тарабарщины до английской тарабарщины», но все равно приятно :)
Кос

Ответы:

176

const Tи T constидентичны. С типами указателей все становится сложнее:

  1. const char* указатель на константу char
  2. char const* указатель на константу char
  3. char* const постоянный указатель на (изменяемый) char

Другими словами, (1) и (2) идентичны. Единственный способ сделать указатель (а не указатель) const- использовать суффикс- const.

Вот почему многие люди предпочитают всегда ставить constсправа от шрифта (стиль «Восточная константа»): это делает его расположение относительно типа последовательным и легким для запоминания (это также, по некоторым данным, упрощает обучение начинающих. ).

Конрад Рудольф
источник
2
В C есть const, при условии: static const char foo [] = "foo"; лучше не переделывай foo.
Джеймс Антилл
4
K&R C не имел const; C90 (и C99) делает. Он немного ограничен по сравнению с C ++, но полезен.
Марк Бейкер,
это также верно для ссылок?
Кен
1
@Ken Да, это то же самое.
Конрад Рудольф
1
@ étale-cohomology Хорошее замечание, добавил. Должен был быть там все время.
Конрад Рудольф
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, указатель, char». Но вы правы, в этой уловке есть двусмысленность. Это действительно «трюк», больше, чем окончательное «правило».
Ates Goral
5
Когда вы объявляете дикую комбинацию массива, функции, указателя и указателя функции, обратное чтение больше не работает (к сожалению). Однако вы можете прочитать эти беспорядочные объявления по спирали . Другие были так разочарованы ими, что изобрели го.
Martin JH
@Martin JH: А нельзя ли их разбить с помощью typedefs? И / или использование ссылок для устранения косвенных указаний?
Питер Мортенсен
14

Нет никакой разницы. Оба они объявляют "a" целым числом, которое нельзя изменить.

Различия начинают проявляться при использовании указателей.

Оба эти:

const int *a
int const *a

объявить "a" указателем на целое число, которое не изменяется. "a" можно присвоить, но нельзя "* a".

int * const 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снаружи», где под «снаружи» я подразумеваю крайний левый или крайний правый угол. Таким образом, не возникает путаницы - константа применяется к ближайшему объекту (типу или типу *). Например,



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, и указатель константными, потому что любой из них когда-то остается за ним.
Mecki
4

Они такие же, но в C ++ есть веская причина всегда использовать const справа. Вы будете последовательны везде, потому что функции-члены const должны быть объявлены следующим образом:

int getInt() const;

Он изменяет thisуказатель в функции с Foo * constна Foo const * const. Посмотреть здесь.

Ник Вестгейт
источник
3
Это совершенно другой вид констант.
Джастин Майнерс
1
Почему это совсем другое? Достаточно разные, чтобы заработать отрицательный голос.
Ник Вестгейт
1
Да, вопрос в разнице между "const int" и "int const", ваш ответ не имеет к этому никакого отношения.
Джастин Майнерс
1
Я сказал, что они такие же. И все же принятые и получившие наибольшее количество голосов ответы также дают дополнительную информацию о типах указателей. Вы тоже проголосовали против них?
Ник Вестгейт
3

Да они такие же только int

и разные для int*

Пракаш
источник
5
(const int *) и (int const *) одинаковы, они просто отличаются от (int * const).
Джеймс Антилл,
3

Я думаю, что в этом случае они такие же, но вот пример, где порядок имеет значение:

const int* cantChangeTheData;
int* const cantChangeTheAddress;
user7545
источник
2
Действительно, но int const * совпадает с первым, поэтому порядок int и const не имеет значения, важен только порядок * и const.
Марк Бейкер,