C语言中的变量大致可以分为全局变量,局部变量,堆变量和静态局部变量,这些不同的变量存储在不同的位置,有不同的生命周期。一般程序将内存分为数据段、代码段、栈段、堆段,这几类变量存储在不同的段中,造成了它们有不同的生命周期。

全局变量

全局变量的生命周期是整个程序的生命周期,随着程序的运行而存在,随着程序的结束而消亡,全局变量位于程序的数据段。每个应用程序有4GB的虚拟地址空间,在程序开始时系统将这个程序加载到内存中,为其分配内存,这个时候,会根据程序文件的内容,为全局变量分配内存,并为之进行初始化,当程序的生命周期结束时,系统回收进程所消耗的资源,这个时候,全局变量所占的内存被销毁。

下面来看一段具体的代码:

int i= 0;
int main(int argc, char* argv[])
{
printf("%d\n", i);
return 0;
}
11:       printf("%d\n", i);
00401268 mov eax,[i (00432e24)]
0040126D push eax
0040126E push offset string "%d\n" (0042e01c)

从上述的汇编代码中可以看到,i所对应的地址为0x00432e24,在调用全局变量时,使用的是一个具体的地址,但是并没有看对应初始化i变量的反汇编代码,这是因为在程序开始运行之前,在准备进程环境的时候就为i分配的了存储空间,并进行了初始化。另外在使用时采用的是直接寻址的方式,并没有用寄存器来进行间接寻址,从这点上来看,i变量的地址不会随着程序的运行而改变,这个地址一直可以使用,所以全局变量的生命周期与程序的生命周期相同。

静态变量

静态变量有两个作用,一是将变量名所能使用的区域限定在对应位置,比如我们在一个函数中定义了一个静态变量,那么久只能在这个函数中使用这个变量,二是静态变量的生命周期是全局的,不会随着堆栈环境的改变而改变,下面是一个简单的例子

int Func()
{
static int i = 0;
i++;
return i;
} int main()
{
printf("%d\n", Func());
printf("%d\n", Func());
return 0;
}
9:        static int i = 0;
10: i++;
00401268 mov eax,[_Ios_init+3 (00433e24)]
0040126D add eax,1
00401270 mov [_Ios_init+3 (00433e24)],eax
11: return i;

上面的汇编代码也采用的是直接寻址的方式,而这个静态变量的地址为0x433e24,与上面的全局变量的地址进行比较,我们可以看出,其实它也是在全局作用域的,在初始化时也没有发现有任何的初始化代码,所以我们可以说,它的生命周期也是全局的,但是由于static将其可见域限定在函数中,所以在函数外不能通过这个变量名来访问这块内存区域。

局部静态变量的工作方式

上面说到局部静态变量的生命周期不随函数的结束而结束,不管进入函数多少次,局部静态变量只有一个内存地址,而且只初始化一次,具体编译器是如何做到的,将用下面这一段代码来说明:

int test(int n)
{
static int i = n;
return i;
} int main(int argc, char* argv[])
{
for (int i = 0; i < 5; i++)
{
printf("%d\n", test(i));
}
return 0;
}
12:       static int i = n;
00401268 xor eax,eax
0040126A mov al,[`test'::`2'::$S25 (00433e24)];用一个字节存储了一个标志位
0040126F and eax,1
00401272 test eax,eax
00401274 jne test+3Eh (0040128e);当该标志位为1则表明进行了初始化,直接跳过初始化的步骤
00401276 mov cl,byte ptr [`test'::`2'::$S25 (00433e24)]
0040127C or cl,1;没有进行初始化的话,先初始化然后将标志位赋值为1
0040127F mov byte ptr [`test'::`2'::$S25 (00433e24)],cl
00401285 mov edx,dword ptr [ebp+8]
00401288 mov dword ptr [__pInconsistency+39Ch (00433e20)],edx
13: return i;
0040128E mov eax,[__pInconsistency+39Ch (00433e20)]

在上面这段代码中我们企图多次对静态变量进行初始化,但是通过运行程序最终得到的结果都是一样的,上述的代码并没有改变静态变量的值,通过查看汇编代码我们可以看到,编译器在处理局部静态变量时多用了一个字节的内存保存了一个标志位,当该静态变量进行了初始化的时候,就跳过初始化的代码,否则进行初始化并将标志位赋相应的值。

局部变量

局部变量,的生命周期随着函数的调用而存在,当函数结束时它的生命周期就结束了。在我的上一篇将函数的博客中,已经说明了它寻址方式和生命周期。在函数调用时,会首先根据函数中局部变量所占的空间,初始化栈环境,并对这些局部变量进行初始化,当函数调用完成后,会首先回收栈环境,这样局部变量所在的内存被回收,用于下一个函数调用或者用作其他用途,因为栈是动态变化的,为了防止使用不当造成程序错误,所以在函数外是不能使用函数中定义的局部变量。另外一个需要说明的就是在语句块内的局部变量,它的生命周期只在语句块中,但是真实的情况是,它所在的内存与局部变量相同,都是在函数栈中,它的生命周期只在语法层面上进行限制。

堆变量

堆变量需要程序员自己申请并释放,需要程序员自己管理,程序不会自动管理这些内存,当调用malloc或者new 的时候,系统分配一块内存,直到调用free 或者delete的时候才释放。

C语言中不同变量的访问方式的更多相关文章

  1. C语言中的数组的访问方式

    闲下来,写的代码,很是简单,不解释,代码如下: #include <stdio.h> int main(int argc, char **argv) { char cArray[] = & ...

  2. smarty中三种变量的访问方式

    在模板中smarty有三种变量,第一种,php分配的变量,第二种配置文件里的变量,第三种,PHP全局数组里的变量,配置文件里变量的访问方式可以是{#bgcolor#},"#"必须紧 ...

  3. Go语言入门(二)Go语言中的变量、常量、数据类型、流程控制以及函数

    Go语言中的变量 通常用var关键声明变量,有常规方式和简化方式. 常规方式: var name1 type1 name1 = value1 //赋值 简化方式: var name2 = value1 ...

  4. Go语言中的变量

    1 概述 变量(Variable)是程序运行过程中,内容可以变化(修改)的量,变量的功能是存储用户的数据,是计算机语言中能储存计算结果或能表示值抽象概念.变量,是通过变量的标识符定位值的过程.变量的内 ...

  5. 【R语言入门】R语言中的变量与基本数据类型

    说明 在前一篇中,我们介绍了 R 语言和 R Studio 的安装,并简单的介绍了一个示例,接下来让我们由浅入深的学习 R 语言的相关知识. 本篇将主要介绍 R 语言的基本操作.变量和几种基本数据类型 ...

  6. C语言中计算变量占用内存空间

    C语言中计算变量占用内存空间 在C语言中通常用[sizeof]运算符计算变量占内存空间,如下面的例子:

  7. C 语言中的变量为什么不能以数字打头

    C 语言中的变量为什么不能以数字打头? C 语言中的变量为什么不能以数字打头? 不要告诉我编译原理书上有.我暂时看不懂. 除了下面的解释外, “假如变量名允许以数字开头的话,那么语法分析器在解析一个全 ...

  8. go语言中在变量后加上接口是什么意思?

    如题刚刚开始学习go 语言有些不懂: a.Data = make(map[string]interface{}) 我认为它是在申请a.Data map为字符串类型的空间,那么它后面接一个空的inter ...

  9. C语言中结构体内存存储方式

    C语言中结构体内存存储方式 结构体的默认存储方式采用以最大字节元素字节数对其方式进行对齐,例如一个结构体中定义有char.int类型元素,则结构体存储空间按照int类型占用字节,如果还有double类 ...

随机推荐

  1. Codeforces Round #271 (Div. 2) F题 Ant colony(线段树)

    题目地址:http://codeforces.com/contest/474/problem/F 由题意可知,最后能够留下来的一定是区间最小gcd. 那就转化成了该区间内与区间最小gcd数相等的个数. ...

  2. 页面的新开页,window.open的hacker

    一.window.open如何进行hack 网上看的办法很多,归根接地还是不能解决掉,只有通过a标签的target属性 $obj.click(function(){  var newTab=windo ...

  3. redis 报Operation against a key holding the wrong kind of value警告的解决方法

    WRONGTYPE Operation against a key holding the wrong kind of value github:https://github.com/antirez/ ...

  4. MyBatis_查询缓存01

    一.查询缓存 查询缓存的使用,主要是为了提高查询访问速度.将用户对同一数据的重复查询过程简单化,不在每次均从数据库中查询获取结果数据,从而提高访问速度. MyBatis的查询缓存机制,根据缓存区的作用 ...

  5. [CSS]第一项和最后一项样式

    列表项的第一项距离顶部0.2rem,最后一项距离底部0.5rem .item:first-child { padding-top: .2rem; } .item:last-child { paddin ...

  6. Material使用02 图标MdIconModule、矢量图作为图标使用及改进

    1 MdIconModule模块的使用 1.1 在需要用到的模块中引入Material图标模块 import { BrowserModule } from '@angular/platform-bro ...

  7. webpack加载多级依赖时css、html文件不能正确resolve的问题

    在使用webpack+avalon以及avalon的mmRouter做SPA的时候,碰到一个困扰数周的问题:webpack加载多级依赖时出现了css文件和模板(html)文件不能正确resolve.原 ...

  8. 安装eclipse时跳转到网页提示JRE Missing

    可能的原因:jdk与安装的eclipse不兼容,可能64位机器安装了32位的jdk,导致64位的eclipse不能识别.

  9. npm发布vue组件流程

    初始化项目vue init webpack-simple XXX 定义组件略 发布配置1.package.json 2.webpack.config.js(注释部分为原配置) 发布1.登录 2.发布n ...

  10. PHP-无限级分类(迭代法创建)

    $area = array( array('id'=>1,'name'=>'安徽','parent'=>0), array('id'=>2,'name'=>'海淀','p ...