longjmp前后的自动变量 、全局变量和易变变量(APUE)

寄存器变量:

当对一个变量频繁被读写时,需要反复访问内存,从而花费大量的存取时间。为此,C语言提供了一种变量,即寄存器变量。这种变量存放在CPU的寄存器中,使用时,不需要访问内存,而直接从寄存器中读写,从而提高效率。寄存器变量的说明符是register。对于循环次数较多的循环控制变量及循环体内反复使用的变量均可定义为寄存器变量,而循环计数是应用寄存器变量的最好候选者。
    (1) 只有局部自动变量和形参才可以定义为寄存器变量。因为寄存器变量属于动态存储方式,凡需要采用静态存储方式的量都不能定义为寄存器变量,包括:模块间全局变量、模块内全局变量、局部static变量; 
    (2) register是一个"建议"型关键字,意指程序建议该变量放在寄存器中,但最终该变量可能因为条件不满足并未成为寄存器变量,而是被放在了存储器中,但编译器中并不报错(在C++语言中有另一个"建议"型关键字:inline)。 

代码摘自APUE2:

 1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <setjmp.h>
4
5 static void f1(int, int, int, int);
6 static void f2(void);
7
8 static jmp_buf jmpbuffer;
9 static int globval;
10
11 int main(void)
12 {
13 int autoval;
14 register int regival;
15 volatile int volaval;
16 static int statval;
17
18 globval = 1; autoval = 2; regival = 3; volaval = 4; statval = 5;
19
20 if (setjmp(jmpbuffer) != 0) {
21 printf ("after longjmp: \n");
22 printf ("globval = %d, autoval = %d, regival = %d, volaval = %d, statval = %d\n",
23 globval, autoval, regival, volaval, statval);
24 exit (0);
25 }
26
27 globval = 95; autoval = 96; regival = 97; volaval = 98; statval = 99;
28 f1(autoval, regival, volaval, statval);
29 exit (0);
30 }
31
32 static void f1(int i, int j, int k, int l)
33 {
34 printf ("in f1():\n");
35 printf ("globval = %d, autoval = %d, regival = %d, volaval = %d, statval = %d\n", globval, i, j, k, l);
36 f2();
37 }
38
39 static void f2(void)
40 {
41 longjmp(jmpbuffer, 1);
42 }

不经过优化编译程序:

1 $ ./a.out 
2 in f1()
3 globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99
4 afterjmp
5 globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99

经过优化之后的情况:

1 $gcc -O test_longjmp.c 
2 $ ./a.out
3 in f1()
4 globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99
5 afterjmp
6 globval = 95, autoval = 2, regival = 3, volaval = 98, statval = 99

可以看出,经过优化之后,全局变量、静态变量和易变变量的值没有改变,就是说不受优化的影响,在调用longjmp之后,它们的值是最后改变的值。不进行优化时,5个变量都存放在存储器中,各个变量的读取和写入都需要在内存中进行,而忽略了regival变量的register寄存器变量说明。而在优化了之后 ,我用objdump -S a.out看到:

1  80484ce:   c7 05 fc a0 04 08 01    movl   $0x1,0x804a0fc
2 148 80484d5: 00 00 00
3 149 80484d8: c7 44 24 2c 04 00 00 movl $0x4,0x2c(%esp)
4 150 80484df: 00
5 151 80484e0: c7 05 40 a0 04 08 05 movl $0x5,0x804a040

初始化时,并没有发现对autoval和regival的操作,再看看longjmp之后:

1 161  804850e:   8b 44 24 2c             mov    0x2c(%esp),%eax
2 162 8048512: 8b 15 40 a0 04 08 mov 0x804a040,%edx
3 163 8048518: 89 54 24 18 mov %edx,0x18(%esp)
4 164 804851c: 89 44 24 14 mov %eax,0x14(%esp)
5 165 8048520: c7 44 24 10 03 00 00 movl $0x3,0x10(%esp)
6 166 8048527: 00
7 167 8048528: c7 44 24 0c 02 00 00 movl $0x2,0xc(%esp)
8 168 804852f: 00
9 169 8048530: a1 fc a0 04 08 mov 0x804a0fc,%eax

可以看到,直接用立即数赋给了栈指针指向的空间,所以在这段操作中,优化的手段就是将局部变量和寄存器变量用立即数代替。

至于为什么autoval和regival会是2和3,APUE是这样解释的,“存放在存储器中的变量将具有longjmp时的值,而在cpu和浮点寄存器中的变量则恢复为调用setjmp时的值。”

至此,还是有疑问,优化之后调用了两次立即数,立即数放在指令中,CPU从代码区取指,并取得立即数,而APUE中提到“autoval和regival”都存放在寄存器中?这是什么情况呢?



posted @ 2011-12-17 14:08  leealways87  阅读(525)  评论(0编辑  收藏  举报