我有以下验证码
{
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]);
^
但是我提到了数组的索引位置,那时我可以访问数组的值。
是什么原因无法使用循环访问数组
答案 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中的规则,该规则指出:
...如果数组对象具有寄存器存储类,则行为未定义。