【问题标题】:Why can't access register array using loop为什么不能使用循环访问寄存器数组
【发布时间】:2020-03-10 03:57:43
【问题描述】:

我有以下代码

{
    register int a[10];
        int i;
        for(i=0;i<10;i++){
            a[i]=1;
        }
        for(i=0;i<10;i++){
            printf("%d\t",&a[i]);
        }
}   

错误

rejarr.c: In function ‘main’:
    rejarr.c:6:3: error: address of register variable ‘a’ requested
       a[i]=1;
       ^
    rejarr.c:9:3: error: address of register variable ‘a’ requested
       printf("%d\t",&a[i]);                                            
       ^

但是我提到了数组的索引位置,那时我可以访问数组的值。
使用循环无法访问数组的原因是什么

【问题讨论】:

标签: c arrays cpu-registers


【解决方案1】:

在 C 中,您不能将 &amp; 运算符与寄存器变量一起使用。这就是编译器抛出错误的原因。

C standard 6.7.1 Storage-class specifiers脚注128:

128) 实现可以将任何寄存器声明简单地视为 自动声明。 但是,无论是否可寻址存储 实际使用时,用声明的对象的任何部分的地址 不能显式计算存储类说明符寄存器 (通过使用 6.5.3.2 中讨论的一元 & 运算符)或隐式 (通过将数组名称转换为 6.3.2.1 中讨论的指针)。 因此,唯一可以应用于声明的数组的运算符 存储类说明符寄存器是 sizeof。

【讨论】:

    【解决方案2】:

    register 是一个旧关键字,它要求 C 实现将对象存储在处理器寄存器而不是内存中。在现代 C 中,它只要求实现快速访问对象,在现代优化面前几乎毫无用处。

    但是,一些旧的语义仍然存在。处理器寄存器没有内存地址,因此 C 有规则禁止使用 register 对象的地址。当您使用带有下标的数组时,该数组必须(自动)转换为指向其第一个元素的指针。也就是说,必须取地址。这违反了 C 2018 6.3.2.1 3 中的规则,该规则说:

    …如果数组对象有注册存储类,则行为未定义。

    【讨论】:

      【解决方案3】:

      对于第二个错误:printf("%d\t",&amp;a[i]); 实际上应该是 printf("%d\t",a[i]);&amp; 表示“地址”,但您需要该值。 但是,register 变量的数组索引不起作用,因为...


      来自Wikipedia

      在 C 中(但不是 C++,其中关键字基本上被忽略)无法访问使用 register 声明的变量的位置,但可以应用 sizeof 运算符。除了这个限制之外,寄存器在现代编译器中基本上没有意义,因为优化会在适当的时候将变量放在寄存器中,而不管是否给出提示。

      因此,对于第一个错误,您不能拥有使用指针算法的代码,例如 *(a+i)=1;,这可能是编译器实现 a[i]=1; 的方式。展开循环似乎可以工作:

      int main(int argc, char *argv[]) {
          register int a[10];
      
          a[0]=1;
          a[1]=1;
          a[2]=1;
          a[3]=1;
          a[4]=1;
          a[5]=1;
          a[6]=1;
          a[7]=1;
          a[8]=1;
          a[9]=1;
      
          printf("%d\t", a[0]);
          printf("%d\t", a[1]);
          printf("%d\t", a[2]);
          printf("%d\t", a[3]);
          printf("%d\t", a[4]);
          printf("%d\t", a[5]);
          printf("%d\t", a[6]);
          printf("%d\t", a[7]);
          printf("%d\t", a[8]);
          printf("%d\t", a[9]);
      
          printf("\n");
      }
      
      >gcc -S -std=c11 -Wall -c main.c && gcc main.o -o main.exe && main
      1       1       1       1       1       1       1       1       1       1
      
      ; ...
          movl    %ecx, 16(%rbp)
          movq    %rdx, 24(%rbp)
          call    __main
          movl    $1, -48(%rbp)
          movl    $1, -44(%rbp)
          movl    $1, -40(%rbp)
          movl    $1, -36(%rbp)
          movl    $1, -32(%rbp)
          movl    $1, -28(%rbp)
          movl    $1, -24(%rbp)
          movl    $1, -20(%rbp)
          movl    $1, -16(%rbp)
          movl    $1, -12(%rbp)
          movl    -48(%rbp), %eax
          movl    %eax, %edx
          leaq    .LC0(%rip), %rcx
          call    printf
          movl    -44(%rbp), %eax
          movl    %eax, %edx
          leaq    .LC0(%rip), %rcx
          call    printf
      ; ...
      

      但请注意,值已移至RBP!中包含的地址


      最简单的解决方案(可能是可读性、可维护性和编译器优化的最佳解决方案):删除 register 关键字!

      int main(int argc, char *argv[]) {
          int a[10];
          int i;
          for(i=0;i<10;i++){
              a[i]=1;
          }
          for(i=0;i<10;i++){
              printf("%d\t", a[i]);
          }
          printf("\n");
      }
      
      ; ...
          movl    %ecx, 16(%rbp)
          movq    %rdx, 24(%rbp)
          call    __main
          movl    $0, -4(%rbp)
          jmp .L2
      .L3:
          movl    -4(%rbp), %eax
          cltq
          movl    $1, -48(%rbp,%rax,4)
          addl    $1, -4(%rbp)
      .L2:
          cmpl    $9, -4(%rbp)
          jle .L3
          movl    $0, -4(%rbp)
          jmp .L4
      .L5:
          movl    -4(%rbp), %eax
          cltq
          movl    -48(%rbp,%rax,4), %eax
          movl    %eax, %edx
          leaq    .LC0(%rip), %rcx
          call    printf
          addl    $1, -4(%rbp)
      .L4:
          cmpl    $9, -4(%rbp)
          jle .L5
      ; ...
      
      >gcc -std=c11 -Wall -c main.c && gcc main.o -o main.exe && main
      1       1       1       1       1       1       1       1       1       1
      

      【讨论】:

      • 这个答案在meta讨论。
      猜你喜欢
      • 2018-08-29
      • 2021-01-16
      • 2012-06-24
      • 2016-11-25
      • 2011-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多