一直对程序的内存使用情况比较好奇,今天查了一番,有了一个大概认识,以下收集自网络
- 首先,常量与变量
- 内存分配
代码区:代码区就是存放程序代码数据区:数据区就是存放程序编译与执行过程中出现的变量与常量
静态数据区:在编译器进行编译的时候就为该变量分配的内存,存放在这个区的数据在程序全部执行结束后系统自动释放,生命周期贯穿于整个程序执行过程;全局变量以及静态变量存放在静态数据区。
动态数据区:
堆区:这部分的分配与释放由程序员自己管理,这是唯一一个由程序员自己决定变量生存周期的区间。可以用malloc,new,calloc,free,delete 等申请释放内存,如果程序员申请了空间,而忘记释放空间,会造成内存泄露,导致该内存段一直被占用而无法利用。
注意:常量的存放区域通常在程序区(程序区是只读,因此任何修改常量的行为都是非法的)。 有的系统,也将部分常量分配到静态数据区,比如字符串常量(有的系统也将其分配在程序区)。但是要记住一点,常量所在的内存空间都是受系统保护的,不能修改。对常量空间的修改将造成访问内存出错,一般系统都会提示。常量的生命周期一直到程序执行结束为止。栈区:存放函数的形参和局部变量,由编译器分配和自动释放,函数执行完后,局部变量和形参占用的空间会自动释放,效率比较高但是分配的容量有限
- 函数调用过程
- 内存分配和函数返回值的例子:
1. 内存分配问题: 根据实际情况安排内存位置若该变量为全局变量,则存放在静态数据区,这里指该变量为局部变量
static int b=0; // b在静态区 int a=1; // a在栈区 char s[]="123"; // s在栈区,“123”在栈区,其值可以被修改 char *s="123"; // s在栈区,“123”在常量区,其值不能被修改 int *p=new int; // p在栈区,申请的空间在堆区(p指向的区域) int *p=(int *)malloc(sizeof(int)); // p在栈区,p指向的空间在堆区2.test1#include<iostream> using namespace std; void test(int *p) { int b=2; p=&b; cout<<p<<endl; } int main(void) { int a=10; int *p=&a; cout<<p<<endl; test(p); cout<<p<<endl; return 0; }第一行输出和第三行输出的结果相同,与第二行输出的结果不同。从这里可以看出,当指针作为参数进行传递时传递的也只是一个值,只不过该值只一个地址,因此对于形参的改变并不影响实参。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值(这里是在说实参指针本身的地址值不会变)。3. test2
#include<iostream> using namespace std; char* test(void) { char str[]="hello world!"; return str; } int main(void) { char *p; p=test(); cout<<p<<endl; return 0; }输出结果可能是hello world!,也可能是乱码。出现这种情况的原因在于:在test函数内部声明的str数组以及它的值"hello world”是在栈上保存的,当用return将str的值返回时,将str的值拷贝一份传回,当test函数执行结束后,会自动释放栈上的空间,即存放hello world的单元可能被重新写入数据,因此虽然main函数中的指针p是指向存放hello world的单元,但是无法保证test函数执行完后该存储单元里面存放的还是hello world,所以打印出的结果有时候是hello world,有时候是乱码。
4. test3
#include<iostream> using namespace std; int test(void) { int a=1; return a; } int main(void) { int b; b=test(); cout<<b<<endl; return 0; }输出结果为 1 有人会问为什么这里传回来的值可以正确打印出来,不是栈会被刷新内容么?是的,确实,在test函数执行完后,存放a值的单元是可能会被重写,但是在函数执行return时,会创建一个int型的临时变量,将a的值复制拷贝给该临时变量,因此返回后能够得到正确的值,即使存放a值的单元被重写数据,但是不会受到影响。5. test4#include<iostream> using namespace std; char* test(void) { char *p="hello world!"; return p; } int main(void) { char *str; str=test(); cout<<str<<endl; return 0; }执行结果是 hello world! 同样返回的是指针,为什么这里会正确地打印出hello world1?这是因为char *p="hello world!",指针p是存放在栈上的,但是"hello world!”是一个常量字符串,因此存放在常量区,而常量区的变量的生存期与整个程序执行的生命期是一样的,因此在test函数执行完后,str指向存放“hello world!”的单元,并且该单元里的内容在程序没有执行完是不会被修改的,因此可以正确输出结果。6. test5#include<iostream> using namespace std; char* test(void) { char *p=(char *)malloc(sizeof(char)*100); strcpy(p,"hello world"); return p; } int main(void) { char *str; str=test(); cout<<str<<endl; return 0; }运行结果 hello world. 这种情况下同样可以输出正确的结果,是因为是用malloc在堆上申请的空间,这部分空间是由程序员自己管理的,如果程序员没有手动释放堆区的空间,那么存储单元里的内容是不会被重写的,因此可以正确输出结果7. test6#include<iostream> using namespace std; void test(void) { char *p=(char *)malloc(sizeof(char)*100); strcpy(p,"hello world"); free(p); if(p==NULL) { cout<<"NULL"<<endl; } } int main(void) { test(); return 0; }没有输出 在这里注意了,free()释放的是指针指向的内存!注意!释放的是内存,不是指针!这点非常非常重 要!指针是一个变量,只有程序结束时才被销毁。释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内容的垃圾,是未定义的,所以说是垃圾。因此,释放内存后应把指针指向NULL,防止指针在后面不小心又被使用,造成无法估计的后果。
结论:
1. 在返回值中,确实有拷贝的一说,但是拷贝的是返回变量的值,拷贝的是比如指针或实值,若是指针,其指向的内存是否被释放,若是释放则其在调用的主函数处,得到的是乱码。
2. 堆区域,程序员释放的是指针指向的内存,而不是指针变量本身。
3. 当指针作为参数进行传递时传递的也只是一个值,只不过该值只一个地址,因此对于形参的改变并不影响实参。
原文作者: 海子 本人只是在他的基础之上做了排版即一点补充内容的改动。