В чем разница между const int *, const int * const и int const *? - PullRequest
1195 голосов
/ 17 июля 2009

Я всегда путаюсь, как правильно использовать const int*, const int * const и int const *. Существует ли набор правил, определяющих, что вы можете и не можете делать?

Я хочу знать все, что можно и чего нельзя делать в отношении назначений, передачи функций и т. Д.

Ответы [ 16 ]

5 голосов
/ 04 января 2015

Это просто, но сложно. Обратите внимание, что мы можем поменять квалификатор const на любой тип данных (int, char, float и т. Д.).

Давайте посмотрим на следующие примеры.


const int *p ==> *p только для чтения [p - указатель на постоянное целое число]

int const *p ==> *p только для чтения [p - указатель на постоянное целое число]


int *p const ==> Неправильно Заявление. Компилятор выдает синтаксическую ошибку.

int *const p ==> p только для чтения [p - постоянный указатель на целое число]. Поскольку указатель p здесь только для чтения, объявление и определение должны находиться в одном месте.


const int *p const ==> Неправильно Заявление. Компилятор выдает синтаксическую ошибку.

const int const *p ==> *p только для чтения

const int *const p1 ==> *p и p только для чтения [p - постоянный указатель на постоянное целое число]. Поскольку указатель p здесь только для чтения, объявление и определение должны находиться в одном месте.


int const *p const ==> Неправильно Заявление. Компилятор выдает синтаксическую ошибку.

int const int *p ==> Неправильно Заявление. Компилятор выдает синтаксическую ошибку.

int const const *p ==> *p только для чтения и эквивалентно int const *p

int const *const p ==> *p и p только для чтения [p - постоянный указатель на постоянное целое число]. Поскольку указатель p здесь только для чтения, объявление и определение должны находиться в одном месте.

5 голосов
/ 13 сентября 2011

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

  • Вы часто передаете большие аргументы, такие как строки, как TYPE const &, что предотвращает изменение или копирование объекта. Пример:

    TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }

    Но TYPE & const не имеет смысла, потому что ссылки всегда постоянны.

  • Вы всегда должны помечать методы класса, которые не изменяют класс, как const, в противном случае вы не можете вызвать метод из ссылки TYPE const &. Пример:

    bool TYPE::operator==(const TYPE &rhs) const { ... }

  • Есть общие ситуации, когда и возвращаемое значение, и метод должны быть постоянными. Пример:

    const TYPE TYPE::operator+(const TYPE &rhs) const { ... }

    Фактически, методы const не должны возвращать данные внутреннего класса как ссылку на неконстантную.

  • В результате часто приходится создавать как константный, так и неконстантный метод с использованием константной перегрузки. Например, если вы определите T const& operator[] (unsigned i) const;, вы, вероятно, также захотите неконстантную версию, заданную как:

    inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }

Афаик, в C нет константных функций, функции, не являющиеся членами, сами по себе не могут быть константными в C ++, методы const могут иметь побочные эффекты, а компилятор не может использовать функции const, чтобы избежать дублирования вызовов функций. Фактически, даже простая ссылка int const & может свидетельствовать об изменении значения, на которое она ссылается, в другом месте.

3 голосов
/ 04 февраля 2019

Для меня позиция const, то есть, отображается ли она влево или вправо или влево и вправо относительно *, помогает мне понять фактическое значение.

  1. A const влево * означает, что объект, на который указывает указатель, является const объектом.

  2. A const ПРАВО * указывает, что указатель является const указателем.

Следующая таблица взята из Stanford CS106L Standard C ++ Programming Laboratory, читателя курса.

enter image description here

2 голосов
/ 23 сентября 2018

Константа с int с обеих сторон сделает указатель на константу int :

const int *ptr=&i;

или

int const *ptr=&i;

const после * сделает постоянный указатель на int :

int *const ptr=&i;

В этом случае все они являются указателем на постоянное целое число , но ни один из них не является постоянным указателем:

 const int *ptr1=&i, *ptr2=&j;

В этом случае все указывают на постоянное целое число , а ptr2 - постоянный указатель на постоянное целое число . Но ptr1 не является постоянным указателем:

int const *ptr1=&i, *const ptr2=&j;
1 голос
/ 17 февраля 2019

Просто ради полноты для C, следуя другим объяснениям, не уверен для C ++.

  • pp - указатель на указатель
  • p - указатель
  • данные - указанная вещь, в примерах x
  • полужирный - переменная только для чтения

Указатель

  • р данные - int *p;
  • p data - int const *p;
  • p data - int * const p;
  • p data - int const * const p;

Указатель на указатель

  1. pp p data - int **pp;
  2. pp p data - int ** const pp;
  3. pp p data - int * const *pp;
  4. pp p data - int const **pp;
  5. pp p data - int * const * const pp;
  6. pp p data - int const ** const pp;
  7. pp p data - int const * const *pp;
  8. pp p data - int const * const * const pp;
// Example 1
int x;
x = 10;
int *p = NULL;
p = &x;
int **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 2
int x;
x = 10;
int *p = NULL;
p = &x;
int ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 3
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 4
int const x = 10; // Definition must happen during declaration
int const * p = NULL;
p = &x;
int const **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 5
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 6
int const x = 10; // Definition must happen during declaration
int const *p = NULL;
p = &x;
int const ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 7
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 8
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

N-уровней разыменования

Просто продолжай идти, но пусть человечество отлучит тебя от жизни.

int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;

printf("%d \n", ****pppp);
1 голос
/ 15 марта 2018

В основном это касается второй строки: лучшие практики, назначения, параметры функций и т. Д.

Общая практика. Попробуйте сделать все, что вы можете. Или, говоря иначе, сделайте все для начала const, а затем удалите точно минимальный набор const, необходимый для работы программы. Это будет большим подспорьем в достижении константности и поможет избежать появления незаметных ошибок, когда люди пытаются вводить вещи, которые они не должны изменять.

Избегайте const_cast <> как чума. Есть один или два законных варианта использования, но их очень мало и они далеко друг от друга. Если вы пытаетесь изменить объект const, вам будет гораздо лучше найти того, кто объявил его const в первом темпе, и обсудить с ними этот вопрос, чтобы достичь консенсуса относительно того, что должно произойти.

Что очень аккуратно приводит к выполнению заданий. Вы можете назначить что-то, только если это неконстантно. Если вы хотите присвоить что-то, что является постоянным, см. Выше. Помните, что в декларациях int const *foo; и int * const bar; разные вещи const - другие ответы здесь освещают эту проблему превосходно, поэтому я не буду вдаваться в подробности.

Параметры функции:

Передача по значению: например, void func(int param) Вас не волнует, так или иначе, на вызывающем сайте. Можно привести аргумент, что есть варианты использования для объявления функции как void func(int const param), но это не влияет на вызывающего, только на саму функцию, в том смысле, что любое переданное значение не может быть изменено функцией во время вызова.

Передать по ссылке: например, void func(int &param) Теперь это имеет значение. Как только что объявлено, func разрешено изменять param, и любой вызывающий сайт должен быть готов справиться с последствиями. Изменение декларации на void func(int const &param) изменяет контракт и гарантирует, что func теперь не может изменить param, что означает, что то, что передано, это то, что вернется. Как уже отмечали другие, это очень полезно для дешевой передачи большого объекта, который вы не хотите менять. Передача ссылки намного дешевле, чем передача большого объекта по значению.

Передача по указателю: например, void func(int *param) и void func(int const *param) Эти два в значительной степени синонимичны с их эталонными ссылками, с оговоркой, что вызываемой функции теперь нужно проверять на nullptr, если какая-то другая договорная гарантия не гарантирует func, что она никогда не получит nullptr в param.

Часть мнения на эту тему. Доказать правильность в таком случае адски сложно, просто чертовски легко ошибиться. Так что не рискуйте, и всегда проверяйте параметры указателя для nullptr. Вы избавите себя от боли и страданий, и вам будет трудно найти ошибки в долгосрочной перспективе. А что касается стоимости проверки, то она очень дешевая, и в случаях, когда статический анализ, встроенный в компилятор, может управлять им, оптимизатор все равно ее исключит. Включите генерацию временного кода канала для MSVC или WOPR (я думаю) для GCC, и вы получите всю программу, то есть даже в вызовах функций, которые пересекают границу модуля исходного кода.

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

...