C语言变量类型与内存

来源:互联网 发布:淘宝店铺首页全屏大图 编辑:程序博客网 时间:2024/05/16 14:21

摘录:http://www.cppblog.com/oosky/archive/2006/01/21/2958.html
http://www.cnblogs.com/mjios/archive/2013/03/21/2973719.html

C语言有丰富的数据类型和运算符,因此计算能力非常强大,计算过程中使用的值一般用变量来存储。变量也是有分类型的,不同类型的变量有不同的存储类型、不同的生命周期、不同的作用域,C语言也提供了一些关键字来设置变量的属性(比如设置存储类型、生命周期)。

一、变量的作用域

C语言根据变量作用域的不同,将变量分为局部变量和全局变量。

1.局部变量

  • 定义:在函数内部定义的变量,称为局部变量。形式参数也属于局部变量。
  • 作用域:局部变量只在定义它的函数内部有效,即局部变量只有在定义它的函数内部使用,其它函数不能使用它。

2.全局变量

  • 定义:在所有函数外部定义的变量,称为全局变量。
  • 作用域:全局变量的作用范围是从定义变量的位置开始到源程序结束,即全局变量可以被在其定义位置之后的其它函数所共享。
int a;//全局变量int main () {    int b;//局部变量 }   

根据变量定义地方不同,决定了该变量的作用范围,因此有了局部和全局之分。

二、变量的生命周期

C语言根据变量的存储类型的不同,可以把变量分为:自动变量、静态变量、寄存器变量。变量的存储类型决定了变量何时创建、何时销毁以及它的值能保持多久,也就是决定了变量的生命周期。

1.自动变量

  • 定义:被关键字auto修饰的局部变量都是自动变量,但是极少使用这个关键字,基本上是废的,因为所有的局部变量在默认情况下都是自动变量。
  • 生命周期:在程序执行到声明自动变量的代码块(函数)时,自动变量才被创建;当自动变量所在的代码块(函数)执行完毕后,这些自动变量就会自行销毁。如果一个函数被重复调用,这些自动变量每次都会重新创建。
void test(int a, int b) {     int c = a + b;     auto int d; }

函数中的形参ab,变量中定义的cd都是局部变量。

2.静态变量

  • 定义:所有的全局变量都是静态变量,或被关键字static修饰的局部变量也是静态变量。
  • 生命周期:静态变量在程序运行之前创建,在程序的整个运行期间始终存在,直到程序结束。
#include <stdio.h>  int a;  void test() {      static int b = 0;      b++;      int c = 0;     c++;     printf("b=%d, c=%d \n", b, c); } int main() {     int i;    // 连续调用3次test函数     for (i = 0; i<3; i++) {         test();     }     return 0; }

第3行的变量a、第6行的变量b都是静态变量,第9行的变量c、第16行的变量i是自动变量。

因为第6行的变量b是静态变量,所以它只会被创建一次,而且生命周期会延续到程序结束。因为它只会创建一次,所以第6行代码只会执行一次,下次再调用test函数时,变量b的值不会被重新初始化为0。

注意:虽然第6行的变量b是静态变量,但是只改变了它的存储类型(即生命周期),并没有改变它的作用域,变量b还是只能在test函数内部使用。

输出结果:

b=1,c=1b=2,c=1b=3,c=1

3.寄存器变量

  • 定义:被关键字register修饰的自动变量都是寄存器变量;只有自动变量才可以是寄存器变量,全局变量和静态局部变量不行;寄存器变量只限于int、char和指针类型变量使用;
  • 生命周期:因为寄存器变量本身就是自动变量,所以函数中的寄存器变量在调用该函数时占用寄存器中存放的值,当函数结束时释放寄存器,变量消失。
int main() {    register int a;     return 0; }

注意:由于计算机中寄存器数目有限,不能使用太多的寄存器变量。如果寄存器使用饱和时,程序将寄存器变量自动转换为自动变量处理
为了提高运算速度,一般会将一些频繁使用的自动变量定义为寄存器变量,这样程序尽可能地为它分配寄存器存放,而不用内存

三、变量与内存

变量定义方式的不同,其在内存中的保存方式也不相同,在程序中也体现为不同的作用域和生命周期。
有3个地方可以用于存储变量:普通内存(静态内存)、运行时堆栈(动态内存)、硬件寄存器。

1.堆栈

自动变量是存储在堆栈中的。

  • 申请方式
    1.栈: 由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
    2.堆: 需要程序员自己申请,并指明大小,在c中malloc函数
    如p1 = (char *)malloc(10);
    在C++中用new运算符
    如p2 = (char *)malloc(10);
    但是注意p1、p2本身是在栈中的。

  • 堆和栈的区别
    1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
    2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表

  • 申请后系统的响应
    1.栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
    2.堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

  • 申请大小的限制
    1.栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
    2.堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

  • 申请效率的比较
    1.栈由系统自动分配,速度较快。但程序员是无法控制的。
    2.堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.

  • 堆和栈中的存储内容
    1.栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
    当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
    2.堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

  • 总结
    局部变量定义后,便存入堆栈。比如函数开始时,首先会将下一执行语句执行地址入栈,接着形参入栈(从右往左),接着是函数中的局部变量(静态变量不入栈)。函数执行结束后,便按后进先出的原则,局部变量、形参逐个出栈,直到最后栈顶指针指向最开始存的下一语句执行地址,程序继续。
    由此可以理解看出,程序的运行本质就是对内存的操作,而不同的内存存储发式决定了程序的执行方式。
    一般自定义的局部变量存于栈;由程序员申请创建的内存空间的,如使用malloc,存于堆。
    栈的地址是连续的,而堆是用链表来存储的空闲内存地址的,自然是不连续的。栈是一种线性结构,堆是一种链式结构,是两种不同的动态数据区。
    进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干

├———————┤高端内存区域
│ …… │
├———————┤
│ 栈│ 向低地址扩展
├———————┤
│ …… │
├———————┤

├———————┤
│ 堆│ 向高地址扩展
├———————┤
│ …… │
├———————┤低端内存区域

2.静态内存

静态变量是存储在静态内存中的,也就是不属于堆栈。
静态变量的生命周期贯穿整个程序, 程序结束后由系统释放。

例子:

//main.cpp int a = 0; //全局初始化区 char *p1; //全局未初始化区 main() { int b; //栈 char s[] = "abc";// 栈 char *p2; //栈 char *p3 = "123456"; //123456\0在常量区,p3在栈上。 static int c =0//全局(静态)初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); //分配得来得10和20字节的区域就在堆区,但是注意p1、p2本身是在栈中的 strcpy(p1, "123456"); //123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 } 

3.硬件寄存器

寄存器变量存储在硬件寄存器中,比存储在内存中的变量访问效率更高。

0 0
原创粉丝点击