В чем разница между const int *, const int * const и int const *?

1171

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

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

  • 145
    Вы можете использовать «Правило по часовой стрелке / спирали», чтобы расшифровать большинство объявлений C и C ++.
  • 43
    cdecl.org - это отличный веб-сайт, который автоматически переводит объявления C для вас.
Показать ещё 4 комментария
Теги:
pointers
int
const

15 ответов

1802
Лучший ответ

Прочитайте его назад (как управляется по часовой стрелке/спиральному правилу):

  • int* - указатель на int
  • int const * - указатель на const int
  • int * const - const указатель на int
  • int const * const - const указатель на const int

Теперь первый const может быть по обе стороны от типа так:

  • const int * == int const *
  • const int * const == int const * const

Если вы хотите сходить с ума, вы можете делать такие вещи:

  • int ** - указатель на указатель на int
  • int ** const - указатель const на указатель на int
  • int * const * - указатель на указатель const на int
  • int const ** - указатель на указатель на const int
  • int * const * const - указатель const на указатель const на int
  • ...

И чтобы убедиться, что мы ясно понимаем значение const

const int* foo;
int *const bar; //note, you actually need to set the pointer 
                //here because you can't change it later ;)

foo - переменный указатель на постоянное целое число. Это позволяет вам изменить то, что вы указываете, но не значение, на которое вы указываете. Чаще всего это видно со строк в стиле C, где у вас есть указатель на const char. Вы можете изменить строку, на которую указываете, но вы не можете изменить содержимое этих строк. Это важно, когда сама строка находится в сегменте данных программы и не должна изменяться.

bar - постоянный или фиксированный указатель на значение, которое можно изменить. Это похоже на ссылку без дополнительного синтаксического сахара. Из-за этого, как правило, вы будете использовать ссылку, где вы будете использовать указатель T* const если вам не нужно NULL указатели NULL.

  • 424
    Я хотел бы добавить эмпирическое правило, которое может помочь вам вспомнить, как определить, относится ли «const» к указателю или к указанным данным: разделите оператор по знаку звездочки, а затем, если ключевое слово const появляется в левой части (как в 'const int * foo') - он принадлежит указанным данным, если он находится в правой части ('int * const bar') - это указатель.
  • 11
    @Michael: Спасибо Майклу за такое простое правило для запоминания / понимания const-правила.
Показать ещё 13 комментариев
282

Для тех, кто не знает о правиле по часовой стрелке/спирали: Начните с имени переменной, перемещайте по часовой стрелке (в данном случае, переместитесь назад) к следующему указателю или . Повторяйте до окончания выражения.

вот демонстрационная версия:

Изображение 1189

Изображение 1190

Изображение 1191

Изображение 1192

Изображение 1193

  • 111
    это просто чтение его справа налево.
  • 13
    @sdotdti В этих случаях да, но это также работает в более сложных сценариях, как в примере "ultimate" здесь: mydov.blogspot.ch/2012/11/…
Показать ещё 9 комментариев
135

Я думаю, здесь все уже сказано, но я просто хочу добавить, что вы должны остерегаться typedef ! Это не просто замена текста.

Например:

typedef char *ASTRING;
const ASTRING astring;

Тип astring - char * const, а не const char *. Это одна из причин, по которой я всегда склоняю const к праву от типа и никогда не начинаю.

  • 16
    И для меня это причина никогда не указывать указатели. Я не вижу преимущества в таких вещах, как typedef int* PINT (я предполагаю, что это то, что пришло из практики в C, и многие разработчики продолжали это делать). Отлично, я заменил это * на P , это не ускоряет ввод текста, а также представляет проблему, о которой вы упоминаете
  • 1
    @Mephane - я это вижу. Тем не менее, мне кажется, что в некотором роде следует избегать хорошей языковой функции, чтобы продолжать использовать исключительное синтаксическое правило (о размещении "const"), а не избегать использования исключительного синтаксического правила, чтобы вы могли безопасно использовать эту языковую функцию ,
Показать ещё 5 комментариев
45

Как и многие, все отметили:

В чем разница между const X* p, X* const p и const X* const p?

Вы должны прочитать декларации указателей справа налево.

  • const X* p означает, что "p указывает на X, который является const": объект X не может быть изменен с помощью p.

  • X* const p означает, что "p является указателем const к X, который не является константой": вы не можете изменить указатель p, но вы можете изменить объект X через p.

  • const X* const p означает, что "p - это указатель const на X, который является const": вы не можете изменить указатель p самостоятельно, и вы не можете изменить объект X через p.

  • 1
    Не забывайте, что const X* p; == X const * p; как в "p points to an X that is const": the X object can't be changed via p.
41
  • Постоянная ссылка:

    Ссылка на переменную (здесь int), которая является постоянной. Мы передаем переменную как ссылку главным образом, потому что ссылки меньше по размеру, чем фактическое значение, но есть побочный эффект, и это потому, что он похож на псевдоним на фактическую переменную. Мы можем случайно изменить основную переменную через наш полный доступ к псевдониму, поэтому мы делаем его постоянным, чтобы предотвратить этот побочный эффект.

    int var0 = 0;
    const int &ptr1 = var0;
    ptr1 = 8; // Error
    var0 = 6; // OK
    
  • Константные указатели

    Как только постоянный указатель указывает на переменную, он не может указывать на какую-либо другую переменную.

    int var1 = 1;
    int var2 = 0;
    
    int *const ptr2 = &var1;
    ptr2 = &var2; // Error
    
  • Указатель на константу

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

    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
    
  • Постоянный указатель на константу

    Постоянный указатель на константу - это указатель, который не может ни изменить адрес, на который он указывает, ни изменить значение, сохраненное по этому адресу.

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error
    
16

Этот вопрос показывает точно, почему мне нравится делать то, что я упомянул в своем вопросе является константой после допустимого идентификатора типа? p >

Короче говоря, я считаю, что самый простой способ запомнить правило состоит в том, что "const" идет за тем, к чему он относится. Поэтому в вашем вопросе "int const *" означает, что int является константой, а "int * const" означает, что указатель является постоянным.

Если кто-то решает поставить его на передний план (например: "const int *" ), в качестве особого исключения в этом случае он применяется к предмету после него.

Многие люди любят использовать это специальное исключение, потому что считают, что он выглядит лучше. Мне это не нравится, потому что это исключение и, таким образом, путает вещи.

  • 2
    Я разрываюсь по этому вопросу. Логически это имеет смысл. Однако большинство разработчиков на С ++ пишут const T* и это стало более естественным. Как часто вы когда-либо используете T* const , обычно справка подойдет. Однажды я получил от всего этого немного, когда хотел получить boost::shared_ptr<const T> и вместо этого написал const boost::shared_ptr<T> . Та же проблема в несколько ином контексте.
  • 0
    На самом деле, я использую постоянные указатели чаще, чем постоянные. Кроме того, вы должны подумать о том, как вы будете реагировать в присутствии указателей на указатели (и т. Д.). Конечно, они встречаются реже, но было бы неплохо подумать о вещах таким образом, чтобы вы могли справиться с этими ситуациями с апломбом.
Показать ещё 1 комментарий
15

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

  • const int* совпадает с int const* и означает "указатель на константу int".
  • const int* const совпадает с int const* const и означает "постоянный указатель на константу int".

Edit: Для Dos и Don'ts, если этого ответа недостаточно, не могли бы вы уточнить, что хотите?

14

Простое использование 'const

Простейшее использование - объявить именованную константу. Чтобы сделать это, объявляем константу, как если бы она была переменной, но добавьте ее перед ней. Нужно немедленно инициализировать его в конструкторе, потому что, конечно, нельзя установить значение позже, так как это изменит его. Например,

const int Constant1=96; 

создаст целочисленную константу, невообразимо названную "Constant1" со значением 96.

Такие константы полезны для параметров, которые используются в программе, но их не нужно менять после компиляции программы. Он имеет преимущество для программистов над командой "preprocessor" #define, поскольку он понимается и используется самим компилятором, а не просто заменяется текстом программы препроцессором перед достижением основного компилятора, поэтому сообщения об ошибках гораздо полезнее.

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

const int * Constant2 

объявляет, что Constant2 является указателем переменной на константное целое число и

int const * Constant2

- альтернативный синтаксис, который делает то же самое, тогда как

int * const Constant3

объявляет, что Constant3 является постоянным указателем на переменное целое число и

int const * const Constant4

объявляет, что константа4 является постоянным указателем на постоянное целое число. В основном "const" относится к тому, что находится на его непосредственном левом (кроме если в нем нет ничего, в этом случае оно относится ко всему, что является его непосредственным правом).

ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html

7

У меня были те же сомнения, что и вы, пока я не встретил эту книгу от С++ Guru Scott Meyers. См. Третий пункт в этой книге, где он подробно рассказывает об использовании const.

Просто следуйте этому совету

  • Если слово const отображается слева от звездочки, то, на что указывает, является константой
  • Если слово const отображается справа от звездочки, сам указатель является константой
  • Если const появляется с обеих сторон, оба являются постоянными
5

Синтаксис объявления C и С++ неоднократно описывался как неудачный эксперимент, оригинальные дизайнеры.

Вместо этого назовите тип "указатель на Type"; Я назову его Ptr_:

template< class Type >
using Ptr_ = Type*;

Теперь Ptr_<char> является указателем на char.

Ptr_<const char> является указателем на const char.

И const Ptr_<const char> является указателем const для const char.

Там.

Изображение 1194

  • 3
    у вас есть цитата для первого предложения?
  • 0
    @ sp2danny: поиск в Google «эксперимент с синтаксисом С потерпел неудачу» только кашляет во многих интервью с Бьярном Страуструпом, где он выражает свое мнение в этом направлении, например «Я считаю, что синтаксис декларатора C эксперимент не удался» в интервью Slashdot. Таким образом, у меня нет ссылки на утверждение о точках зрения оригинальных дизайнеров C. Я думаю, что это может быть найдено достаточно сильными исследовательскими усилиями или, возможно, опровергнуто, просто спросив их, но я думаю, что это лучше, чем сейчас. с этой частью претензии, все еще не определились и, скорее всего, правда :)
Показать ещё 3 комментария
5

Это просто, но сложно. Обратите внимание, что мы можем поменять квалификатор 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

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

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

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

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

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

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

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

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

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

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

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

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

2

Константа с 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

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

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

Избегайте const_cast < > как чума. Для него есть один или два законных варианта использования, но их очень мало и далеко друг от друга. Если вы пытаетесь изменить объект const, вам будет намного лучше найти того, кто его объявил 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 in param.

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

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

0

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

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

  2. const справа от * указывает на то, что указатель является const указатель.

Следующая таблица взята из Стандартного считывателя курсов по программированию Stanford CS106L Standard C++.

Изображение 1195

Ещё вопросы

Сообщество Overcoder
Наверх
Меню