Я не говорю об указателях на константные значения, а о самих константных указателях.
Я изучаю C и C ++ за пределами самых базовых вещей, и до сегодняшнего дня я понял, что указатели передаются по значению функциям, что имеет смысл. Это означает, что внутри функции я могу заставить скопированный указатель указывать на какое-то другое значение, не влияя на исходный указатель от вызывающей стороны.
Так какой смысл иметь заголовок функции, который говорит:
void foo(int* const ptr);
Внутри такой функции вы не можете заставить ptr указывать на что-то другое, потому что это const, и вы не хотите, чтобы это было изменено, но функция как эта:
void foo(int* ptr);
Работает ли так же хорошо! потому что указатель копируется в любом случае, и указатель в вызывающей стороне не затрагивается, даже если вы измените копию. Так в чем же преимущество const?
const
параметр.const
гарантий -корректности. Это просто дает нам уверенность в том, что наш код, несомненно, правильный.Ответы:
const
это инструмент, который вы должны использовать для реализации очень важной концепции C ++:Даже если это не меняет функциональность, добавление
const
генерирует ошибку компилятора, когда вы делаете то, что не хотели делать. Представьте себе следующую опечатку:Если вы используете
int* const
, это приведет к ошибке компилятора, потому что вы меняете значение наptr
. Добавление ограничений через синтаксис - это вообще хорошо. Только не принимайте это слишком далеко - пример, который вы привели, - это случай, когда большинство людей не удосуживаются использоватьconst
.источник
Я хочу использовать только
const
аргументы, потому что это позволяет проводить больше проверок компилятором: если я случайно переназначу значение аргумента внутри функции, компилятор укусит меня.Я редко использую переменные, чистее создавать новые переменные для хранения новых значений, поэтому, по сути, все мои объявления переменных
const
(за исключением некоторых случаев, таких как переменные цикла, гдеconst
может помешать работе кода).Обратите внимание, что это имеет смысл только в определении функции. Он не принадлежит объявлению , которое видит пользователь. И пользователю все равно, использую ли я
const
для параметров внутри функции.Пример:
Обратите внимание, как аргумент и локальная переменная
const
. Это не является необходимым, но с функциями, которые даже немного больше, это неоднократно спасало меня от ошибок.источник
+1
от другогоconst
одержимого фанатика. Однако я предпочитаю, чтобы мои компиляторы лаяли на меня. Я делаю слишком много ошибок и буду сильно страдать, если они будут кусаться.const
стратегию " если нет веских причин". Однако есть несколько хороших исключений, например, Копирование и обменconst
аргумент from и, предпочтительно, прокомментируйте почему. Эта крошечная дополнительная работа не оправдывает пометку всех аргументов неconst
по умолчанию и раскрытие всех потенциальных ошибок, которые возникают.Ваш вопрос затрагивает нечто более общее: должны ли аргументы функции быть константными?
Константность значений аргументов (например, вашего указателя) - это деталь реализации , и она не является частью объявления функции. Это означает, что ваша функция всегда такова:
Это полностью до реализатора функции хочет ли она использовать переменный аргумент функции-Scope в изменяемом или в постоянном образом:
Итак, следуйте простому правилу, чтобы никогда не вставлять
const
объявление (заголовок), и вставьте его в определение (реализацию), если вы не хотите или не хотите изменять переменную.источник
const
объявления и (части прототипа) определения, как правило, будут идентичны.const
в декларации. Это полностью зависит от вас, если вы хотите добавить квалификатор в реализации.const
декларацию, а не определение имеет смысл. В языке просто ощущается сбой, что это единственный случай, когда имеет смысл сделать объявление и определение не идентичными. (Это вряд ли единственный глюк Си, конечно.)Спецификатор const верхнего уровня отбрасывается в объявлениях, поэтому объявления в вопросе объявляют точно такую же функцию. С другой стороны, в определении (реализации) компилятор проверит, что если вы помечаете указатель как const, он не изменяется внутри тела функции.
источник
int* t ptr
это синтаксическая ошибка Без этого они идентичны для целей перегрузки.Вы правы, для звонящего это не имеет абсолютно никакого значения. Но для автора функции это может быть сеть безопасности: «Хорошо, мне нужно убедиться, что я не обращаю внимание на неправильную вещь». Не очень полезно, но не бесполезно.
Это в основном то же самое, что иметь
int const the_answer = 42
в вашей программе.источник
const int
иint const
эквивалентны, в то время какconst int*
иint* const
имеют два разных значения!int const
части; Я помещал тип перед const (это звучит неестественно) в течение некоторого времени, и я знаю о различиях. Эта статья может оказаться полезной. У меня самого были несколько иные причины перейти на этот стиль.Ключевого слова много
const
, оно довольно сложное. Как правило, добавление большого количества const в вашу программу считается хорошей практикой программирования, поищите в Интернете «правильность const», и вы найдете много информации об этом.Ключевое слово const является так называемым «определителем типа», другие -
volatile
иrestrict
. По крайней мере volatile следует тем же (сбивающим с толку) правилам, что и const.Прежде всего, ключевое слово const служит двум целям. Наиболее очевидным является защита данных (и указателей) от преднамеренного или случайного неправильного использования, делая их доступными только для чтения. Любая попытка изменить переменную const будет обнаружена компилятором во время компиляции.
Но в любой системе, имеющей постоянную память, есть и другая цель - обеспечить, чтобы в такой памяти выделялась определенная переменная - например, EEPROM или flash. Они известны как энергонезависимые воспоминания, NVM. Переменная, размещенная в NVM, будет, конечно, следовать всем правилам переменной const.
Есть несколько разных способов использования
const
ключевого слова:Объявите постоянную переменную.
Это можно сделать как
Эти две формы полностью эквивалентны . Последний стиль считается плохим стилем и не должен использоваться.
Причина, по которой вторая строка считается плохим стилем, заключается, вероятно, в том, что «спецификаторы класса хранения», такие как static и extern, также могут быть объявлены после фактического типа и
int static
т. Д. Но выполнение этого для спецификаторов класса хранения помечается как устаревшая функция. Комитетом C (проект ISO 9899 N1539, 6.11.5). Следовательно, для согласованности не следует также писать классификаторы типов таким способом. Это не служит никакой другой цели, кроме как запутать читателя так или иначе.Объявите указатель на постоянную переменную.
Это означает, что содержимое «X» не может быть изменено. Это обычный способ объявления указателей подобным образом, в основном как часть параметров функции для «правильности констант». Поскольку 'X' на самом деле не нужно объявлять как const, это может быть любая переменная. Другими словами, вы всегда можете «обновить» переменную до const. Технически, C также позволяет понизить const до простой переменной с помощью явных типов, но это считается плохим программированием, и компиляторы обычно выдают предупреждения против него.
Объявить постоянный указатель
Это означает , что указатель сам является постоянным. Вы можете изменить то, на что он указывает, но вы не можете изменить сам указатель. Это не имеет много применений, есть несколько, например, обеспечение того, чтобы указатель-указатель (указатель-на-указатель) не менял свой адрес при передаче в качестве параметра функции. Вам придется написать что-то не слишком читабельное, как это:
Я сомневаюсь, что многие программисты на C могут получить const и * прямо там. Я знаю, что не могу - я должен был проверить с GCC. Я думаю, именно поэтому вы редко когда-либо видите такой синтаксис для указателя на указатель, хотя это считается хорошей практикой программирования.
Также можно использовать константные указатели, чтобы гарантировать, что сама переменная указателя объявлена в постоянной памяти, например, вы можете объявить какую-то таблицу поиска на основе указателей и выделить ее в NVM.
И, конечно же, как указывают другие ответы, константные указатели также могут использоваться для обеспечения «правильности констант».
Объявите постоянный указатель на постоянные данные
Это два описанных выше типа указателей, со всеми атрибутами обоих.
Объявить функцию-член только для чтения (C ++)
Поскольку это тег C ++, я должен также упомянуть, что вы можете объявить функции-члены класса как const. Это означает, что функция не имеет права изменять любой другой член класса при вызове, что одновременно предотвращает случайные ошибки программиста класса, но также сообщает вызывающей функции-члену, что они не будут ничего портить позвонив по телефону Синтаксис:
источник
(IMO) это действительно не имеет смысла по умолчанию. более разумное значение по умолчанию - передать как не переназначаемый указатель (
int* const arg
). то есть я бы предпочел, чтобы указатели, передаваемые в качестве аргументов, были неявно объявлены как const.Преимущество состоит в том, что это достаточно просто и иногда неясно, когда вы изменяете адрес, на который указывает аргумент, так что вы можете внести ошибку, когда она не достаточно проста. изменение адреса нетипично. проще создать локальную переменную, если вы хотите изменить адрес. Кроме того, необработанные манипуляции с указателями - это простой способ введения ошибок.
так что проще перейти по неизменяемому адресу и создать копию (в этих нетипичных случаях), когда вы хотите изменить адрес, на который указывает аргумент:
добавив, что local практически бесплатен, он снижает вероятность ошибок и улучшает читаемость.
на более высоком уровне: если вы манипулируете аргументом в виде массива, клиенту обычно проще и менее подвержено ошибкам объявлять аргумент как контейнер / коллекцию.
в общем, добавление const к значениям, аргументам и адресам - хорошая идея, потому что вы не всегда понимаете побочные эффекты, которые компилятор успешно применяет. следовательно, это так же полезно, как const, как и в других нескольких других случаях (например, вопрос похож на «Почему я должен объявлять значения const?»). К счастью, у нас также есть ссылки, которые не могут быть переназначены.
источник
const
ключевого слова у него должно бытьmutable
ключевое слово (ну, у него есть, но с неверной семантикой).Если вы выполняете программирование встроенных систем или драйверов устройств, когда у вас есть устройства с отображенной памятью, то часто используются обе формы «const», одна для предотвращения переназначения указателя (так как он указывает на фиксированный аппаратный адрес.) И, если периферийное устройство регистр, на который он указывает, является аппаратным регистром только для чтения, тогда другой const обнаружит множество ошибок во время компиляции, а не во время выполнения.
16-битный регистр микросхемы только для чтения может выглядеть примерно так:
static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;
Тогда вы можете легко прочитать аппаратный регистр, не прибегая к языку ассемблера:
input_word = *peripheral;
источник
int iVal = 10; int * const ipPtr = & iVal;
Как и обычная переменная const, указатель const должен быть инициализирован значением после объявления, и его значение нельзя изменить.
Это означает, что указатель const всегда будет указывать на одно и то же значение. В приведенном выше случае ipPtr всегда будет указывать на адрес iVal. Однако, поскольку значение, на которое указывает указатель, все еще не является константным, можно изменить значение, на которое указывает указатель, путем разыменования указателя:
* ipPtr = 6; // разрешено, так как pnPtr указывает на неконстантный int
источник
Тот же вопрос можно задать для любого другого типа (не только указатели):
источник
Ваш вопрос действительно больше о том, почему определять любую переменную как const, а не просто параметр const-указателя на функцию. Здесь применяются те же правила, что и при определении любой переменной как константы, если она является параметром для функции или переменной-члена или локальной переменной.
В вашем конкретном случае функционально это не имеет значения, как во многих других случаях, когда вы объявляете локальную переменную как const, но накладывает ограничение на невозможность ее изменения.
источник
Передача const-указателя на функцию не имеет большого смысла, так как она все равно будет передаваться по значению. Это просто одна из тех вещей, которые допускаются общим языковым дизайном. Запрещение этого только потому, что это не имеет смысла, просто сделало бы спецификацию языка. больше.
Если вы находитесь внутри функции, это, конечно, другой случай. Наличие указателя, который не может изменить то, на что он указывает, является утверждением, которое делает код более понятным.
источник
Я предполагаю преимущество в том, что компилятор может выполнять более агрессивные оптимизации внутри функции, зная, что этот указатель не может измениться.
Это также позволяет избежать, например. передача этого указателя в подфункцию, которая принимает неконстантную ссылку на указатель (и, следовательно, может изменить указатель как
void f(int *&p)
), но я согласен, что полезность в этом случае несколько ограничена.источник
Пример того, где указатель const очень применим, может быть продемонстрирован таким образом. Предположим, у вас есть класс с динамическим массивом внутри, и вы хотите передать пользователю доступ к массиву, но не предоставляя им права на изменение указателя. Рассматривать:
Который производит:
Но если мы попробуем это:
Мы получили:
Ясно, что мы можем изменить содержимое массива, но не указатель массива. Хорошо, если вы хотите убедиться, что указатель имеет согласованное состояние при передаче его обратно пользователю. Но есть одна загвоздка:
Мы все еще можем удалить ссылку памяти указателя, даже если мы не можем изменить сам указатель.
Поэтому, если вы хотите, чтобы ссылка на память всегда указывала на что-то (IE никогда не изменялся, подобно тому, как ссылка работает в настоящее время), тогда она очень применима. Если вы хотите, чтобы пользователь имел полный доступ и изменил его, тогда non-const для вас.
Редактировать:
Обратите внимание на комментарий okorz001 о невозможности назначения из-за того, что GetArray () является операндом с правильным значением, его комментарий полностью верен, но вышеприведенное применимо, если вы должны вернуть ссылку на указатель (я полагаю, я предполагал, что GetArray был ссылаясь на ссылку), например:
Вернется в первом, что приведет к ошибке:
Но второе произойдет весело, несмотря на потенциальные последствия на дне.
Очевидно, возникнет вопрос: «Почему вы хотите вернуть ссылку на указатель»? В редких случаях вам нужно назначить память (или данные) непосредственно исходному указателю (например, создать свой собственный malloc / free или new / free front-end), но в этих случаях это неконстантная ссылка , Ссылка на константный указатель Я не сталкивался с ситуацией, которая оправдывала бы это (разве может быть, как объявленные константные ссылочные переменные, а не возвращаемые типы?).
Рассмотрим, есть ли у нас функция, которая принимает константный указатель (по сравнению с той, которая этого не делает):
Ошибка в const выдает таким образом сообщение:
Это хорошо, так как мы, вероятно, не хотим этого делать, если только мы не хотим вызывать проблемы, обозначенные в комментариях. Если мы отредактируем декремент в функции const, произойдет следующее:
Ясно, что, хотя A - это «Data [1]», он обрабатывается как «Data [0]», потому что указатель NonConst разрешил операцию декремента. Когда const реализован, как пишет другой человек, мы обнаруживаем потенциальную ошибку до ее появления.
Еще одно основное соображение заключается в том, что указатель const может использоваться в качестве псевдо-ссылки, поскольку вещь, на которую указывает ссылка, не может быть изменена (интересно, возможно, именно так и было реализовано). Рассматривать:
При попытке компиляции выдает следующую ошибку:
Что, вероятно, плохо, если была нужна постоянная ссылка на А. Если
B = NULL
это закомментировано, компилятор с радостью разрешит нам изменить*B
и, следовательно, A. Это может показаться бесполезным для int, но подумайте, если у вас была единственная позиция графического приложения, где вы хотели использовать неизменяемый указатель, ссылающийся на него, который вы могли бы передать вокруг.Его использование является переменным (извините за непреднамеренное каламбур), но используется правильно, это еще один инструмент в коробке, чтобы помочь с программированием.
источник
Temp.GetArray() = NULL
терпит неудачу, потому чтоTemp.GetArray()
это значение, а не потому, что оно является const-квалифицированным. Кроме того, я считаю, что const-qualifier исключен из всех типов возвращаемых данных.Нет ничего особенного в указателях, где вы бы никогда не хотели, чтобы они были постоянными. Точно так же, как вы можете иметь постоянные
int
значения членов класса , вы также можете иметь постоянные указатели по аналогичным причинам: вы хотите убедиться, что никто никогда не изменит то, на что указывают. Ссылки на C ++ несколько решают эту проблему, но поведение указателя унаследовано от C.источник
Я считаю, что это помешает коду увеличить или уменьшить указатель в теле функции.
источник
Типы объявления любых переменных, например:
(1) Объявление постоянной переменной.
DataType const varibleName;
const dataType* PointerVaribleName=&X;
dataType* const PointerVaribleName=&X;
источник