关于C的变量

我们可以简单的把一个编译好程序认为是由代码区和数据区组成,这是因为数据区又可以分区已初始化数据区,未初始化数据区,虽然这样叫是不规范的但是总的来说都可以看做是在数据区的,可以很直观的认为程序运行所需要的数据是存储在数据区的,但是事实是并不是所有的数据都是在数据区中存储的。数据区中存储都是持久化的存储对象,也就是说这次使用这个数据后需要保留对它这次操作的结果。

但是在C程序里面,变量在定义要么是在函数外面,要么是在函数里面,main函数也好,用户自定义的函数也好,都服从这个规律。编译器对代码编译之后把你所定义的变量放在哪里是要看你定义的变量在哪里定义的、长的什么样子了。在哪里定义的?一般要么是在函数外面,要么是在函数内部(这里的内部包含了函数参数列表);长的什么样子呢?也就是说除了简单的类似 “int a; “这样的,还有没有其他的关键字修饰你定义的变量了,例如static 等。

分清变量的存储位置和存储类型,将对我们理解C语言里其他的东西很有用。下面逐条分析一下吧。

1、             在函数外面定义的变量,顾名思义:外部变量(extern  variable)。有时候前面会多出来个extern ,如“extern int a ;“,extern这个关键字只不过是扩大了变量的作用域范围,对外部变量的存储位置是没有影响的。一般说来,不管前面是否有extern,你在函数外面(包括main函数)定义的变量都是放在程序的数据区中的固定位置的。

2、             在函数内部定义的变量,顾名思义:内部变量(internal  variable)。一般有人会认为内部变量是放置在程序的堆栈里,这种说法不算错,但是只说对了一半。内部变量有两种:一种是静态的内部变量,一种是动态的。静态的是在定义时有个关键字static出现。而动态的则没有。例如:

void  fun(int arg)

{

  int a ;

static int b;

}

很明显,a是动态的,b是静态的。

静态的内部变量的存储位置和外部变量是一样的。而动态变量的存储位置是在调用函数时所使用的堆栈中,通常对函数内部的动态变量的存取就是在堆栈中操作。

这里举例没有讲到参数列表里面的变量arg ,其实arg 的位置是和动态变量一样的。

用一个示例说明一下上面两点。程序的编译环境是VC6.0.

#include<stdio.h>

/*

*file=ex1.c

*date =08.6.2

*/

/*定义外部变量*/

 int ext=5;

/*定义函数*/

 void fun(int arg)

 {

        int a=1;       /* a 是动态内部变量*/

        static int b=1;/* b 是静态内部变量*/

        a+=arg;

        b+=arg;

        printf("a = %d/nb = %d/n",a,b);

 }

int main(int argc,char ** argv)

{

       fun(ext);/*第一次调用fun*/

       fun(ext);/*第二次调用fun*/

       return 0;

}

这个程序的结果只要是有点C经验的都会知道是什么。第一次调用fun函数输出的是 a = 6  b =  6

第二次调用fun函数输出的是 a = 6  b = 11

但是我要用编译生成的会变代码解释一遍相信大家会更清楚,因为我就是看了汇编后的代码对这个才有了更清楚的认识。汇编后的代码可以在你调试程序时的反汇编窗口里面看到。

下面只是选取了反汇编窗口里面最主要的一部分来进行说明,用C++的注释风格进行了注释说明,下面最左边的一列是程序代码的二进制地址。右边对应的就是在这些地址中放的指令。

如果对intel汇编熟悉那理解起来就很容易了。

@ILT+0(_fun):                        //fun函数在汇编后名字变为@ILT+0

00401005   jmp         fun (00401020)

@ILT+5(_main):                       //main函数在汇编后名字变为@ILT+5

0040100A   jmp         main (004010a0)//整个程序的入口地址是0040100A

                                                             //PE文件头中的信息可以知道这个地址

0040100F   int         3

00401010   int         3

00401011   int         3

00401012   int         3

00401013   int         3

00401014   int         3

00401015   int         3

00401016   int         3

00401017   int         3

00401018   int         3

00401019   int         3

0040101A   int         3

0040101B   int         3

0040101C   int         3

0040101D   int         3

0040101E   int         3

0040101F   int         3

--- c:/ex1.c  -----------------------------------------------------------------------

1:    #include<stdio.h>

2:    /*

3:    *file=ex1.c

4:    *date =08.6.2

5:    */

6:    /*定义外部变量*/

7:     int ext=5;

8:    /*定义函数*/

9:     void fun(int arg)

10:    {

00401020   push        ebp         //下面这几行代码跟main函数刚开始时差不多,不

                        //fun函数为局部变量留了0x44H个字节的空间。

00401021   mov         ebp,esp

00401023   sub         esp,44h

00401026   push        ebx

00401027   push        esi

00401028   push        edi

00401029   lea         edi,[ebp-44h]

0040102C   mov         ecx,11h

00401031   mov         eax,0CCCCCCCCh

00401036   rep stos    dword ptr [edi]

11:        int a=1;       /* a 是动态内部变量*/

00401038   mov         dword ptr [ebp-4],1      //动态内部变量就放在堆栈的这个地方。

12:        static int b=1;/* b 是静态内部变量*/

13:        a+=arg;

0040103F   mov         eax,dword ptr [ebp-4]  //a放在eax寄存器中

00401042   add         eax,dword ptr [ebp+8]    //ebp+8地址放的是什么呢?,看看在

                                    //004010BD这个地址是什么指令吧,

                                                                         //其实这时的ebp+8地址中放的就是传

                                                                        //入的fun的实参:ext=5,指令结束后

                                                                        //eax就是6了。如果你对堆栈的使用 

                                                                         //有疑问,可以自己跟着程序的执行画

                                                                         //一下堆栈的使用情况。

                                          

00401045   mov         dword ptr [ebp-4],eax     //eax的值(6)赋给a了。

14:        b+=arg;                             //这条语句的执行跟上一条差不多,但

                           //是看看下条指令我们就知道b不是放

                           //在堆栈中了。

00401048   mov         ecx,dword ptr [b (00424a34)]

//从这条指令我们可以看出来b 的地址是00424a34,看看004010B8处的这条指

//令:系统是从地址00424a30处取外部变

//ext的值的,也就是说外部变量ext

//紧接着放着的就是静态内部变量b

0040104E   add         ecx,dword ptr [ebp+8]

00401051   mov         dword ptr [b (00424a34)],ecx   //这条指令执行后,b=6,

                                                                                   //00424a34地址处的内容是6

                                                                                   //当我们第二次调用fun函数

                                                                                    //后时,你也就理解了b为什么

                                                                                    //变成了11

15:        printf("a = %d/nb = %d/n",a,b);                 //下面的是调用printf函数输出

00401057   mov         edx,dword ptr [b (00424a34)]

0040105D   push        edx

0040105E   mov         eax,dword ptr [ebp-4]

00401061   push        eax

00401062   push        offset string "a = %d/nb = %d/n" (0042201c)

00401067   call        printf (00401100)

0040106C   add         esp,0Ch

16:    }

0040106F   pop         edi

00401070   pop         esi

00401071   pop         ebx

00401072   add         esp,44h

00401075   cmp         ebp,esp

00401077   call        __chkesp (00401180)                //检查堆栈

0040107C   mov         esp,ebp

0040107E   pop         ebp

0040107F   ret                                          //fun函数是在这里返回的。

--- No source file  -----------------------------------------------------------------

00401080   int         3

00401081   int         3

00401082   int         3

00401083   int         3

00401084   int         3

00401085   int         3

00401086   int         3

00401087   int         3

00401088   int         3

00401089   int         3

0040108A   int         3

0040108B   int         3

0040108C   int         3

0040108D   int         3

0040108E   int         3

0040108F   int         3

00401090   int         3

00401091   int         3

00401092   int         3

00401093   int         3

00401094   int         3

00401095   int         3

00401096   int         3

00401097   int         3

00401098   int         3

00401099   int         3

0040109A   int         3

0040109B   int         3

0040109C   int         3

0040109D   int         3

0040109E   int         3

0040109F   int         3

--- c:/ex1.c  -----------------------------------------------------------------------

17:   int main(int argc,char ** argv)

18:   {

004010A0   push        ebp        //保存ebp寄存器的内容,下面要用到这个寄存器

                                                //保存esp的内容,堆栈中用ebp来寻址,所以要

//保存

004010A1   mov         ebp,esp

004010A3   sub         esp,40h     //这里esp减去0x40H,是给局部变量留的空间

004010A6   push        ebx

004010A7   push        esi

004010A8   push        edi

004010A9   lea         edi,[ebp-40h] //ebp的值就是esp减去0x40H之前的值

004010AC   mov         ecx,10h

004010B1   mov         eax,0CCCCCCCCh

004010B6   rep stos    dword ptr [edi] //刚才给局部变量留了0x40H(64)个字节的空间,这

                                  //里都初始化为0xCCCCCCCCH,是按字初始化的, 

                                  //一共0x10H(16)个字.这段区域的值可以写程序用指

                 //针来查看一下。

19:       fun(ext);/*第一次调用fun*/

004010B8   mov         eax,[_ext (00424a30)]//ext是我们定义的一个外部变量,它的位

//置是固定的,这个指令是把ext的值放

// eax寄存器中。

004010BD   push        eax               //调用fun函数需要一个实参,这个实参的传

                            //入我们可以看到是通过把它压入到堆栈中

                            //传递的

004010BE   call        @ILT+0(_fun) (00401005)//这里就调用了函数@ILT+0,也就是fun,

                                                                           //现在跟着看看fun函数都是怎么处理的

004010C3   add         esp,4

20:       fun(ext);/*第二次调用fun*/

004010C6   mov         ecx,dword ptr [_ext (00424a30)]//这次调用fun函数是用ecx传参

004010CC   push        ecx

004010CD   call        @ILT+0(_fun) (00401005)

004010D2   add         esp,4

21:       return 0;

004010D5   xor         eax,eax

22:   }

004010D7   pop         edi

004010D8   pop         esi

004010D9   pop         ebx

004010DA   add         esp,40h

004010DD   cmp         ebp,esp

004010DF   call        __chkesp (00401180)

004010E4   mov         esp,ebp

004010E6   pop         ebp

004010E7   ret

代码实在是比较长,不知道能否忍着看完,主要是为了说明以上两点:简单的说除了外部变量和静态内部变量之外,其它的变量都没有在程序的静态存储区中存放,需要这些变量的时候是把它们放在堆栈中的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值