5

我知道寄存器变量的概念及其用例,但根据我的尝试,我脑海中几乎没有问题。

  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”存储在寄存器中,这可以从运行时间推断出来如果“i”在寄存器中,for 循环将始终在 4-12 毫秒左右。但是,如果我尝试打印变量“i”的地址,那么虽然我没有收到任何错误,但 C++ 拒绝了建议,这可以从执行循环的时间推断出来,如果 i 没有注册,则该时间总是超过 25! !

因此,基本上我无法获取具有存储类的变量的地址作为 C 和 C++ 中的寄存器!为什么?

4

8 回答 8

18

C 和 C++ 是不同的语言。

  • 在 C 中,您不能将变量的地址用于register存储。参照。C11 6.7.1/6:

    使用存储类说明符为对象声明标识符register 建议尽可能快地访问该对象。此类建议的有效程度由实施定义。

    脚注:实现可以将任何register声明简单地视为auto声明。[...]

  • 在 C++ 中,register是一个已弃用、无意义的关键字,它没有任何效果(可能用作编译器提示除外),并且声明为的变量register仍然只有自动存储。特别是,C++没有“注册”存储类。(它只有存储类说明符,继承自 C。)参见。C++11、7.1.1/3:

    说明register符是对实现的提示,即如此声明的变量将被大量使用。[ 注意:提示可以被忽略,并且在大多数实现中,如果变量的地址被获取,它将被忽略。这种用法已被弃用 [...]

即使在 C 中,实际上也无法保证寄存器存储的实现方式(实现可以随意register视为auto),但无论如何都适用语言规则。

于 2013-07-09T23:23:16.187 回答
8

首先,我们来看看相关标准。对于这个关键字,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 中, register 关键字保留了一个含义。例如,在这里,我们不允许获取对象的地址(参见引用的脚注)。实现可能会忽略寄存器提示,并将对象放在内存中,但关键字确实限制了您可以对对象执行的操作。这些限制应该使编译器能够更好地优化对对象的访问,但也有可能(就像在 C++ 案例中所建议的那样),编译器无论如何都能够推断出这一点。

至于你在实践中看到的:

  • 当您尝试获取 a 的地址时,我们可以看到为什么在 C 中会出现语法错误register int,所以让我们跳过它。
  • 您声称看到 C++ 的性能差异取决于您是否使用register. 在这种情况下,最好展示您的测试,因为测试本身可能存在问题。如果完整的测试没问题,那么你的编译器肯定有可能使用提示来生成更好的代码。
  • 您显示的代码当然很奇怪。这是因为处于优化状态的编译器可能只会从代码中删除整个 for 循环。for 循环没有副作用。for (int i=0; i<100; ++i){}编译器很可能(并且首选)将返回与and相同的代码(即没有代码)for (register int i=0; i<100; ++i) {}
于 2013-07-09T23:36:11.953 回答
3

获取变量的地址将强制编译器将其存储在内存中(除非它是寄存器具有地址的架构 - 我认为 TI 9900 系列处理器是以这种方式实现的,但它是 ca 1984 的模糊内存)。如果您告诉编译器使用 a register,那将使其不兼容。尽管 C++ 标准似乎暗示编译器没有义务告诉您,实际上可以忽略该register关键字。

C++11 草案 n3337,第 7.1.1 节,项目符号 3

寄存器说明符是对实现的提示,即如此声明的变量将被大量使用。[ 注意:提示可以被忽略,并且在大多数实现中,如果变量的地址被获取,它将被忽略。这种用法已被弃用(见 D.2)。——尾注]

(编辑:是的,TMS 9900 确实有“内存中的寄存器”,所以理论上,您可以在该架构中拥有寄存器的地址 - 但该架构更多的是“寄存器存在于内存中(有地址) " 而不是 "寄存器有地址")。

于 2013-07-09T23:24:10.940 回答
1

基本答案是,在大多数架构上,通用寄存器没有内存地址。通常,指针只是包含对象的内存位置的(虚拟)内存地址。这是为了效率。

可以将指针的概念扩展为指向内存或寄存器。但是,这样做会降低程序速度,因为取消引用指针的代码需要检查指针指向的位置类型。

于 2013-07-09T23:10:34.650 回答
1

C 语言的许多方面都源于允许单遍编译的愿望。许多早期的编译器会读一点源代码,生成一些汇编或机器代码,忘记他们刚刚读到的大部分内容,读更多的代码,生成更多的汇编/机器代码等。如果编译器正在生成机器代码,它可能需要为向前跳转之类的事情建立一个后补丁列表,但是编译器可以为大于其可用 RAM 的函数生成代码。

许多机器有一些寄存器可以专门用于存储值,但是编译器无法知道在代码中的任何给定点处哪些变量最有用地保存在寄存器中,除非它知道以后将如何使用变量在代码中。给定类似的东西:

 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了编译器,编译器可以安全地生成假定没有基于指针的访问会影响它的代码,从而解决了这个问题。恕我直言,禁止获取地址是不必要的过度延伸(*),但它比允许在更多情况下使用限定符的描述更简单。

(*)即使在今天,如果register编译器承诺编译器可以安全地将变量保存在寄存器中,如果它在获取其地址时将其刷新到内存,并且推迟重新加载它直到下一次代码使用变量,向后分支 [通过循环构造或 goto],或进入使用变量的循环]。

于 2017-01-25T21:08:27.007 回答
0

正是因为它是一个寄存器。地址是内存位置的地址。如果某物驻留在寄存器中,则根据定义,它不在主存储器中

于 2013-07-09T23:08:43.650 回答
0

您不能获取寄存器变量的地址,因为它不存储在 RAM 中。

于 2020-06-09T02:51:00.743 回答
0

在 C 中,我们不能通过寄存器存储来获取变量的地址。我们需要用普通的变量名来存储。

于 2017-01-25T18:40:00.780 回答