адрес переменной регистра в C и переполнение стека

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

  1. Я не могу получить доступ к адресу переменной регистра в C, хотя я могу сделать это C ++! Зачем? Есть ли проблема в доступе к адресации регистровой переменной?

  2. Предположим, если я объявлю строковую переменную в C ++ как регистр, то где эта переменная будет храниться? Какой смысл объявлять класс хранения нечисловых типов данных, таких как «строка», в C ++ для регистрации ??

ОБНОВИТЬ:
Я думал, что C ++ позволяет нам получить адрес переменной регистра, так как я не получал никакой ошибки в моей программе, которая выглядит следующим образом:

#include<iostream>
#include<time.h>

using namespace std;

clock_t beg, en;

int main(){

int j, k=0;

beg=clock();
for(register int i=0;i<10000000;i++){
/*if(k==0){
cout<<&i<<endl;    // if this code is uncommented, then C++ rejects the recommendation to make 'i' as register
k++;
}*/
}
en=clock();

cout<<en-beg<<endl;

cout<<&j<<endl<<&k;

return 0;
}

Я заметил, что если я сделаю переменную ‘i’ регистром и не попытаюсь напечатать адрес с помощью ‘&i ‘тогда C ++ принимает рекомендацию и сохраняет’ i ‘в регистре, это можно определить по времени выполнения цикла for, которое всегда будет около 4-12 мс, если’ i ‘находится в регистре. Но если я попытаюсь напечатать адрес переменной ‘i’, тогда я не получаю никакой ошибки, но C ++ отклоняет рекомендацию, и это может быть получено с момента выполнения цикла, который всегда больше 25, если я не зарегистрирован! !

Таким образом, в основном я не могу получить адрес переменной с классом хранения как регистр в C и C ++ !! ЗАЧЕМ?

5

Решение

C и C ++ — это разные языки.

  • В C вы не можете взять адрес переменной с register место хранения. Ср С11 6.7.1 / 6:

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

    Сноска: реализация может относиться к любому register декларация просто как auto декларация. […]

  • В C ++ register является устаревшим, бессмысленным ключевым словом, которое не имеет никакого эффекта (кроме, возможно, служит подсказкой компилятора), а переменные объявлены как register еще просто есть автоматическое хранение. В частности, C ++ не имеет класс хранения «регистр». (Просто класс хранения спецификатор, унаследовано от C.) Ср. С ++ 11, 7.1.1 / 3:

    register Спецификатор является подсказкой для реализации, что объявленная переменная будет интенсивно использоваться. [Примечание: подсказка может быть проигнорирована, и в большинстве реализаций она будет игнорироваться, если выбран адрес переменной. Это использование не рекомендуется […]

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

17

Другие решения

Во-первых, давайте посмотрим на соответствующие стандарты. Всегда возможно, что C и C ++ имеют разные значения для этого ключевого слова.

C ++ 2011 Раздел 7.1.1 Пункты 2 и 3:

Спецификатор регистра должен применяться только к именам переменных, объявленным в блоке (6.3), или к параметрам функции (8.4). Указывает, что именованная переменная имеет автоматическую продолжительность хранения (3.7.3). Переменная, объявленная без спецификатора класса хранения в области видимости блока или объявленная как параметр функции, имеет автоматическую продолжительность хранения по умолчанию.

Спецификатор регистра является подсказкой для реализации, что объявленная переменная будет интенсивно использоваться. [Примечание: подсказка может быть проигнорирована, и в большинстве реализаций она будет игнорироваться, если выбран адрес переменной. Это использование не рекомендуется (см. D.2). — конец примечания]

C 2011 Раздел 6.7.1, пункт 6 и сноска 121:

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

Реализация может обрабатывать любое объявление регистра просто как автоматическое объявление. Однако независимо от того, используется ли адресное хранилище на самом деле, адрес любой части объекта, объявленной с помощью регистра спецификатора класса хранилища, не может быть вычислен ни явно, ни с помощью & оператор, как описано в 6.5.3.2) или неявно (путем преобразования имени массива в указатель, как описано в 6.3.2.1). Таким образом, единственными операторами, которые могут быть применены к массиву, объявленному с помощью регистра спецификатора класса хранения, являются sizeof и _Alignof.

Итак, давайте заберем то, что мы узнали здесь.

  • В C ++ ключевое слово register не имеет смысла. Он действует как подсказка компилятора, но предполагается, что большинство компиляторов в любом случае проигнорирует эту подсказку.
  • В C ключевое слово регистра сохраняет смысл. Здесь нам, например, не разрешено брать адрес объекта (см. Цитируемую сноску). Реализация может игнорировать подсказку регистра и в любом случае поместить объект в память, но ключевое слово ограничивает то, что вы можете делать с объектом. Эти ограничения должны позволить компилятору лучше оптимизировать доступ к объекту, но также возможно (как это предлагается в случае C ++), что компилятор сможет вывести это в любом случае.

Что касается того, что вы видите на практике:

  • Мы можем видеть, почему вы получаете синтаксическую ошибку в C, когда вы пытаетесь взять адрес register intтак что давайте пройдем мимо этого.
  • Вы утверждаете, что видите разницу в производительности в C ++ в зависимости от того, используете ли вы register, В этом случае было бы хорошо показать свой тест, потому что могут быть проблемы в самом тесте. Если полный тест в порядке, то вполне возможно, что ваш компилятор использует подсказку для создания лучшего кода.
  • Код, который вы показали, конечно, странный. Это потому, что компилятор в условиях оптимизации, скорее всего, просто удалит весь цикл for из кода. Цикл for не имеет побочных эффектов. Вполне вероятно (и предпочтительно), чтобы компилятор возвращал идентичный код (то есть без кода) с for (int i=0; i<100; ++i){} а также for (register int i=0; i<100; ++i) {},
7

Взятие адреса переменной заставит компилятор хранить его в памяти (если только это не архитектура, где регистры имеют адрес — я думаю, что процессоры серии TI 9900 были реализованы таким образом, но это смутная память приблизительно с 1984 года). Если вы сказали компилятору использовать register, что делает его несовместимым. Хотя стандарт C ++, по-видимому, предполагает, что компилятор не обязан вам это говорить, и может фактически игнорировать register ключевое слово.

C ++ 11 черновик n3337, раздел 7.1.1, пул 3

Спецификатор регистра является подсказкой для реализации, что объявленная переменная будет интенсивно использоваться.
[Примечание: подсказка может быть проигнорирована, и в большинстве реализаций она будет игнорироваться, если адрес переменной
взят. Это использование не рекомендуется (см. D.2). — конец примечания]

(Правка: Да, TMS 9900 действительно имеет «регистры в памяти», поэтому теоретически вы можете иметь адрес регистра в этой архитектуре — но архитектура гораздо больше того, что «регистры живут в памяти (у которой есть адрес)» «чем» регистры имеют адреса «).

3

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

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

1

Многие аспекты C проистекают из желания разрешить однопроходную компиляцию. Многие ранние компиляторы читают немного исходного кода, генерируют некоторую сборку или машинный код, забывают большую часть того, что они только что прочитали, читают немного больше кода, генерируют еще немного сборки / машинного кода и т. Д. Если компилятор генерирует машинный код, он может необходимо создать список обратных патчей для таких вещей, как скачки вперед, но компилятор может генерировать код для функций, которые больше, чем его доступная оперативная память.

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

 void test(void)
{
int i,j,*p;
p=&i;
i=j=0;
do
{
j++;
*p+=10;
j++;
...

однопроходный компилятор не сможет узнать, сможет ли он безопасно сохранить j в реестре через доступ к *p, смывание j в память до *p+=10; и перезагрузка после этого сведет на нет большинство преимуществ выделения ему регистра, но компилятор пропустил сброс и перезагрузку, но за приведенным выше кодом последовало p=&j; это было бы проблемой. Все проходы цикла после первого нужно будет сохранить j в памяти при выполнении *p+=10;, но компилятор уже забыл код, который потребуется для второго прохода.

Эта проблема была решена путем указания, что если объявлен компилятор registerкомпилятор может безопасно генерировать код, который предполагает, что никакие обращения на основе указателей не повлияют на него. Запрет на использование адреса был IMHO излишне чрезмерным (*), но описать его было проще, чем тот, который позволял бы использовать классификатор в большем количестве обстоятельств.

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

1

Именно потому, что это регистр. Адрес есть адрес ячейки памяти. Если что-то находится в реестре, то это по определению не в основной памяти.

0

В C мы не можем взять адрес переменной с хранилищем регистров. нам нужно хранить с нормальным именем переменной.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector