Я всегда беспорядок , как использовать const int*
, const int * const
и int const *
правильно. Существует ли набор правил, определяющих, что вы можете и не можете делать?
Я хочу знать все, что можно и чего нельзя делать с точки зрения назначений, передачи функций и т. Д.
int *(*)(char const * const)
, Начинают справа от скобки ,*
то мы должны двигаться влево:pointer
. Вне скобок, мы можем двигаться вправо:pointer to function of ...
. Затем мы должны двигаться влевоpointer to function of ... that returns pointer to int
. Повторите , чтобы развернуть параметр (...
):pointer to function of (constant pointer to constant char) that returns pointer to int
. Каким будет эквивалентное однострочное объявление на легкочитаемом языке, таком как Паскаль?function(x:^char):^int
. Там типы функций подразумевают указатель на функцию, поэтому нет необходимости указывать ее, а Паскаль не обеспечивает правильности констант. Это можно прочитать слева направо.Ответы:
Прочитайте это в обратном направлении (как управляется по часовой стрелке / правилу спирали ):
int*
- указатель на intint const *
- указатель на const intint * const
- константный указатель на intint const * const
константный указатель на const intТеперь первый
const
может быть с любой стороны типа так:const int *
==int const *
const int * const
==int const * const
Если вы хотите сойти с ума, вы можете сделать что-то вроде этого:
int **
- указатель на указатель на intint ** const
- константный указатель на указатель на intint * const *
- указатель на константный указатель на intint const **
- указатель на указатель на const intint * const * const
- константный указатель на константный указатель на intИ чтобы убедиться, что мы ясно понимаем значение
const
:foo
переменный указатель на постоянное целое число Это позволяет вам изменить то, на что вы указываете, но не значение, на которое вы указываете. Чаще всего это наблюдается в строках в стиле C, где у вас есть указатель на aconst char
. Вы можете изменить строку, на которую вы указываете, но вы не можете изменить содержимое этих строк. Это важно, когда сама строка находится в сегменте данных программы и не должна изменяться.bar
постоянный или фиксированный указатель на значение, которое можно изменить. Это как ссылка без лишнего синтаксического сахара. Из-за этого обычно вы используете ссылку, в которой вы используетеT* const
указатель, если вам не нужно разрешатьNULL
указатели.источник
const int x = 0; const int *const px = &x; const int *const *const p = &px;
работает просто отлично.Для тех, кто не знает о правиле по часовой стрелке / по спирали: начните с имени переменной, двигайтесь по часовой стрелке (в данном случае назад) к следующему указателю или типу . Повторяйте, пока не закончится выражение.
Вот демо:
источник
void (*signal(int, void (*fp)(int)))(int);
изЯ думаю, что все уже здесь ответили, но я просто хочу добавить, что вы должны остерегаться
typedef
s! Они НЕ просто текстовые замены.Например:
Тип
astring
естьchar * const
, нетconst char *
. Это одна из причин, которую я всегда склоняюconst
справа от шрифта, и никогда в начале.источник
typedef int* PINT
(я предполагаю, что это то, что пришло из практики в C, и многие разработчики продолжали делать это). Отлично, я заменил это*
наP
, это не ускоряет ввод текста, а также представляет проблему, о которой вы упомянули.PINT
- действительно довольно глупое использование typedef, особенно потому, что оно заставляет меня думать, что системные магазины используют пиво для памяти. Однако typedef довольно полезны для работы с указателями на функции.PVOID
,LPTSTR
вещи в Win32 API!Как почти все отметили:
В чем разница
const X* p
,X* const p
аconst X* const p
?источник
const X* p;
==X const * p;
как в"p points to an X that is const": the X object can't be changed via p.
Постоянная ссылка:
Ссылка на переменную (здесь int), которая является постоянной. Мы передаем переменную в основном как ссылку, потому что ссылки меньше по размеру, чем фактическое значение, но есть побочный эффект, и это потому, что это похоже на псевдоним реальной переменной. Мы можем случайно изменить основную переменную через наш полный доступ к псевдониму, поэтому мы устанавливаем ее постоянной, чтобы предотвратить этот побочный эффект.
Постоянные указатели
Как только постоянный указатель указывает на переменную, он не может указывать на любую другую переменную.
Указатель на постоянную
Указатель, с помощью которого нельзя изменить значение переменной, на которую он указывает, называется указателем на константу.
Постоянный указатель на постоянную
Постоянный указатель на константу - это указатель, который не может изменить адрес, на который он указывает, и не может изменить значение, хранящееся по этому адресу.
источник
Общее правило заключается в том, что
const
ключевое слово относится к тому, что ему предшествует. Исключение, началоconst
относится к тому, что следует.const int*
то же, чтоint const*
и означает «указатель на константу int» .const int* const
это то же самое, чтоint const* const
и означает «постоянный указатель на константу int» .Редактировать: Для того, что можно и чего нельзя делать, если этого ответа недостаточно, не могли бы вы быть более точным относительно того, что вы хотите?
источник
Этот вопрос показывает , именно поэтому мне нравится делать вещи так , как я уже упоминал в моем вопросе является сопзЬ после типа ид приемлемым?
Короче говоря, я считаю, что самый простой способ запомнить правило - это то, что «const» идет после того, к чему оно относится. Итак, в вашем вопросе «int const *» означает, что int является константой, а «int * const» будет означать, что указатель является константой.
Если кто-то решит поместить его на передний план (например, «const int *»), в качестве особого исключения в этом случае он применяется к предмету после него.
Многим людям нравится использовать это специальное исключение, потому что они думают, что это выглядит лучше. Мне это не нравится, потому что это исключение, и, таким образом, все путает.
источник
const T*
и это стало более естественным. Как часто вы когда-либо используете вT* const
любом случае, обычно справка будет хорошо. Я получил все это один раз, когда захотелboost::shared_ptr<const T>
и вместо этого написалconst boost::shared_ptr<T>
. Та же проблема в несколько ином контексте.const
является типом того, что является const, а все его права - тем, что на самом деле является const. Взять хотяint const * const * p;
бы пример. Нет, я обычно так не пишу, это всего лишь пример. Первыйconst
: введите int, и int, который является const, является содержимым указателя const, который является содержимымp
. Второй const: type - указатель наconst
int, const oblect - содержимоеp
Простое использование
const
.Самое простое использование - объявить именованную константу. Для этого нужно объявить константу, как если бы она была переменной, но добавить
const
перед ней. Нужно немедленно инициализировать его в конструкторе, потому что, конечно, нельзя установить значение позже, так как это изменило бы его. Например:создаст целочисленную константу, не вызывающую воображения
Constant1
, со значением 96.Такие константы полезны для параметров, которые используются в программе, но их не нужно менять после компиляции программы. Он имеет преимущество для программистов перед командой препроцессора C
#define
в том, что он понимается и используется самим компилятором, а не просто подставляется препроцессором в текст программы до того, как он достигнет основного компилятора, поэтому сообщения об ошибках гораздо более полезны.Он также работает с указателями, но нужно быть осторожным, где
const
определить, является ли указатель или то, на что он указывает, постоянным или и тем, и другим. Например:объявляет, что
Constant2
является указателем переменной на постоянное целое число и:альтернативный синтаксис, который делает то же самое, тогда как
объявляет, что
Constant3
является постоянным указателем на целое число переменной иобъявляет, что
Constant4
является постоянным указателем на постоянное целое число. По существу, «const» относится к тому, что находится непосредственно слева от него (кроме случаев, когда там ничего нет, в этом случае оно применяется к тому, что непосредственно справа от него).ссылка: http://duramecho.com/ComputerInformation/WhyHowCppConst.html
источник
У меня были те же сомнения, что и у вас, пока я не наткнулся на эту книгу Гуру С ++ Скотта Мейерса. Обратитесь к третьему пункту в этой книге, где он подробно рассказывает об использовании
const
.Просто следуйте этому совету
const
появляется слева от звездочки, на что указывает постояннаяconst
появляется справа от звездочки, сам указатель является постояннымconst
появляется с обеих сторон, оба являются постояннымиисточник
Это просто, но сложно. Пожалуйста , обратите внимание , что мы можем поменять
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
здесь только для чтения, объявление и определение должны быть в одном месте.источник
Есть много других тонких моментов, касающихся правильности констант в 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 &
ссылка может свидетельствовать об изменении значения, на которое она ссылается, в другом месте.источник
Синтаксис объявления C и C ++ неоднократно описывался разработчиками как неудачный эксперимент.
Вместо этого давайте называть тип «указатель
Type
»; Я назову этоPtr_
:Теперь
Ptr_<char>
указатель наchar
.Ptr_<const char>
является указателемconst char
.И
const Ptr_<const char>
этоconst
указатель наconst char
.Там.
источник
Для меня позиция,
const
то есть, кажется ли она ЛЕВОЙ или ПРАВОЙ или ЛЕВОЙ и ПРАВОЙ относительно,*
помогает мне понять фактическое значение.A
const
LEFT*
означает, что объект, на который указывает указатель, являетсяconst
объектом.От А
const
до ПРАВА*
указывает, что указатель являетсяconst
указателем.Следующая таблица взята из Stanford CS106L Standard C ++ Programming Laboratory Reader.
источник
В основном это касается второй строки: лучшие практики, назначения, параметры функций и т. Д.
Общая практика. Попробуйте сделать все,
const
что вы можете. Или, говоря иначе, сделайте всеconst
для начала, а затем удалите точно минимальный наборconst
s, необходимый для работы программы. Это будет большим подспорьем в достижении константности и поможет избежать появления незаметных ошибок, когда люди попытаются заняться вещами, которые они не должны изменять.Избегайте const_cast <>, как чума. Есть один или два законных варианта использования, но их очень мало и они далеко друг от друга. Если вы пытаетесь изменить
const
объект, вам будет гораздо лучше найти того, кто объявил об этомconst
в первом темпе, и обсудить этот вопрос с ними, чтобы достичь консенсуса относительно того, что должно произойти.Что приводит очень аккуратно в назначениях. Вы можете назначить что-то, только если это неконстантно. Если вы хотите присвоить что-то, что является постоянным, см. Выше. Помните , что в декларации
int const *foo;
иint * const bar;
разные вещиconst
- другие ответы здесь покрыли этот вопрос превосходно, поэтому я не буду вдаваться в подробности .Параметры функции:
Передача по значению: например,
void func(int param)
вам не важно, так или иначе на вызывающем сайте. Можно привести аргумент, что существуют варианты использования для объявления функции как,void func(int const param)
но это не влияет на вызывающего, только на саму функцию, в том смысле, что любое переданное значение не может быть изменено функцией во время вызова.Передайте по ссылке: например,
void func(int ¶m)
теперь это имеет значение. Как только что заявленоfunc
, разрешено изменятьparam
, и любой вызывающий сайт должен быть готов справиться с последствиями. Изменение декларации, чтобыvoid func(int const ¶m)
изменить контракт, и гарантии, которыеfunc
теперь не могут изменитьсяparam
, то есть то, что передано, то, что вернется. Как уже отмечали другие, это очень полезно для дешевой передачи большого объекта, который вы не хотите менять. Передача ссылки намного дешевле, чем передача большого объекта по значению.Проходит указатель: например ,
void func(int *param)
иvoid func(int const *param)
эти два довольно много синонимов их позиционные коллегами, с той оговоркой , что вызываемая функция в настоящее время необходимо , чтобы проверить ,nullptr
если некоторые другие договорные гарантии уверяют ,func
что он никогда не получитnullptr
вparam
.Часть мнения на эту тему. Доказать правильность в таком случае адски сложно, просто чертовски легко ошибиться. Так что не рискуйте, и всегда проверяйте параметры указателя
nullptr
. Вы избавите себя от боли и страданий, и вам будет трудно найти ошибки в долгосрочной перспективе. А что касается стоимости проверки, то она очень дешевая, и в случаях, когда статический анализ, встроенный в компилятор, может управлять им, оптимизатор все равно ее исключит. Включите Link Time Generation Generation для MSVC или WOPR (я думаю) для GCC, и вы получите всю программу, то есть даже в вызовах функций, которые пересекают границу модуля исходного кода.В конце концов, все вышеперечисленное дает веские основания всегда отдавать предпочтение ссылкам на указатели. Они просто безопаснее со всех сторон.
источник
Константа с int с обеих сторон сделает указатель на константу int :
или:
const
после*
сделает постоянный указатель на int :В этом случае все они являются указателями на постоянное целое число , но ни один из них не является постоянным указателем:
В этом случае все являются указателями на постоянное целое число, а ptr2 является постоянным указателем на постоянное целое число . Но ptr1 не является постоянным указателем:
источник
const
это слева от*
, оно относится к значению (это не имеет значения , является ли этоconst int
илиint const
)const
это справа от*
, это относится к самому указателюВажный момент:
const int *p
не означает, что значение, на которое вы ссылаетесь, является постоянным! , Это означает, что вы не можете изменить его через этот указатель (то есть вы не можете назначить $ * p = ... `). Само значение может быть изменено другими способами. НапримерЭто предназначено для использования в основном в сигнатурах функций, чтобы гарантировать, что функция не может случайно изменить переданные аргументы.
источник
Просто ради полноты для C, следуя другим объяснениям, не уверен для C ++.
x
Указатель
int *p;
int const *p;
int * const p;
int const * const p;
Указатель на указатель
int **pp;
int ** const pp;
int * const *pp;
int const **pp;
int * const * const pp;
int const ** const pp;
int const * const *pp;
int const * const * const pp;
N-уровни разыменования
Просто продолжай, но пусть человечество отлучит тебя от церкви.
источник
const int*
указатель на постоянныйint
объект.Вы можете изменить значение указателя; Вы не можете изменить значение
int
объекта, на который указывает указатель.const int * const
постоянный указатель на постоянныйint
объект.Вы не можете изменить значение указателя или значение
int
объекта, на который указывает указатель.int const *
указатель на постоянныйint
объект.Это утверждение эквивалентно 1.
const int*
- Вы можете изменить значение указателя, но вы не можете изменить значениеint
объекта, на который указывает указатель.На самом деле, есть 4-й вариант:
int * const
постоянный указатель наint
объект.Вы можете изменить значение объекта, на который указывает указатель, но вы не можете изменить значение самого указателя. Указатель всегда будет указывать на один и тот же
int
объект, но это значение этогоint
объекта можно изменить.Если вы хотите определить определенный тип конструкции C или C ++, вы можете использовать Правило по часовой стрелке / спирали, разработанное Дэвидом Андерсоном; но не путать с правилом Андерсона, сформулированным Россом Дж. Андерсоном, который является чем-то совершенно отличным.
источник