В языке программирования C, restrict
это ключевое слово, которое может быть использовано в стрелочных декларациях. Добавляя этот квалификатор типа, программист намекает компилятору, что в течение времени жизни указателя только сам указатель или значение, непосредственно полученное из него (например, pointer + 1
), будет использоваться для доступа к объекту, на который он указывает.
restrict
ограничивает эффекты сглаживания указателей, помогая оптимизировать. Если декларация о намерениях не соблюдается и к объекту обращается независимый указатель, это приведет к неопределенному поведению. Использование этого квалификатора типа позволяет коду C достигать той же производительности, что и та же программа, написанная на Фортране. Он был введен в стандарт C99.
C ++ не имеет стандартную поддержку restrict
, но многие компиляторы имеют эквиваленты, которые обычно работают как в C ++ и C, например, GCC «ы и Clang » s __restrict__
и Visual C ++ «ами __declspec(restrict)
. Вдобавок __restrict
поддерживается этими тремя компиляторами. Точная интерпретация этих альтернативных ключевых слов зависит от компилятора:
__restrict
и __restrict__
означает то же самое, что и их аналог на C. Расширения включают возможность применения их к ссылочным типам и this
.__declspec(restrict)
применяется к объявлению функции и указывает на то, что у возвращаемого указателя нет псевдонима.__restrict
используется там же, где и restrict
, но подсказка без псевдонима не распространяется, как в restrict
. Он также расширен для типов объединения.Если компилятор знает, что существует только один указатель на блок памяти, он может создать более оптимизированный код. Например:
void updatePtrs(size_t *ptrA, size_t *ptrB, size_t *val) { *ptrA += *val; *ptrB += *val; }
В приведенном выше коде указатели ptrA
, ptrB
и val
могут относиться к одному и тому же месту в памяти, поэтому компилятор может сгенерировать менее оптимальный код:
; Hypothetical RISC Machine. ldr r12, [val] ; Load memory at val to r12. ldr r3, [ptrA] ; Load memory at ptrA to r3. add r3, r3, r12 ; Perform addition: r3 = r3 + r12. str r3, [ptrA] ; Store r3 to memory location ptrA, updating the value. ldr r3, [ptrB] ; 'load' may have to wait until preceding 'store' completes. ldr r12, [val] ; Have to load a second time to ensure consistency. add r3, r3, r12 str r3, [ptrB]
Однако, если используется restrict
ключевое слово и указанная выше функция объявлена как
void updatePtrs(size_t *restrict ptrA, size_t *restrict ptrB, size_t *restrict val);
тогда компилятору разрешается предположить, что ptrA
, ptrB
и val
указывают на разные места, и обновление области памяти, на которую ссылается один указатель, не повлияет на участки памяти, на которые ссылаются другие указатели. Программист, а не компилятор, отвечает за то, чтобы указатели не указывали на идентичные места. Компилятор может, например, переупорядочить код, сначала загрузив все ячейки памяти, а затем выполнив операции перед фиксацией результатов обратно в память.
ldr r12, [val] ; Note that val is now only loaded once. ldr r3, [ptrA] ; Also, all 'load's in the beginning... ldr r4, [ptrB] add r3, r3, r12 add r4, r4, r12 str r3, [ptrA] ;... all 'store's in the end. str r4, [ptrB]
Приведенный выше ассемблерный код короче, потому что val
загружается только один раз. Кроме того, поскольку компилятор может более свободно изменять порядок кода, компилятор может генерировать код, который выполняется быстрее. Во второй версии приведенного выше примера все store
операции выполняются после load
операций, гарантируя, что процессору не придется блокироваться в середине кода, чтобы дождаться завершения store
операций.
Обратите внимание, что реальный сгенерированный код может иметь другое поведение. Выгоды от приведенного выше мини-примера, как правило, невелики, и в реальных случаях большие циклы, выполняющие тяжелый доступ к памяти, как правило, являются тем, чему действительно помогает ограничение.
Как упоминалось выше, поведение некорректного кода не определено, компилятор гарантирует, что сгенерированный код работает правильно, только если код следует декларации о намерениях.
Чтобы предотвратить неправильный код, некоторые компиляторы и другие инструменты пытаются определить, когда перекрывающиеся аргументы были переданы функциям с отмеченными параметрами restrict
. CERT C стандарт кодирования рассматривает злоупотребление restrict
и библиотечные функции, отмеченные с ней (EXP43-C) вероятный источник ошибок программного обеспечения, хотя по состоянию на ноябрь 2019 года не известно ни одной уязвимости, были вызваны этим.
|journal=
( помощь )