我对register
在C中使用关键字感到困惑。通常会告诉人们,不需要像在stackoverflow这个问题中那样使用它。
由于现代编译器,此关键字在C中是完全多余的吗?还是在某些情况下它仍然有用?如果是,在哪些情况下使用register
关键字实际上会有所帮助?
const
关键字也有同样的想法,但是这个问题证明我错了。因此,我将拭目以待。
const
关键字与register有所不同。
我对register
在C中使用关键字感到困惑。通常会告诉人们,不需要像在stackoverflow这个问题中那样使用它。
由于现代编译器,此关键字在C中是完全多余的吗?还是在某些情况下它仍然有用?如果是,在哪些情况下使用register
关键字实际上会有所帮助?
const
关键字也有同样的想法,但是这个问题证明我错了。因此,我将拭目以待。
const
关键字与register有所不同。
Answers:
就语言而言,这不是多余的,只是通过使用它,您告诉编译器,您“倾向于”将变量存储在寄存器中。但是,绝对没有零保证,这将在运行时实际发生。
如前所述,编译器优化程序实际上使register
关键字已过时,而不是防止混叠。但是,有整个代码库都是-O0
在关闭优化的情况下编译的(在gcc-speak中)。对于这样的代码,register
关键字可以产生很大的效果。具体而言,如果使用关键字声明,则原本会在堆栈上占用插槽的变量(即所有函数参数和自动变量)可以直接放置到寄存器中register
。
这是一个真实的示例:假设发生了一些数据库检索,并且检索代码已将检索到的元组填充到C结构中。此外,假设需要将此C结构的某些子集复制到另一个结构-也许第二个结构是表示存储在数据库中的元数据的缓存记录,由于内存限制,该元数据仅缓存所存储的每个元数据记录的子集在数据库中。
给定一个获取指向每种结构类型的指针的函数,其唯一的工作就是将某些成员从初始结构复制到第二个结构:结构指针变量将存在于堆栈中。当从一个结构的成员到另一结构的成员进行分配时,对于每个分配,该结构的地址将被加载到寄存器中,以执行对正在复制的结构的成员的访问。如果要使用register
关键字声明结构指针,则结构的地址将保留在寄存器中,从而为每个分配有效地切出了“将地址装入寄存器”指令。
同样,请记住,上面的描述适用于未优化的代码。
在16位计算机时代,经常需要多个寄存器来执行32位乘法和除法。随着浮点单元被集成到芯片中,然后“ 64位”架构被“接管”,寄存器的宽度和数量都在增加。这最终导致对CPU的完整重新架构。请参阅在Wikipedia上注册文件。
简而言之,如果您使用的是64位X86或ARM芯片,则需要花费一些时间才能弄清楚实际情况。如果您使用的是16位嵌入式CPU,则实际上可能会让您有所收获。但是,大多数小型嵌入式芯片对时间的要求并不严格-微波炉可能每秒对触摸板进行采样10,000次-不会给4Mhz CPU造成压力。
为了确定register关键字是否有意义,微小的示例代码将不起作用。这是一个C代码,向我建议,register关键字仍然有意义。但我不知道Linux上的GCC可能有所不同。寄存器int k&l是否存储在CPU寄存器中?Linux用户(尤其是)应该使用GCC和优化进行编译。在Borland bcc32中,register关键字似乎起作用(在此示例中),因为&运算符为寄存器声明的整数提供错误代码。注意!在Windows上使用Borland的一个小例子就不是这种情况!为了真正了解编译器是否进行了优化,它不仅仅是一个小例子。空循环不会做!不过,如果可以使用&运算符读取地址,则该变量不会存储在CPU寄存器中。但是,如果无法读取寄存器声明的变量(在编译时导致错误代码)-我必须假定register关键字实际上确实将变量放入了CPU寄存器中。我不知道在不同的平台上它可能有所不同。(如果可行,寄存器声明中的“滴答声”数将大大减少。
/* reg_or_not.c */
#include <stdio.h>
#include <time.h>
#include <stdlib> //not requiered for Linux
#define LAPSb 50
#define LAPS 50000
#define MAXb 50
#define MAX 50000
int main (void)
{
/* 20 ints and 2 register ints */
register int k,l;
int a,aa,b,bb,c,cc,d,dd,e,ee,f,ff,g,gg,h,hh,i,ii,j,jj;
/* measure some ticks also */
clock_t start_1,start_2;
clock_t finish_1,finish_2;
long tmp; //just for the workload
/* pointer declarations of all ints */
int *ap, *aap, *bp, *bbp, *cp, *ccp, *dp, *ddp, *ep, *eep;
int *fp, *ffp, *gp, *ggp, *hp, *hhp, *ip, *iip, *jp, *jjp;
int *kp,*lp;
/* end of declarations */
/* read memory addresses, if possible - which can't be done in a CPU-register */
ap=&a; aap=&aa; bp=&b; bbp=&bb;
cp=&c; ccp=&cc; dp=&d; ddp=ⅆ
ep=&e; eep=ⅇ fp=&f; ffp=&ff;
gp=&g; ggp=≫ hp=&h; hhp=&hh;
ip=&i; iip=ⅈ jp=&j; jjp=&jj;
//kp=&k; //won't compile if k is stored in a CPU register
//lp=&l; //same - but try both ways !
/* what address , isn't the issue in this case - but if stored in memory some "crazy" number will be shown, whilst CPU-registers can't be read */
printf("Address a aa: %u %u\n",a,aa);
printf("Address b bb: %u %u\n",b,bb);
printf("Address c cc: %u %u\n",c,cc);
printf("Address d dd: %u %u\n",d,dd);
printf("Address e ee: %u %u\n",e,ee);
printf("Address f ff: %u %u\n",f,ff);
printf("Address g gg: %u %u\n",g,gg);
printf("Address h hh: %u %u\n",h,hh);
printf("Address i ii: %u %u\n",i,ii);
printf("Address j jj: %u %u\n\n",j,jj);
//printf("Address k: %u \n",k); //no reason to try "k" actually is in a CPU-register
//printf("Address l: %u \n",l);
start_2=clock(); //just for fun
/* to ensure workload */
for (a=1;a<LAPSb;a++) {for (aa=0;aa<MAXb;aa++);{tmp+=aa/a;}}
for (b=1;b<LAPSb;b++) {for (bb=0;bb<MAXb;bb++);{tmp+=aa/a;}}
for (a=1;c<LAPSb;c++) {for (cc=0;cc<MAXb;cc++);{tmp+=bb/b;}}
for (d=1;d<LAPSb;d++) {for (dd=0;dd<MAXb;dd++);{tmp+=cc/c;}}
for (e=1;e<LAPSb;e++) {for (ee=0;ee<MAXb;ee++);{tmp+=dd/d;}}
for (f=1;f<LAPSb;f++) {for (ff=0;ff<MAXb;ff++);{tmp+=ee/e;}}
for (g=1;g<LAPSb;g++) {for (gg=0;gg<MAXb;gg++);{tmp+=ff/f;}}
for (h=1;h<LAPSb;h++) {for (hh=0;hh<MAXb;hh++);{tmp+=hh/h;}}
for (jj=1;jj<LAPSb;jj++) {for (ii=0;ii<MAXb;ii++);{tmp+=ii/jj;}}
start_1=clock(); //see following printf
for (i=0;i<LAPS;i++) {for (j=0;j<MAX;j++);{tmp+=j/i;}} /* same double loop - in supposed memory */
finish_1=clock(); //see following printf
printf ("Memory: %ld ticks\n\n", finish_1 - start_1); //ticks for memory
start_1=clock(); //see following printf
for (k=0;k<LAPS;k++) {for (l=0;l<MAX;l++);{tmp+=l/k;}} /* same double loop - in supposed register*/
finish_1=clock(); //see following printf
printf ("Register: %ld ticks\n\n", finish_1 - start_1); //ticks for CPU register (?) any difference ?
finish_2=clock();
printf ("Total: %ld ticks\n\n", finish_2 - start_2); //really for fun only
system("PAUSE"); //only requiered for Windows, so the CMD-window doesn't vanish
return 0;
}