ограничивать

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

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

В языке программирования C, restrictэто ключевое слово, которое может быть использовано в стрелочных декларациях. Добавляя этот квалификатор типа, программист намекает компилятору, что в течение времени жизни указателя только сам указатель или значение, непосредственно полученное из него (например, pointer + 1), будет использоваться для доступа к объекту, на который он указывает.

restrictограничивает эффекты сглаживания указателей, помогая оптимизировать. Если декларация о намерениях не соблюдается и к объекту обращается независимый указатель, это приведет к неопределенному поведению. Использование этого квалификатора типа позволяет коду C достигать той же производительности, что и та же программа, написанная на Фортране. Он был введен в стандарт C99.

C ++ не имеет стандартную поддержку restrict, но многие компиляторы имеют эквиваленты, которые обычно работают как в C ++ и C, например, GCC «ы и Clang » s __restrict__и Visual C ++ «ами __declspec(restrict). Вдобавок __restrictподдерживается этими тремя компиляторами. Точная интерпретация этих альтернативных ключевых слов зависит от компилятора:

  • В компиляторах в стиле Unix, таких как GCC и Clang, __restrictи __restrict__означает то же самое, что и их аналог на C. Расширения включают возможность применения их к ссылочным типам и this.
  • В Visual C ++ предоставляется несколько квалификаторов без псевдонима:
    1. __declspec(restrict)применяется к объявлению функции и указывает на то, что у возвращаемого указателя нет псевдонима.
    2. __restrictиспользуется там же, где и restrict, но подсказка без псевдонима не распространяется, как в restrict. Он также расширен для типов объединения.
СОДЕРЖАНИЕ
  • 1 Оптимизация
  • 2 предупреждения компилятора
  • 3 ссылки
  • 4 Внешние ссылки
Оптимизация

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

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 года не известно ни одной уязвимости, были вызваны этим.

Рекомендации
Внешние ссылки
Последняя правка сделана 2024-01-09 04:47:22
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте