学习[C语言] register变量修饰符

163 阅读2分钟

在计算机程序运行过程中不可避免地使用寄存器,缓存,内存,外存分别存放运行时的一些数据。但他们的速度却存在很大差异。速度从高到低为 寄存器 > 缓存 > 内存 > 外存。

在C语言中给我们提供了一个关键字用来手动标明哪些变量存储在寄存器,这样可以让一些常用的变量存到寄存器中,从而提高变量的访问速度,并以此提高程序的运行速度。

如何使用register关键字呢

如下图所示,直接将register添加到变量声明的起始位置即可。

int main(int argc, char* argv, char* envp){
    register int register_num = 0x8888;
    exit(0);
}

使用register关键字后,程序到低发生哪些变化了呢

首先我们准备一个测试程序

#include<stdio.h>

int main () {
    int num = 0x7777;
    register register_num = 0x8888;
    printf("num: %d register: %d\n",num, register_num);
    exit(0);
}

我们通过objdump -S ./a.out可以看到如下汇编代码

0000000000001149 <main>:
    1149:       55                      push   %rbp
    114a:       48 89 e5                mov    %rsp,%rbp
    114d:       53                      push   %rbx
    114e:       48 83 ec 18             sub    $0x18,%rsp
    1152:       c7 45 ec 77 77 00 00    movl   $0x7777,-0x14(%rbp) 
    1159:       bb 88 88 00 00          mov    $0x8888,%ebx
    115e:       8b 45 ec                mov    -0x14(%rbp),%eax
    1161:       89 da                   mov    %ebx,%edx
    1163:       89 c6                   mov    %eax,%esi
    1165:       48 8d 05 98 0e 00 00    lea    0xe98(%rip),%rax        # 2004 <_IO_stdin_used+0x4>
    116c:       48 89 c7                mov    %rax,%rdi
    116f:       b8 00 00 00 00          mov    $0x0,%eax
    1174:       e8 b7 fe ff ff          call   1030 <printf@plt>
    1179:       bf 00 00 00 00          mov    $0x0,%edi
    117e:       e8 bd fe ff ff          call   1040 <exit@plt>

在这段汇编movl $0x7777,-0x14(%rbp)是将0x7777移动到内存地址中,这个地址是在rbp中存放,将rbp向左偏移0x14字节。

而这段汇编的意思是mov $0x8888,%ebx是将0x8888移动到寄存器ebx中。

int main () {
    int num = 0x7777;                 // movl   $0x7777,-0x14(%rbp)
    register register_num = 0x8888;   // mov    $0x8888,%ebx
    printf("num: %d register: %d\n",num, register_num);
    exit(0);
}

我们可以看到使用register关键字和不使用register在编译器生成的汇编代码中就有很大的不同,使用的汇编指令直接导致了该变量是放在内存还是寄存器中。

那么使用register的限制和代价是什么呢?

  1. register修饰符只能修饰局部变量,不能声明全局变量和静态变量
  2. register只能修饰单个字符,整数,指针或者数组等简单类型,不能声明结构体,联合体,等复杂类型。
  3. register声明的变量都会声明在寄存器中,但是寄存器的数量有限,编译器会自动将部分变量放入内存。
  4. 编译器会自动优化变量,自动将变量放到寄存器中,如果手动指派可能影响程序的运行速度。