一般认为在C中分为这几个存储区:
1.栈:由编译器自动分配并释放内存空间,如局部变量,函数参数等;
2.堆:一般由程序员分配释放内存空间(如C语言中的malloc和C++中的new)。若程序员不释放,程序结束时可能由OS回收。堆中的所有东西都是匿名的,这样不能按名字访问,而只能通过指针访问,比如以下语句:
#include
struct node
{
int a;
int b;
};
int main(void)
{
struct node *p;//如果改为“struct node p;”,下一条语句改为“p=(struct node )malloc(sizeof(struct node));”,程序运行会出错
p=(struct node *)malloc(sizeof(struct node));
return 0;
}
3.全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和初始化的静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。如程序外声明的全局变量,static声明的静态变量就存放在该区。在C++中是不区分初始化区和未初始化区的。程序结束释放。
4.还有一块常量区。它是静态区的一部分,主要存放常量(如const声明的常量数据),形如“abcdef”的字符串字面值也存放在此处。
5.register声明的变量存放在寄存器中,寄存器不在内存中,而是比内存更接近CPU,因此执行效率更高。但每一台电脑的寄存器个数是有限的,其存储的变量也是有限的。
比如:
int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈区
char s[] = "abc";
//abc在内存中共有两个,一个是字符串字面值“abc ”,存放在常量区;另一个是字符
数组s[],其数组内也存放着“abc ”,但它们被存放在栈中。
char *p2; //栈区
char *p3 = "123456";
//“123456 ”作为字符串字面值在常量区,p3在栈区
static int c = 0;
//全局(静态)初始化区
p1 = (char *)malloc(10); p2 =
(char *)malloc(20); //分配得来的10和20字节的区域就在堆区。
}
除此之外,还应注意以下几点:
1. 栈是先入后出的,由高地址向低地址生长,堆是由低地址向高地址生长,详细介绍请参照数据结构。运行以下程序就一目了然了:
#include
#include
struct node
{
int
a;
int
b;
int
c;
int
d;
};
int main(void)
{
struct node
*m;
int
e;
int
f;
int
g;
int
h;
m=(struct node
*)malloc(sizeof(struct node));
puts("堆的存储:由低地址到高地址");
printf("m->a的地址:%dn",&m->a);
printf("m->b的地址:%dn",&m->b);
printf("m->c的地址:%dn",&m->c);
printf("m->d的地址:%dn",&m->d);
puts("栈的存储:由高地址向低地址");
printf("e的地址:%dn",&e);
printf("f的地址:%dn",&f);
printf("g的地址:%dn",&g);
printf("h的地址:%dn",&h);
return
0;
}
2.栈的空间大小是有限的,VC默认是2M。栈不够用的情况一般是程序中分配了大量数组和递归函数的层次太深。有一点必须知道,当一个函数调用完返回后它会释放该函数中所有的栈空间。还应注意,当函数执行完毕后并不是立刻释放它开辟的栈空间,而是将栈数据继续留在内存中以保留现场,直到调用另一个函数时栈空间才被清空。请看以下程序:
#include
int *fun()
{
int *p,a=3;
p=&a;
return p;
}
int main(void)
{
int *p;
p=fun();
printf("%dn",*p);
return 0;
}
当执行完fun()函数之后,存放在栈中的局部变量a理应被释放,但输出结果仍然是3,也就是说p所指向的地址中仍然保存着a的数据,即保存现场(但当函数一旦执行返回值操作,存放在栈中的自动变量(即a)就失效了(见以下注意5),因此在程序外引用a仍是无效的)。但如果对程序做以下修改,结果就不同了:
#include
int *fun()
{
int *p,a=3;
p=&a;
return p;
}
int main(void)
{
int *p;
p=fun();
printf("%dn",*p);
printf("%dn",*p);
return 0;
}
你会看到,程序输出的第一个*p的结果为3,但第二个*p的结果却为1245056,是个无效值。说明当调用新函数printf()之后,原函数中的栈数据被释放了。
3.在栈上存取数据比通过指针在堆上存取数据效率高。
4.堆是动态分配内存的,并且你可以分配使用很大的内存。但是用不好会产生内存泄露。并且频繁地调用malloc()和free()会产生内存碎片,而栈不会产生碎片。
5.堆(heap)和栈是C/C++编程不可避免会碰到的两个基本概念,他们都是基本的数据结构。具体地说,现代计算机(串行执行机制)都直接在代码底层支持栈的数据结构。这体现在,有专门的寄存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操作。这种机制的特点是效率高,支持的数据有限,一般是整数、指针、浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构。因为栈的这种特点,对栈的使用在程序中是非常频繁的。对子程序的调用就是直接利用栈完成的。机器的call指令里隐含了把返回地址推入栈,然后跳转至子程序地址的操作,而子程序中的ret指令则隐含从堆栈中弹出返回地址并跳转的操作。C/C++中的自动变量是直接利用栈的例子,这也就是为什么当函数返回时,该函数的自动变量失效的原因。