Какое использование константных указателей (в отличие от указателей на константные объекты)?

Я часто использовал указатели на константные объекты, вот так ...

const int *p;

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

int* const p;

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

Какая возможная польза от этого?


person Head Geek    schedule 20.10.2008    source источник


Ответы (11)


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

Например, у меня есть 32-битный процессор MIP с маленький ЖК-дисплей, прикрепленный к нему. Мне нужно записать данные ЖК-дисплея в определенный порт памяти, которые затем отправляются на контроллер ЖК-дисплея.

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

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

Примером для программ для ПК может быть: если вы разрабатываете игры для DOS VGA (в Интернете есть учебные пособия, которые интересно пройти, чтобы изучить базовую низкоуровневую графику), тогда вам необходимо выполнить запись в память VGA, на которую можно ссылаться как на смещение. из константного указателя.

-Адам

person Adam Davis    schedule 20.10.2008
comment
Незначительный нюанс: для устройств с отображением в память вам определенно (а не «может») нужно иметь элемент, помеченный как изменчивый - иначе вы не могли быть уверены, действительно ли компилятор выполнит операцию чтения или записи или когда это произойдет. - person Michael Burr; 21.10.2008
comment
Вам, возможно, понадобится volatile правильно. Вы можете использовать barrier() и другую семантику в зависимости от устройства. Это правда, что вам нужно осторожно обращаться с ценностями. Т.е. кеш / без кеширования, барьер и т. Д. Это зависит от типа устройства, и энергозависимость не всегда лучший вариант. - person artless noise; 15.02.2013
comment
@MichaelBurr: Я согласен с вами, что volatile обязательно должен быть там; с другой стороны, заголовки многих поставщиков компиляторов, похоже, не беспокоят - факт, который может несколько раздражать (поскольку даже если компиляторы обычно поступают правильно, они могут игнорировать попытки чтения регистра, но ничего не делать с результат). - person supercat; 13.04.2013

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

int* const p = &i;

...

p++;     /* Compiler error, oops you meant */
(*p)++;  /* Increment the number */
person Andrew Johnson    schedule 20.10.2008
comment
Я никогда раньше такого не видел и согласен, что это очень полезный трюк. - person Rob; 21.10.2008
comment
Это не прикол :). Я стараюсь использовать const там, где это возможно, с аргументами функции, чтобы было ясно, что функция не будет изменять передаваемую строку или структуру. - person Andrew Johnson; 21.10.2008
comment
Смущает то, что const часто встречается в объявлении дважды, и на самом деле может находиться в нескольких разных позициях. Обычно это может выглядеть как что-то вроде const uint8 *const value. Это объявляет, что указатель и значение, на которое он указывает, не могут быть изменены (но могут быть изменены). - person James Wald; 07.06.2014

другой пример: если вы знаете, где он был инициализирован, вы можете избежать будущих проверок NULL. Компилятор гарантирует вам, что указатель никогда не менялся (на NULL)…

person Benedikt Waldvogel    schedule 20.10.2008
comment
В C. В C ++ в вашем конкретном случае (т.е. указатели, отличные от NULL), вы вместо этого используете ссылки. - person paercebal; 21.10.2008

В любой неконстантной функции-члене C ++ указатель this имеет тип C * const, где C - это тип класса - вы можете изменить то, на что он указывает (то есть его члены), но вы не можете изменить его, чтобы он указывал на другой экземпляр C. Для const функций-членов this имеет тип const C * const. Также существуют (редко встречаются) volatile и const volatile функции-члены, для которых this также имеет квалификатор volatile.

person Adam Rosenfield    schedule 20.10.2008

Одно из применений - это низкоуровневый код (драйвер устройства или встроенный), где вам нужно ссылаться на конкретный адрес, который сопоставлен с устройством ввода / вывода, например, с аппаратным контактом. Некоторые языки позволяют связывать переменные по определенным адресам (например, в Ada есть use at). В C наиболее идиоматический способ сделать это - объявить постоянный указатель. Обратите внимание, что такие употребления также должны иметь квалификатор volatile.

В других случаях это просто защитное кодирование. Если у вас есть указатель, который не должен изменяться, разумно объявить его так, чтобы он не мог изменяться. Это позволит компилятору (и инструментам lint) обнаруживать ошибочные попытки изменить его.

person Michael Carman    schedule 20.10.2008

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

'this' - это жестко запрограммированный указатель на константу.

person Tom Ritter    schedule 20.10.2008

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

struct MyClass
{
    char* const ptr;
    MyClass(char* str) :ptr(str) {}

    void SomeFunc(MyOtherClass moc)
    {
         for(int i=0; i < 100; ++i)
         { 
                 printf("%c", ptr[i]);
                 moc.SomeOtherFunc(this);
         }
    }
}

Теперь компилятор мог бы немного оптимизировать этот цикл - при условии, что он знает, что SomeOtherFunc () не изменяет значение ptr. С константой компилятор знает это и может делать предположения. Без него компилятор должен предположить, что SomeOtherFunc изменит ptr.

person James Curran    schedule 20.10.2008
comment
Не нужно ли объявлять SomeOtherFunc с аргументом константного указателя для компиляции? И не так ли компилятор узнает, что SomeOtherFunc не изменит указатель? Поэтому объявление локального указателя как const, похоже, не помогает. - person Andrew Johnson; 21.10.2008
comment
Эндрю: Я думаю, ты путаешь ptr с этим - person Leon Timmermans; 21.10.2008
comment
@Andrew: Нет. SomeOtherFunc может изменять любую другую часть объекта MyClass. - person James Curran; 21.10.2008
comment
Какое значение const для оптимизации? Любой умный компилятор может определить, изменяете ли вы переменную, и оптимизируете, если это возможно, на основе этого, независимо от того, сказали ли вы, что вы это сделаете. - person underscore_d; 23.05.2017
comment
@underscore_d 1) ptr является открытым участником. Компилятору очень сложно отслеживать использование общедоступной переменной в модулях. Многие даже не будут пытаться и будут относиться к этому как к volatile 2) 9 лет назад компиляторы были тупее. - person James Curran; 23.05.2017

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

person epotter    schedule 20.10.2008

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

У меня, конечно же, нет данных, подтверждающих это предположение, но я все же готов поспорить.

person Michael Burr    schedule 20.10.2008
comment
Я часто использую его для оптимизации. - person DavidG; 21.10.2008
comment
@DavidG Как именно const имеет значение для оптимизации? Любой умный компилятор может определить, изменяете ли вы переменную, и оптимизируете, если это возможно, на основе этого, независимо от того, сказали ли вы, что вы это сделаете. - person underscore_d; 23.05.2017
comment
это произошло еще в 2008 году на некоторых устройствах, когда я писал этот комментарий ... но с тех пор мир сильно изменился, и теперь компиляторы значительно улучшились. - person DavidG; 24.05.2017

Думайте о типе * и константном типе * как о самих типах. Затем вы можете понять, почему вам может понадобиться константа этих типов.

person Shadow2531    schedule 20.10.2008
comment
да, и этот тип в основном int. - person DavidG; 21.10.2008

всегда думайте об указателе как об int. это означает, что

object* var;

на самом деле можно рассматривать как

int var;

Итак, константный указатель просто означает, что:

const object* var;

становится

const int var;

и, следовательно, вы не можете изменить адрес, на который указывает указатель, и все. Чтобы предотвратить изменение данных, u должен сделать его указателем на константный объект.

person DavidG    schedule 20.10.2008
comment
Указатели не int во многих отношениях. Кроме того, очень четко сформулирован вопрос о const указателях, а не о указателях на const объекты. Но вы все равно неправильно их поняли. - person underscore_d; 23.05.2017