Какой смысл в константных указателях?

149

Я не говорю об указателях на константные значения, а о самих константных указателях.

Я изучаю C и C ++ за пределами самых базовых вещей, и до сегодняшнего дня я понял, что указатели передаются по значению функциям, что имеет смысл. Это означает, что внутри функции я могу заставить скопированный указатель указывать на какое-то другое значение, не влияя на исходный указатель от вызывающей стороны.

Так какой смысл иметь заголовок функции, который говорит:

void foo(int* const ptr);

Внутри такой функции вы не можете заставить ptr указывать на что-то другое, потому что это const, и вы не хотите, чтобы это было изменено, но функция как эта:

void foo(int* ptr);

Работает ли так же хорошо! потому что указатель копируется в любом случае, и указатель в вызывающей стороне не затрагивается, даже если вы измените копию. Так в чем же преимущество const?

Р. Руис.
источник
29
Что если вы хотите гарантировать во время компиляции, что указатель не может и не должен быть изменен, чтобы указывать на что-то еще?
Платиновая Лазурь
25
Точно такая же точка, как и любой constпараметр.
Дэвид Хеффернан
2
@PlatinumAzure, у меня есть несколько лет опыта в программировании, но мне не хватает моих программных практик. Я рад, что задал этот вопрос, потому что я никогда не чувствовал необходимости заставлять компилятор указывать на мои собственные логические ошибки. В тот момент, когда я отправил вопрос, моей первой реакцией было «почему меня должно волновать изменение указателя? Это не повлияет на вызывающего». Пока я не прочитал все ответы, я понимал, что должен заботиться, потому что, если я пытаюсь изменить его, я и моя логика кодирования ошибочны (или была опечатка, например, пропущенная звездочка), и я мог бы не осознавать, что компилятор не сделал ' не говори мне.
Р. Руис.
3
@ R.Ruiz. Несомненно, даже самые опытные из нас могут сделать с дополнительными гарантиями правильности. Поскольку разработка программного обеспечения является формой разработки, при которой допускается немного больше возможностей (случайные HTTP 502, медленные соединения, случайные неудачные загрузки изображения - не исключительные случаи, но отказ двигателя в самолете недопустим и, вероятно, серьезен), иногда люди программируют с чрезмерной поспешностью. Тот же самый аргумент, который оправдывает написание модульных тестов, объяснит использование constгарантий -корректности. Это просто дает нам уверенность в том, что наш код, несомненно, правильный.
Платиновая Лазурь
3
Связанный вопрос: stackoverflow.com/questions/219914/…
Raedwald

Ответы:

207

const это инструмент, который вы должны использовать для реализации очень важной концепции C ++:

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

Даже если это не меняет функциональность, добавление constгенерирует ошибку компилятора, когда вы делаете то, что не хотели делать. Представьте себе следующую опечатку:

void foo(int* ptr)
{
    ptr = 0;// oops, I meant *ptr = 0
}

Если вы используете int* const, это приведет к ошибке компилятора, потому что вы меняете значение на ptr. Добавление ограничений через синтаксис - это вообще хорошо. Только не принимайте это слишком далеко - пример, который вы привели, - это случай, когда большинство людей не удосуживаются использовать const.

tenfour
источник
8
Спасибо, это ответ, который убеждает меня. Вы ставите const так, чтобы компилятор предупреждал вас о собственных ошибках присваивания. Ваш пример идеально подходит для иллюстрации этой концепции, потому что это распространенная ошибка с указателями. Чем ты!
Р. Руис.
25
«Помогите компилятору помочь вам» - мантра, которую я обычно пою для этого.
Флексо
1
+1, но вот интересный случай, когда это можно поспорить: stackoverflow.com/questions/6305906/…
Raedwald
3
так что это тот же ответ, который вы бы дали: «Зачем делать переменные частными, если вы просто не можете использовать их вне класса».
Ли Лувьер
это может быть немного не по теме, но где этот константный указатель находится в памяти? Я знаю, что все const находятся в глобальной части памяти, но я не на 100% уверен в том, что const передается как func var. спасибо и извините, если это не по теме
Tomer
77

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

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

Обратите внимание, что это имеет смысл только в определении функции. Он не принадлежит объявлению , которое видит пользователь. И пользователю все равно, использую ли я constдля параметров внутри функции.

Пример:

// foo.h
int frob(int x);
// foo.cpp
int frob(int const x) {
   MyConfigType const config = get_the_config();
   return x * config.scaling;
}

Обратите внимание, как аргумент и локальная переменная const. Это не является необходимым, но с функциями, которые даже немного больше, это неоднократно спасало меня от ошибок.

Конрад Рудольф
источник
17
+1от другого constодержимого фанатика. Однако я предпочитаю, чтобы мои компиляторы лаяли на меня. Я делаю слишком много ошибок и буду сильно страдать, если они будут кусаться.
ВОО
2
+1, я настоятельно рекомендую constстратегию " если нет веских причин". Однако есть несколько хороших исключений, например, Копирование и обмен
Flexo
2
Если бы я проектировал новый язык, объявленные объекты были бы только для чтения ("const") по умолчанию. Вам понадобится специальный синтаксис, возможно, ключевое слово «var», чтобы сделать объект доступным для записи.
Кит Томпсон
@ Мне хочется сказать «конечно». Все остальное глупо в этой точке. Но , к сожалению , большинство дизайнеров языка , кажется, не согласны с нами ... На самом деле, я уже писал , как много (в настоящее удаляемого вопрос «Каково ваше самое спорное мнение программирования?»).
Конрад Рудольф
1
@KubaOber Я вообще не понимаю, как это пессимизация. Если позже вы обнаружите, что вам нужно изменить то, что было передано в теле функции, просто отбросьте constаргумент from и, предпочтительно, прокомментируйте почему. Эта крошечная дополнительная работа не оправдывает пометку всех аргументов не constпо умолчанию и раскрытие всех потенциальных ошибок, которые возникают.
underscore_d
20

Ваш вопрос затрагивает нечто более общее: должны ли аргументы функции быть константными?

Константность значений аргументов (например, вашего указателя) - это деталь реализации , и она не является частью объявления функции. Это означает, что ваша функция всегда такова:

void foo(T);

Это полностью до реализатора функции хочет ли она использовать переменный аргумент функции-Scope в изменяемом или в постоянном образом:

// implementation 1
void foo(T const x)
{
  // I won't touch x
  T y = x;
  // ...
}

// implementation 2
void foo(T x)
{
  // l33t coding skillz
  while (*x-- = zap()) { /* ... */ }
}

Итак, следуйте простому правилу, чтобы никогда не вставлять constобъявление (заголовок), и вставьте его в определение (реализацию), если вы не хотите или не хотите изменять переменную.

Керрек С.Б.
источник
5
Я согласен с этим - но мне несколько неловко от идеи сделать декларацию и определение другими. Для других вещей, кроме constобъявления и (части прототипа) определения, как правило, будут идентичны.
Кит Томпсон
@KeithThompson: Ну, тебе не обязательно делать это, если ты не хочешь. Просто не указывайте constв декларации. Это полностью зависит от вас, если вы хотите добавить квалификатор в реализации.
Kerrek SB
1
Как я уже сказал, я согласен, что ставить constдекларацию, а не определение имеет смысл. В языке просто ощущается сбой, что это единственный случай, когда имеет смысл сделать объявление и определение не идентичными. (Это вряд ли единственный глюк Си, конечно.)
Кит Томпсон
16

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

Дэвид Родригес - дрибеи
источник
Я этого не знал. Поэтому, если я попытаюсь перегрузить void foo (int * const ptr) void foo (int * t ptr), я получу ошибку компилятора. Спасибо!
Р. Руис.
@ R.Ruiz. Если ваше замедление void foo (int * t ptr) и ваше определение void foo (int * const ptr), вы НЕ получите ошибку компилятора.
Тревор Хики
1
@TrevorHickey Они будут ... но не по той причине, по которой они думают. int* t ptrэто синтаксическая ошибка Без этого они идентичны для целей перегрузки.
underscore_d
14

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

Это в основном то же самое, что иметь int const the_answer = 42в вашей программе.

cnicutar
источник
1
Какое это имеет значение, куда они указывают, это указатель, размещенный в стеке? Функция не может испортить это. Гораздо больше смысла использовать указатели только для чтения при работе с указателями на указатели. Это не то же самое, что int const! Я буду опускать это только за это вводящее в заблуждение утверждение. const intи int constэквивалентны, в то время как const int*и int* constимеют два разных значения!
Лундин
1
@lundin Давайте предположим, что функция большая и в ней есть что-то другое. Случайно можно указать на что-то другое (в стеке функции). Это не проблема для вызывающего абонента, но, безусловно, может быть для вызываемого абонента. Я не вижу в своих высказываниях ничего вводящего в заблуждение: « для автора функции это может быть сеть безопасности».
cnicutar 10.10.11
1
@Lundin о int constчасти; Я помещал тип перед const (это звучит неестественно) в течение некоторого времени, и я знаю о различиях. Эта статья может оказаться полезной. У меня самого были несколько иные причины перейти на этот стиль.
cnicutar 10.10.11
1
Ладно. Я просто написал свой многословный ответ на тот случай, если кто-то, кроме вас и меня, читает этот пост. Я также включил обоснование, почему int const - это плохой стиль, а const int - это хороший стиль.
Лундин
Лундин, я тоже читаю! Я согласен, что const int - лучший стиль. @cnicutar, твой первый ответ на Лундина - это то, что я искал, когда задавал этот вопрос. Таким образом, проблема не в вызывающем абоненте (как я думал бездумно), но const является гарантией для вызываемого абонента. Мне нравится эта идея, спасибо.
Р. Руис.
14

Ключевого слова много const, оно довольно сложное. Как правило, добавление большого количества const в вашу программу считается хорошей практикой программирования, поищите в Интернете «правильность const», и вы найдете много информации об этом.

Ключевое слово const является так называемым «определителем типа», другие - volatileи restrict. По крайней мере volatile следует тем же (сбивающим с толку) правилам, что и const.


Прежде всего, ключевое слово const служит двум целям. Наиболее очевидным является защита данных (и указателей) от преднамеренного или случайного неправильного использования, делая их доступными только для чтения. Любая попытка изменить переменную const будет обнаружена компилятором во время компиляции.

Но в любой системе, имеющей постоянную память, есть и другая цель - обеспечить, чтобы в такой памяти выделялась определенная переменная - например, EEPROM или flash. Они известны как энергонезависимые воспоминания, NVM. Переменная, размещенная в NVM, будет, конечно, следовать всем правилам переменной const.

Есть несколько разных способов использования constключевого слова:

Объявите постоянную переменную.

Это можно сделать как

const int X=1; or
int const X=1;

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

Причина, по которой вторая строка считается плохим стилем, заключается, вероятно, в том, что «спецификаторы класса хранения», такие как static и extern, также могут быть объявлены после фактического типа и int staticт. Д. Но выполнение этого для спецификаторов класса хранения помечается как устаревшая функция. Комитетом C (проект ISO 9899 N1539, 6.11.5). Следовательно, для согласованности не следует также писать классификаторы типов таким способом. Это не служит никакой другой цели, кроме как запутать читателя так или иначе.

Объявите указатель на постоянную переменную.

const int* ptr = &X;

Это означает, что содержимое «X» не может быть изменено. Это обычный способ объявления указателей подобным образом, в основном как часть параметров функции для «правильности констант». Поскольку 'X' на самом деле не нужно объявлять как const, это может быть любая переменная. Другими словами, вы всегда можете «обновить» переменную до const. Технически, C также позволяет понизить const до простой переменной с помощью явных типов, но это считается плохим программированием, и компиляторы обычно выдают предупреждения против него.

Объявить постоянный указатель

int* const ptr = &X;

Это означает , что указатель сам является постоянным. Вы можете изменить то, на что он указывает, но вы не можете изменить сам указатель. Это не имеет много применений, есть несколько, например, обеспечение того, чтобы указатель-указатель (указатель-на-указатель) не менял свой адрес при передаче в качестве параметра функции. Вам придется написать что-то не слишком читабельное, как это:

void func (int*const* ptrptr)

Я сомневаюсь, что многие программисты на C могут получить const и * прямо там. Я знаю, что не могу - я должен был проверить с GCC. Я думаю, именно поэтому вы редко когда-либо видите такой синтаксис для указателя на указатель, хотя это считается хорошей практикой программирования.

Также можно использовать константные указатели, чтобы гарантировать, что сама переменная указателя объявлена ​​в постоянной памяти, например, вы можете объявить какую-то таблицу поиска на основе указателей и выделить ее в NVM.

И, конечно же, как указывают другие ответы, константные указатели также могут использоваться для обеспечения «правильности констант».

Объявите постоянный указатель на постоянные данные

const int* const ptr=&X;

Это два описанных выше типа указателей, со всеми атрибутами обоих.

Объявить функцию-член только для чтения (C ++)

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

void MyClass::func (void) const;
Лундин
источник
8

... сегодня я понял, что по значению функции передают указатели, что имеет смысл.

(IMO) это действительно не имеет смысла по умолчанию. более разумное значение по умолчанию - передать как не переназначаемый указатель ( int* const arg). то есть я бы предпочел, чтобы указатели, передаваемые в качестве аргументов, были неявно объявлены как const.

Так в чем же преимущество const?

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

так что проще перейти по неизменяемому адресу и создать копию (в этих нетипичных случаях), когда вы хотите изменить адрес, на который указывает аргумент:

void func(int* const arg) {
    int* a(arg);
    ...
    *a++ = value;
}

добавив, что local практически бесплатен, он снижает вероятность ошибок и улучшает читаемость.

на более высоком уровне: если вы манипулируете аргументом в виде массива, клиенту обычно проще и менее подвержено ошибкам объявлять аргумент как контейнер / коллекцию.

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

джастин
источник
4
+1 для этого по умолчанию. В C ++, как и в большинстве языков, все неправильно. Вместо constключевого слова у него должно быть mutableключевое слово (ну, у него есть, но с неверной семантикой).
Конрад Рудольф
2
Интересная идея, имеющая const по умолчанию. Спасибо за Ваш ответ!
Р. Руис.
6

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

16-битный регистр микросхемы только для чтения может выглядеть примерно так:

static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;

Тогда вы можете легко прочитать аппаратный регистр, не прибегая к языку ассемблера:

input_word = *peripheral;

Дэн Хейнс
источник
5

int iVal = 10; int * const ipPtr = & iVal;

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

Это означает, что указатель const всегда будет указывать на одно и то же значение. В приведенном выше случае ipPtr всегда будет указывать на адрес iVal. Однако, поскольку значение, на которое указывает указатель, все еще не является константным, можно изменить значение, на которое указывает указатель, путем разыменования указателя:

* ipPtr = 6; // разрешено, так как pnPtr указывает на неконстантный int

jusathr
источник
5

Тот же вопрос можно задать для любого другого типа (не только указатели):

/* Why is n const? */
const char *expand(const int n) {
    if (n == 1) return "one";
    if (n == 2) return "two";
    if (n == 3) return "three";
    return "many";
}
PMG
источник
LOL, вы правы. Случай с указателями пришел мне в голову, потому что я думал, что они особенные, но они похожи на любую другую переменную, передаваемую по значению.
Р. Руис.
5

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

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

зар
источник
Ваш комментарий заставляет понять, насколько бесполезен мой вопрос, потому что вы правы: это то же самое, что иметь любой другой параметр как const. Это может показаться бесполезным, но оно поможет программисту преодолеть это ограничение и избежать ошибок
Р. Руиз.
4

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

Если вы находитесь внутри функции, это, конечно, другой случай. Наличие указателя, который не может изменить то, на что он указывает, является утверждением, которое делает код более понятным.

Андерс Абель
источник
4

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

Это также позволяет избежать, например. передача этого указателя в подфункцию, которая принимает неконстантную ссылку на указатель (и, следовательно, может изменить указатель как void f(int *&p)), но я согласен, что полезность в этом случае несколько ограничена.

MartinStettner
источник
+1 за оптимизацию. По крайней мере, книги говорят, что это правда. Я хотел бы понять, что это за оптимизация
Р. Руис.
4

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

#include <new>
#include <string.h>

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const GetArray(){ return Array; }
};

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
    Temp.GetArray()[1] = ' '; 
    printf("%s\n",Temp.GetArray());
}

Который производит:

Входные данные
положить данные

Но если мы попробуем это:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null
}

Мы получили:

error: lvalue требуется в качестве левого операнда присваивания // Drat снова сорвался!

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

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    delete [] Temp.GetArray(); //Bwuahaha this actually works!
}

Мы все еще можем удалить ссылку памяти указателя, даже если мы не можем изменить сам указатель.

Поэтому, если вы хотите, чтобы ссылка на память всегда указывала на что-то (IE никогда не изменялся, подобно тому, как ссылка работает в настоящее время), тогда она очень применима. Если вы хотите, чтобы пользователь имел полный доступ и изменил его, тогда non-const для вас.

Редактировать:

Обратите внимание на комментарий okorz001 о невозможности назначения из-за того, что GetArray () является операндом с правильным значением, его комментарий полностью верен, но вышеприведенное применимо, если вы должны вернуть ссылку на указатель (я полагаю, я предполагал, что GetArray был ссылаясь на ссылку), например:

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; } //Note & reference operator
        char * &GetNonConstArray(){ return Array; } //Note non-const
};

int main()
{
    TestA Temp;
    Temp.GetArray() = NULL; //Returns error
    Temp.GetNonConstArray() = NULL; //Returns no error
}

Вернется в первом, что приведет к ошибке:

ошибка: назначение места только для чтения 'Temp.TestA :: GetArray ()'

Но второе произойдет весело, несмотря на потенциальные последствия на дне.

Очевидно, возникнет вопрос: «Почему вы хотите вернуть ссылку на указатель»? В редких случаях вам нужно назначить память (или данные) непосредственно исходному указателю (например, создать свой собственный malloc / free или new / free front-end), но в этих случаях это неконстантная ссылка , Ссылка на константный указатель Я не сталкивался с ситуацией, которая оправдывала бы это (разве может быть, как объявленные константные ссылочные переменные, а не возвращаемые типы?).

Рассмотрим, есть ли у нас функция, которая принимает константный указатель (по сравнению с той, которая этого не делает):

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; }

        void ModifyArrayConst(char * const Data)
        {
            Data[1]; //This is okay, this refers to Data[1]
            Data--; //Produces an error. Don't want to Decrement that.
            printf("Const: %c\n",Data[1]);
        }

        void ModifyArrayNonConst(char * Data)
        {
            Data--; //Argh noo what are you doing?!
            Data[1]; //This is actually the same as 'Data[0]' because it's relative to Data's position
            printf("NonConst: %c\n",Data[1]);
        }
};

int main()
{
    TestA Temp;
    Temp.ModifyArrayNonConst("ABCD");
    Temp.ModifyArrayConst("ABCD");
}

Ошибка в const выдает таким образом сообщение:

ошибка: уменьшение параметра «Данные» только для чтения

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

NonConst: A
Const: B

Ясно, что, хотя A - это «Data [1]», он обрабатывается как «Data [0]», потому что указатель NonConst разрешил операцию декремента. Когда const реализован, как пишет другой человек, мы обнаруживаем потенциальную ошибку до ее появления.

Еще одно основное соображение заключается в том, что указатель const может использоваться в качестве псевдо-ссылки, поскольку вещь, на которую указывает ссылка, не может быть изменена (интересно, возможно, именно так и было реализовано). Рассматривать:

int main()
{
    int A = 10;
    int * const B = &A;
    *B = 20; //This is permitted
    printf("%d\n",A);
    B = NULL; //This produces an error
}

При попытке компиляции выдает следующую ошибку:

ошибка: присвоение переменной только для чтения 'B'

Что, вероятно, плохо, если была нужна постоянная ссылка на А. Если B = NULLэто закомментировано, компилятор с радостью разрешит нам изменить *Bи, следовательно, A. Это может показаться бесполезным для int, но подумайте, если у вас была единственная позиция графического приложения, где вы хотели использовать неизменяемый указатель, ссылающийся на него, который вы могли бы передать вокруг.

Его использование является переменным (извините за непреднамеренное каламбур), но используется правильно, это еще один инструмент в коробке, чтобы помочь с программированием.

SSight3
источник
2
Temp.GetArray() = NULLтерпит неудачу, потому что Temp.GetArray()это значение, а не потому, что оно является const-квалифицированным. Кроме того, я считаю, что const-qualifier исключен из всех типов возвращаемых данных.
Оскар Корз
@ okorz001: После тестирования вы действительно правы. Однако вышеприведенное применимо, если вы возвращаете ссылку на сам указатель. Я буду редактировать свой пост соответственно.
SSight3
3

Нет ничего особенного в указателях, где вы бы никогда не хотели, чтобы они были постоянными. Точно так же, как вы можете иметь постоянные intзначения членов класса , вы также можете иметь постоянные указатели по аналогичным причинам: вы хотите убедиться, что никто никогда не изменит то, на что указывают. Ссылки на C ++ несколько решают эту проблему, но поведение указателя унаследовано от C.

Марк Б
источник
3

Я считаю, что это помешает коду увеличить или уменьшить указатель в теле функции.

Майк Кристенсен
источник
3

Типы объявления любых переменных, например:
(1) Объявление постоянной переменной.
DataType const varibleName;

 int const x;
    x=4; //you can assign its value only One time
(2) Объявить указатель на постоянную переменную
const dataType* PointerVaribleName=&X;
 const int* ptr = &X;
     //Here pointer variable refer contents of 'X' that is const Such that its cannot be modified
dataType* const PointerVaribleName=&X;
 int* const ptr = &X;
     //Here pointer variable itself is constant  Such that value of 'X'  can be modified But pointer can't be modified

Pyadav
источник
Мы предоставляем указатель переменной как const в функцию в качестве аргумента, когда мы не хотим изменять ее фактическое значение
Pyadav