Дает ли const-корректность компилятору больше возможностей для оптимизации?
Я знаю, что это улучшает читаемость и делает программу менее подверженным ошибкам, но насколько это улучшит производительность?
и на боковой ноте, в чем основное различие между ссылкой и const указатель? Я предполагаю, что они хранятся в памяти по-разному, но как так?
7 ответов:
[Edit: хорошо, так что этот вопрос более тонкий, чем я думал сначала.]
объявление указателя на const или ссылки на const никогда не помогает компилятору оптимизировать что-либо. (Хотя см. обновление в нижней части этого ответа.)
The
constобъявление только указывает, как идентификатор будет использоваться в scope декларации; он не говорит, что объект не может изменение.пример:
int foo(const int *p) { int x = *p; bar(x); x = *p; return x; }компилятор не может предположить, что
*pне изменяется при вызовеbar(), потому чтоpможет быть (например) указатель на глобальный int иbar()может изменить его.если компилятор знает достаточно о вызывающем
foo()и содержаниеbar()что он может доказатьbar()не изменяет*p, потом он также может выполнять это доказательство без const декларация.но это верно в целом. Потому что
constимеет эффект только в области объявления, компилятор уже может видеть, как вы обрабатываете указатель или ссылку в этой области; он уже знает, что вы не изменяете базовый объект.так короче, все
constв данном контексте это предотвратить вас от совершения ошибок. Это не говорит компилятору, что он уже не знает, и поэтому не имеет значения для оптимизации.насчет функций, которые вызывают
foo()? Например:int x = 37; foo(&x); printf("%d\n", x);может ли компилятор доказать, что это печатает 37, так как
foo()принимаетconst int *?нет. Даже если
foo()берет указатель на const, он может отбросить const-ness и изменить int. (Это не неопределенное поведение.) Здесь опять же компилятор не может делать никаких предположений вообще; и если он знает достаточно оfoo()чтобы сделать такое оптимизация, он будет знать, что даже безconst.только
constможет разрешить оптимизацию в таких случаях:const int x = 37; foo(&x); printf("%d\n", x);здесь, чтобы изменить
xчерез любой механизм вообще (например, взяв указатель на него и отбросивconst) - это вызов неопределенного поведения. Таким образом, компилятор может предположить, что вы этого не делаете, и он может распространять константу 37 в printf(). Такая оптимизация легальна для любого объекта вы заявляетеconst. (На практике локальная переменная, на которую вы никогда не ссылаетесь, не будет полезна, потому что компилятор уже может видеть, изменяете ли вы ее в своей области.)чтобы ответить на ваш вопрос" side note", (a) указатель const является указателем; и (b) указатель const может равняться NULL. Вы правы, что внутреннее представление (т. е. адрес), скорее всего, то же самое.
[обновление]
как Кристоф указывает в комментариях мой ответ неполный, потому что он не упоминает
restrict.в разделе 6.7.3.1 (4) стандарта C99 говорится:
во время каждого выполнения B пусть L-любое значение lvalue, которое имеет &L на основе P. Если L используется для доступ к значению объекта X, который он обозначает, и X также изменяется (любыми средствами), в этом случае применяются следующие требования: T не должно иметь постоянной квалификации. ...
(здесь B-основной блок над который P, ограничивающий указатель на T, находится в области видимости.)
так что если функция C
foo()объявляется следующим образом:foo(const int * restrict p)...тогда компилятор мая предположим, что никаких изменений в
*pпроисходят в течение жизниp-- т. е. во время исполненияfoo()-- потому что в противном случае поведение будет неопределенным.так, в принципе, комбинируя
restrictС помощью указателя на const можно включить обе оптимизации, которые являются уволен выше. Интересно, какие-нибудь компиляторы действительно реализуют такую оптимизацию? (GCC 4.5.2, по крайней мере, не делает.)обратите внимание, что
restrictсуществует только в C, а не в C++ (даже не C++0x), за исключением расширения, специфичного для компилятора.
С моей головы, я могу думать о двух случаях, когда правильный
const-квалификация позволяет проводить дополнительную оптимизацию (в тех случаях, когда анализ всей программы недоступен):const int foo = 42; bar(&foo); printf("%i", foo);здесь компилятор знает для печати
42без необходимости исследовать телоbar()(который может быть не виден в curent translation unit), потому что все изменения вfooявляются незаконными (это то же самое, что Немо пример).однако, это также возможно без маркировки
fooкакconstдекларантомbar()какextern void bar(const int *restrict p);во многих случаях программист действительно хочет
restrict-квалифицированные указатели-к-constа не простые указатели на -constкак параметры функции, так как только первые дают какие-либо гарантии относительно изменчивости указанных объектов.что касается второй части вашего вопроса: для всех практических целей, на C++ ссылка может рассматриваться как постоянный указатель (не указатель на постоянное значение!) с автоматической косвенностью-это не "безопаснее" или "быстрее", чем указатель, просто удобнее.
есть две проблемы с
constв C++ (что касается оптимизации):
const_castmutable
const_castозначает, что даже если вы передаете объект по ссылке const или указателю const, функция может отбросить константу и изменить объект (разрешено, если объект не является const для начала).
mutableозначает, что даже если объектconst, некоторые из его частей могут быть изменены (алгоритм кэширования). Кроме того, объекты, на которые указывают (вместо того, чтобы принадлежать), могут быть изменены вconstметоды, даже если они логически являются частью состояния объекта. И, наконец, глобальные переменные также могут быть изменены...
constздесь, чтобы помочь разработчику поймать логические ошибки на ранней стадии.
const-корректность обычно не помогает производительности; большинство компиляторов даже не утруждают себя отслеживанием константы за пределами интерфейса. Маркировка переменных как const может помочь, в зависимости от ситуации.
ссылки и указатели хранятся точно так же, как в памяти.
Это действительно зависит от компилятора / платформы (это может помочь оптимизации на каком-то компиляторе, который еще не был написан, или на какой-то платформе, которую вы никогда не используете). Стандарты C и c++ ничего не говорят о производительности, кроме требований к сложности для некоторых функций.
указатели и ссылки на const обычно не помогают оптимизации, так как квалификация const может быть юридически отброшена в некоторых ситуациях, и возможно, что объект может быть изменен различные неконстантные ссылки. С другой стороны, объявление объекта const может быть полезно, поскольку оно гарантирует, что объект не может быть изменен (даже при передаче функций, определение которых компилятор не знает). Это позволяет компилятору хранить объект const в памяти только для чтения или кэшировать его значение в централизованном месте, уменьшая необходимость в копиях и проверках на изменения.
указатели и ссылки обычно реализуются точно так же, но опять же, это полностью зависит от платформы. Если вы действительно заинтересованы, то вы должны смотреть на сгенерированный машинный код код платформа и компилятор в код
одно дело, если вы объявите глобальную переменную const, ее можно поместить в часть библиотеки или исполняемого файла только для чтения и, таким образом, разделить ее между несколькими процессами с помощью mmap только для чтения. Это может быть большой выигрыш памяти на Linux, по крайней мере, если у вас есть много данных, объявленных в глобальных переменных.
другая ситуация, если вы объявляете постоянное глобальное целое число или float или enum, компилятор может просто поместить константу в строку, а не использовать переменную ссылка. Это немного быстрее, я считаю, хотя я не эксперт компилятора.
ссылки-это просто указатели внизу, по реализации.
Это может помочь производительности немного, но только если вы получаете доступ к объекту непосредственно через его объявление. Ссылочные параметры и такие не могут быть оптимизированы, так как могут быть другие пути к объекту, первоначально не объявленному const, и компилятор обычно не может сказать, был ли объект, на который вы ссылаетесь, фактически объявлен const или нет, если это не объявление, которое вы используете.
Если вы используете объявление const, компилятор будет знать, что внешне скомпилированные тела функций и т. д. не может изменить его, так что вы получаете там пособие. И, конечно же, такие вещи, как const int, распространяются во время компиляции, так что это огромная победа (по сравнению с просто int).
ссылки и указатели хранятся точно так же, они просто ведут себя по-разному синтаксически. Ссылки-это в основном переименования, и поэтому они относительно безопасны, тогда как указатели могут указывать на множество разных вещей и, таким образом, являются более мощными и подверженный ошибкам.
Я думаю, что указатель const будет архитектурно идентичен ссылке, поэтому машинный код и эффективность будут одинаковыми. реальная разница заключается в синтаксисе-ссылки являются более чистым, более легким для чтения синтаксисом, и поскольку вам не нужны дополнительные механизмы, предоставляемые указателем, ссылка будет стилистически предпочтительнее.
Comments