为什么不能使用循环访问寄存器数组

时间:2019-11-14 08:43:43

标签: c arrays cpu-registers

我有以下验证码

{
    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]);                                            
       ^

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

3 个答案:

答案 0 :(得分:2)

在C语言中,不能将&运算符与寄存器变量一起使用。这就是为什么编译器抛出错误。

C standard 6.7.1 Storage-class specifiers脚注128:

  

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

答案 1 :(得分:0)

对于第二个错误:printf("%d\t",&a[i]);实际上应该是printf("%d\t",a[i]);&的意思是“地址”,但您需要该值。 但是,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

答案 2 :(得分:0)

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

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

  

...如果数组对象具有寄存器存储类,则行为未定义。