Register c что это
Перейти к содержимому

Register c что это

  • автор:

Register c что это

На этом шаге мы познакомимся с регистровыми переменными.

Объекты, описанные внутри блока с классом памяти register , называются регистровыми переменными . Они подчиняются всем правилам, касающимся автоматических переменных. Описание register указывает компилятору, что данная переменная будет часто использоваться.

Описание может выглядеть, например, так:

register int n; register char c; register int *px;

Использование регистровых переменных приводит к меньшим по размерам и более быстрым программам.

Компилятор C++ автоматически выполняет оптимальное распределение регистров в соответствии с потоком данных. Этим снята необходимость обязательного указания переменных программы как регистровых, так что описание register идентично по смыслу описанию auto и никакого другого смысла не имеет.

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

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

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

К регистровым переменным не может быть применена операция «&» (получения адреса), таким образом, если в программе не используются адреса некоторых переменных, то эффективнее использовать для этих переменных регистры.

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

Таблица 1. Классы памяти, определяемые внутри функции Класс памяти Ключевое слово Продолжительность существования Область действия
Автоматический auto Временно Локальная
Статический static Постоянно Локальная
Регистровый register Временно Локальная
Таблица 2. Классы памяти, определяемые вне функции Класс памяти Ключевое слово Продолжительность существования Область действия
Внешний extern Постоянно Глобальная (все файлы)
Внешний статический static Постоянно Глобальная (один файл)

Классы памяти, перечисленные в первой таблице, описываются внутри функции. Классы памяти, перечисленные во второй таблице, описываются вне функции.

На следующем шаге мы познакомимся с рекурсией.

Регистровые переменные

С имеет еще один спецификатор, который первоначально применялся только к переменным int и char. Стандарт ANSI С расширил сферу его применения. Спецификатор register просит, чтобы компилятор сохранил переменную способом, позволяющим осуществлять наибыстрейший доступ. Для целых чисел и символов это обычно подразумевает размещение не в памяти, а в регистрах процессора. Для других типов переменных компилятор может использовать другие способы для уменьшения времени доступа. Компилятор может просто игнорировать данную просьбу.

В Borland С++ спецификатор register может применяться к локальным переменным и формальным параметрам функции. Нельзя применять register к глобальным переменным. Также, поскольку регистровая переменная может быть сохранена в регистре процессора, нельзя получить адрес регистровой переменной. (Данное ограничение присутствует только в С, но не в С++)

В целом операции с регистровыми переменными выполняются гораздо быстрее, чем с переменными, сохраненными в памяти. Фактически, когда значение переменной содержится в процессоре, не требуется доступа к памяти для определения или модификации значения. Это делает регистровые переменные идеальным средством для управления циклами. Ниже приведен пример объявления регистровой переменной типа int и дальнейшего ее использования для управления циклом. Данная функция вычисляет m* для целых чисел.

int int_pwr (register int m, register int e)
register int temp;
temp = 1 ;
for( ; e; e—) temp *= m;
return temp;
>

В данном примере m, e и temp объявлены как регистровые переменные, поскольку они используются в цикле. Обычно регистровые переменные используются там, где они принесут наибольшую пользу, то есть в местах, где делается много ссылок на одну и ту же переменную. Это важно, поскольку не все переменные можно оптимизировать по времени доступа.

Важно понять, что спецификатор register — это просто запрос компилятору, который не обязательно будет удовлетворен. Как правило, можно использовать по крайней мере две регистровые переменные, размещаемые в регистрах процессора, типа char или int. Дополнительные регистровые переменные оптимизируются более продвинутыми компиляторами.

Регистровые переменные

Возможно, чаще всего используется спецификатор класса памяти register. Для компилятора модификатор register означает предписание обеспечить такое хранение соответствующей переменной, чтобы доступ к ней можно было получить максимально быстро. Обычно переменная в этом случае будет храниться либо в регистре центрального процессора (ЦП), либо в кэше (быстродействующей буферной памяти небольшой емкости). Вероятно, вы знаете, что доступ к регистрам ЦП (или к кэш-памяти) принципиально быстрее, чем доступ к основной памяти компьютера. Таким образом, переменная, сохраняемая в регистре, будет обслужена гораздо быстрее, чем переменная, сохраняемая, например, в оперативной памяти (ОЗУ). Поскольку скорость, с которой к переменным можно получить доступ, определяет, по сути, скорость выполнения вашей программы, для получения удовлетворительных результатов программирования важно разумно использовать спецификатор register. Спецификатор register в объявлении переменной означает требование оптимизировать код для получения максимально возможной скорости доступа к ней. Формально спецификатор register представляет собой лишь запрос, который компилятор вправе проигнорировать. Это легко объяснить: ведь количество регистров (или устройств памяти с малым временем выборки) ограничено, причем для разных сред оно может быть различным. Поэтому, если компилятор исчерпает память быстрого доступа, он будет хранить register-переменные обычным способом. В общем случае неудовлетворенный register-запрос не приносит вреда, но, конечно же, и не дает никаких преимуществ хранения в регистровой памяти. Поскольку в действительности только для ограниченного количества переменных можно обеспечить быстрый доступ, важно тщательно выбрать, к каким из них применить модификатор register. (Только правильный выбор может повысить быстродействие программы.) Как правило, чем чаще к переменной требуется доступ, тем большая выгода будет получена в результате оптимизации кода с помощью спецификатора register. Поэтому объявлять регистровыми имеет смысл управляющие переменные цикла или переменные, к которым выполняется доступ в теле цикла. На примере следующей функции показано, как register-переменная типа int используется для управления циклом. Эта функция вычисляет результат выражения m е для целочисленных значений с сохранением знака исходного числа (т.е. при m = -2 и е = 2 результат будет равен -4). int signed_pwr(register int m, register int e) < register int temp; int sign; if(m < 0) sign = -1; else sign = 1; temp = 1; for( ; e; e--) temp = temp * m; return temp * sign; >В этом примере переменные m, е и temp объявлены как регистровые, поскольку все они используются в теле цикла, и потому к ним часто выполняется доступ. Однако переменная sign объявлена без спецификатора register, поскольку она не является частью цикла и используется реже.

Происхождение модификатора register

Модификатор register был впервые определен в языке С. Первоначально он применялся только к переменным типа int и char или к указателям и заставлял хранить переменные этого типа в регистре ЦП, а не в ОЗУ, где хранятся обычные переменные. Это означало, что операции с регистровыми переменными могли выполняться намного быстрее, чем операции с остальными (хранимыми в памяти), поскольку для опроса или модификации их значений не требовался доступ к памяти. После стандартизации языка С было принято решение расширить определение спецификатора register. Согласно ANSI-стандарту С модификатор register можно применять к любому типу данных. Его использование стало означать для компилятора требование сделать доступ к переменной типа register максимально быстрым. Для ситуаций, включающих символы и целочисленные значения, это по-прежнему означает помещение их в регистры ЦП, поэтому традиционное определение все еще в силе. Поскольку язык C++ построен на ANSI-стандарте С, он также поддерживает расширенное определение спецификатора register. Как упоминалось выше, точное количество register-переменных, которые реально будут оптимизированы в любой одной функции, определяется как типом процессора, так и конкретной реализацией C++, которую вы используете. В общем случае можно рассчитывать по крайней мере на две. Однако не стоит беспокоиться о том, что вы могли объявить слишком много register-переменных, поскольку C++ автоматически превратит регистровые переменные в нерегистровые, когда их лимит будет исчерпан. (Это гарантирует переносимость С++-кода в рамках широкого диапазона процессоров.) Чтобы показать влияние, оказываемое register-переменными на быстродействие программы, в следующем примере измеряется время выполнения двух циклов for, которые отличаются друг от друга только типом управляющих переменных. В программе используется стандартная библиотечная С++-функция clock(), которая возвращает количество импульсов сигнала времени системных часов, подсчитанных с начала выполнения этой программы. Программа должна включать заголовок . /* Эта программа демонстрирует влияние, которое может оказать использование register-переменной на скорость выполнения программы. */ #include #include using namespace std; unsigned int i; //не register-переменная unsigned int delay; int main() < register unsigned int j; long start, end; start = clock(); for(delay=0; delayПри выполнении этой программы вы убедитесь, что цикл с "регистровым" управлением выполняется приблизительно в два раза быстрее, чем цикл с "нерегистровым" управлением.

Ключевое слово «register» в C?

Что делает ключевое слово register на языке C? Я прочитал, что он используется для оптимизации, но четко не определен ни в одном стандарте. Это все еще актуально, и если да, то когда вы его используете?

ОТВЕТЫ

Ответ 1

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

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

Ответ 2

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

Таким образом, используя register , вы ничего не выигрываете (в любом случае компилятор сам решит, где поставить эту переменную) и потерять оператор & — нет причин для его использования.

Ответ 3

Он сообщает компилятору попытаться использовать регистр ЦП, а не ОЗУ, чтобы сохранить переменную. Регистры находятся в ЦП и намного быстрее доступны, чем оперативная память. Но это всего лишь предложение компилятору, и это может не произойти.

Ответ 4

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

Последний проект стандарта С++ 11, N3485, говорит об этом в 7.1.1/3:

A register specifier — это намек на реализацию, которую объявленная переменная будет сильно использоваться. [note: подсказка может быть проигнорирована, и в большинстве реализаций она будет игнорироваться, если будет принят адрес переменной. Это использование устарело. -end note]

В С++ (но не в C) стандарт не указывает, что вы не можете принять адрес переменной, объявленной register ; однако, поскольку переменная, хранящаяся в регистре CPU на протяжении всего жизненного цикла, не имеет связанного с ней местоположения памяти, попытка получить его адрес будет недопустимой, а компилятор будет игнорировать ключевое слово register , чтобы разрешить использование адреса.

Ответ 5

Это не актуально в течение как минимум 15 лет, поскольку оптимизаторы принимают более правильные решения по этому поводу, чем вы можете. Даже когда это было актуально, это имело гораздо больший смысл в архитектуре процессора с большим количеством регистров, таких как SPARC или M68000, чем у Intel с небольшим количеством регистров, большинство из которых зарезервировано компилятором в своих целях.

Ответ 6

Фактически, регистр сообщает компилятору, что переменная не имеет псевдонимов с все остальное в программе (даже не char).

Это может быть использовано современными компиляторами в различных ситуациях и может помочь компилятору совсем немного в сложном коде — в простом коде компиляторы могут понять это самостоятельно.

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

Ответ 7

Я читал, что он используется для оптимизации, но четко не определен ни в одном стандарте.

Фактически он четко определен стандартом C. Цитирование N1570 draft в разделе 6.7.1, пункт 6 (другие версии имеют одинаковую формулировку):

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

Унарный оператор & не может применяться к объекту, определенному с помощью register , а register не может использоваться во внешней декларации.

Существует несколько других (довольно неясных) правил, характерных для register -qualified объектов:

  • Определение объекта массива с помощью register имеет поведение undefined.
    Исправление:. Легально определять объект массива с помощью register , но вы не можете делать ничего полезного с таким объектом (индексирование в массив требует ввода адреса его начального элемента).
  • Спецификатор _Alignas (новый в C11) не может быть применен к такому объекту.
  • Если имя параметра, переданное макросу va_start , register -qualified, поведение undefined.

Может быть несколько других; загрузите черновик стандарта и найдите «register», если вы заинтересованы.

Как следует из названия, первоначальный смысл register состоял в том, чтобы потребовать сохранения объекта в регистре CPU. Но с улучшением оптимизации компиляторов это стало менее полезным. Современные версии стандарта C не относятся к регистрам CPU, потому что они больше не нуждаются (должны) предполагать, что есть такая вещь (существуют архитектуры, которые не используют регистры). Общая мудрость заключается в том, что применение register к объявлению объекта с большей вероятностью ухудшит сгенерированный код, поскольку это мешает распределению собственного регистратора компилятора. Там может быть несколько случаев, когда это полезно (скажем, если вы действительно знаете, как часто переменная будет доступна, и ваши знания лучше, чем может понять современный оптимизирующий компилятор).

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

Ответ 8

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

Ключевое слово было использовано long, long back. Когда было только так мало регистров, которые могли подсчитать их всех с помощью указательного пальца.

Но, как я уже сказал, устаревшее не означает, что вы не можете его использовать.

Ответ 9

Просто небольшое демо (без какой-либо реальной цели) для сравнения: при удалении ключевых слов register перед каждой переменной этот фрагмент кода занимает 3,41 секунды на моем i7 (GCC), при этом register тот же код завершается через 0,7 секунды.

#include int main(int argc, char** argv) < register int numIterations = 20000; register int i=0; unsigned long val=0; for (i; i> printf("%d", val); return 0; > 
Ответ 10

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

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

Так много людей ошибочно рекомендуют не использовать ключевое слово register.

Давайте посмотрим, почему!

Ключевое слово register имеет связанный побочный эффект: вы не можете ссылаться (получить адрес) на переменную типа регистра.

Люди, советующие другим не использовать регистры, ошибочно воспринимают это как дополнительный аргумент.

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

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

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

Ответ 11

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

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

Ответ 12

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

Ответ 13

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

Но C — это только абстракция. И, в конечном счете, то, что он извлекает из вас, является языком Ассамблеи. Assembly — это язык, который читает процессор, и если вы его используете, вы делаете что-то с точки зрения процессора. Что делает процессор? В принципе, он читает из памяти, выполняет математику и записывает в память. CPU не просто выполняет математику по номерам в памяти. Во-первых, вам нужно переместить число из памяти в память внутри процессора, называемого регистром. Как только вы закончите делать все, что вам нужно сделать, чтобы это число, вы можете переместить его обратно в обычную системную память. Зачем использовать системную память? Регистры ограничены по количеству. В современных процессорах вы получаете около ста байтов, а более старые популярные процессоры были еще более фантастически ограничены (у 6502 было 3 8-битных регистра для вашего бесплатного использования). Итак, ваша средняя математическая операция выглядит так:

load first number from memory load second number from memory add the two store answer into memory 

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

Когда вы объявляете переменную register , вы сообщаете компилятору «Yo, я намерен использовать эту переменную много и/или быть коротким. Если бы я был вами, я бы попытался сохранить его в регистр». Когда стандарт C говорит, что компиляторам не нужно ничего делать, потому что стандарт C не знает, на каком компьютере вы компилируете, и это может быть похоже на 6502 выше, где все 3 регистра необходимы только для работы, и нет запасного регистра, чтобы сохранить свой номер. Однако, когда говорится, что вы не можете принять адрес, это потому, что регистры не имеют адресов. Это процессорные руки. Поскольку компилятор не должен указывать вам адрес, и поскольку он вообще не может иметь адрес, в настоящее время для компилятора открываются несколько оптимизаций. Он мог, скажем, всегда держать номер в регистре. Он не должен беспокоиться о том, где он хранится в памяти компьютера (за исключением необходимости вернуть его обратно). Он может даже качать ее в другую переменную, передавать ее другому процессору, давать ему изменяющееся местоположение и т.д.

tl; dr: Краткоживущие переменные, которые выполняют много математики. Не объявляйте слишком много сразу.

Ответ 14

Компилятор Microsoft Visual С++ игнорирует ключевое слово register , когда включена глобальная оптимизация распределения регистров (флаг компилятора /Oe ).

Ответ 15

Ключевое слово Register указывает компилятору, чтобы сохранить конкретную переменную в регистре CPU, чтобы она могла быть быстро доступной. С точки зрения программиста ключевое слово register используется для переменных, которые сильно используются в программе, поэтому компилятор может ускорить код. Хотя это зависит от компилятора, следует ли сохранять переменную в регистры процессора или в основной памяти.

Ответ 16

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

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *