2015-12-06 1 views
23

В языке программирования C нам не разрешено использовать адрес-оператора (&) с переменными, объявленными с помощью спецификатора класса хранения регистра.Почему адрес-оператор ('&') может использоваться с объектами, объявленными с помощью спецификатора класса хранения регистров в C++?

Это дает error: address of register variable ‘var_name’ requested

Но если мы делаем программу C++ и выполнять ту же задачу (т.е. использовать & с переменной для хранения регистра) не дает нам никакой ошибки.

например.

#include <iostream> 
using namespace std; 
int main() 
{ 
    register int a; 
    int * ptr; 
    a = 5; 
    ptr = &a; 
    cout << ptr << endl; 
    return 0; 
} 

Выход: -

0x7ffcfed93624 

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

+5

'register' - это подсказка на C++, поэтому компилятор имеет право игнорировать его. Если вы берете его адрес, компилятор игнорирует его. –

+3

@ Jean-BaptisteYunès Это тоже намек на C. –

+2

, вероятно, нет современного ключевого слова для поддержки компилятора –

ответ

21

Ограничение на прием адреса было умышленно удалено на C++ - для него не было никакой пользы, и это усложнило язык. (Например, что произойдет, если вы связали ссылку на переменную register?)

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

+1

Это не просто устарело, оно не имеет никакого эффекта. – HolyBlackCat

+0

@HolyBlackCat Это почти наверняка не влияет на какой-либо текущий компилятор, но я не убежден в том, что компиляторам фактически запрещено делать какие-либо уведомления об этом. –

+0

Я не могу прокомментировать, является ли это аргументом стандарта, но факт остается фактом: привязка адреса делает недействительной идею о том, что данные остаются в регистре. «Что произойдет» - ничто - адреса нет, его не может быть. Решение заключалось в том, чтобы * молча * игнорировать ошибочный намек, так же как C, если бы вы заявили больше переменных «register», чем в действительности были зарегистрированы регистры. Однако в '&' регистр, * есть * ошибка в том, что он логически не может работать. – sqykly

1

Реестром, зарегистрированным в реестре, будет сам регистр. Если вызывающая функция передала ESI в качестве ссылочного параметра, то вызываемая функция будет использовать ESI в качестве параметра. Как отметил Алан Стоукс, проблема в том, что другая функция также вызывает ту же функцию, но на этот раз с EDI в качестве того же ссылочного параметра.

Для того, чтобы это сработало, необходимо было создать два перегруженных типа экземпляра вызываемой функции, один из которых принимает ESI в качестве параметра, а EDI - в качестве параметра. Я не знаю, действительно ли какой-либо реальный компилятор C++ реализует такую ​​оптимизацию в целом, но так можно это сделать.

Одним из примеров регистрации по ссылке является способ оптимизации std :: swap() (оба параметра являются ссылками), который часто заканчивается как встроенный код. Иногда не происходит смены: например, std :: swap (a, b), не происходит никакого обмена, вместо этого смысл a и b заменяется в следующем коде (ссылки на то, что стало ссылкой на b и vice Versa).

В противном случае опорный параметр заставит переменную располагаться в памяти вместо регистра.

+0

Как это работает, если другая функция вызова использовала EDI? Или вы думаете о звонках? –

+0

@AlanStokes - я обновил свой ответ. – rcgldr

8

Класс хранения register первоначально намекнул компилятору, что переменная, так квалифицированная, должна была использоваться так часто, что сохранение ее значения в памяти было бы недостатком производительности. огромные большинство архитектур процессора (возможно, не SPARC? Даже не уверен, что есть контрпример) не может выполнять какую-либо операцию между двумя переменными без первой загрузки одной или обеих из памяти в свои регистры. Загрузка переменных из памяти в регистры и запись их обратно в память, когда она работает, занимает много раз больше циклов процессора, чем сами операции. Таким образом, если переменная используется часто, можно добиться выигрыша в производительности, отложив для нее регистр и вообще не беспокоясь о памяти.

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

  • Все процессоры имеют фиксированный число регистров, но каждая модель процессора имеет разное количество. В 80-е годы у вас могло быть 4, которые можно было бы разумно использовать для переменной register.
  • Большинство процессоров не поддерживают использование каждого регистра для каждой инструкции. В 80-е годы было не редкость иметь только один регистр, который вы могли бы использовать для сложения и вычитания, и вы, вероятно, не могли использовать тот же регистр, что и указатель.
  • Вызывающие соглашения продиктовали разные наборы регистров, которые можно было бы перезаписать подпрограммами, то есть вызовами функций.
  • Размер регистра различается между процессорами, поэтому бывают случаи, когда переменная register не будет вписываться в регистр.

Поскольку C предназначен для независимости от платформы, эти ограничения не могут быть соблюдены стандартом. Другими словами, хотя может быть невозможно скомпилировать процедуру с переменными 20 register для системы, в которой было только 4 машинных регистра, сама программа C не должна быть «неправильной», так как нет логической причины, что машина не может иметь 20 регистров , Таким образом, класс хранения register всегда был только подсказкой , что компилятор мог игнорировать, если целевая платформа не поддерживала бы ее.

Невозможность ссылки на регистр отличается. A register - конкретно не обновляется в памяти и не поддерживается в актуальном состоянии, если внесены изменения в память; вот в чем весь класс хранения. Поскольку они не предназначены для гарантированного представления в памяти, они не могут логически иметь адрес в памяти, который будет иметь смысл для внешнего кода, который может получить указатель. Регистры не имеют адреса для своего собственного процессора, и у них почти нет адреса, доступного для любого сопроцессора. Поэтому любая попытка получить ссылку на register равна , всегда. Стандарт C может с комфортом обеспечить соблюдение этого правила.

По мере развития вычислительной техники, однако, некоторые тенденции развиты, что ослабило цель самого класса register хранения:

  • Процессоры пришли с большим количеством регистров. Сегодня у вас, вероятно, есть не менее 16, и, возможно, все они будут использоваться взаимозаменяемо для большинства целей.
  • Многоядерные процессоры и распределенное выполнение кода стали очень распространенными; только одно ядро ​​имеет доступ к любому регистру и никогда не делится ими без использования памяти.
  • Алгоритмы выделения регистров переменным стали очень эффективными.

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

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

+5

Nit: Я думаю, вы имеете в виду, что C++ - это супермножество C (что технически это не так, хотя большая часть работы сводилась к минимизации несовместимости). –

+1

Процессоры x86 могут выполнять строковые операции над двумя операндами памяти: см. 'CMPS' и' Инструкции MOVS (коды операций 0xA4 - 0xA7). – Ruslan

+0

@ruslan 'movs' и' cmps' редко используются компиляторами, но они включают несколько регистров: 'ds',' es', 'ecx' /' rcx', 'esi' /' rsi' и ' edi'/'rdi'. 'cmps' также включает' eflags'. Обычно '* cx',' * di' и '* si' нужно будет загружать из памяти до, а иногда и после использования. Я предполагаю, что адресный режим с немедленной записью на самом деле является хорошим контрпримером. Починю. 65x имеет инструкции перемещения блока, такие как 'movs' тоже, память в память с 3 неявными регистрами. – sqykly

Смежные вопросы