
从操作系统的角度简要介绍该过程. 进程是占用资源的最小单元. 此资源当然包括内存. 在现代操作系统中,每个进程可以访问的内存彼此独立(某些交换区除外). 进程中的线程共享该进程分配的内存空间.
从操作系统的角度来看,过程=程序+数据+ PCB(过程控制块).
代码区(文本): 用于存储CPU执行的机器指令(机器指令),还可能包含一些只读常量变量,例如字符串常量. 通常,代码区域是可共享的(也就是说,它可以由另一个执行程序调用),因为对于经常执行的程序,内存中仅需要一个代码. 该区域的大小是在程序运行之前确定的,通常是只读的. 使其成为只读的原因是为了防止程序意外修改其指令. 此外,代码区还计划有关局部变量的信息.
全局数据区域(静态区域)(静态): 全局变量和静态变量的存储放在一起,初始化的全局变量和静态变量在一个区域(数据区域),未初始化的全局变量和静态变量在另一个区域相邻区域(BSS区域). 除了文本常量区域外,常量字符串还放置在此处,并在程序结束后由系统释放.
正在运行的C编译器占用的内存分为5部分: 代码区,初始化数据区,未初始化数据区,堆区和堆栈区.
(1)由编译器自动分配和释放,通常存储由程序临时创建的局部变量(但不包括由static声明的变量,static意味着将变量存储在数据段中),即该函数包括curl方括号“ {}”中定义的变量,还包括函数调用的形式参数,调用后的返回值等.
(2)调用原理: 每当调用一个函数时,该函数的返回地址和有关该调用的某些信息(例如某些寄存器的内容)都存储在堆栈区域中. 然后,被调用函数在堆栈区域为其自动变量和临时变量分配空间. 这就是C实现函数递归调用的方式. 每次执行递归函数调用时,都会使用一个新的堆栈框架,以使该新实例堆栈中的变量不会与该函数的另一个实例堆栈中的变量混淆.
(3)堆栈是一种从高地址扩展到低地址的数据结构. 它具有先进先出的特点,即按顺序定义了两个局部变量. 第一个定义的变量的地址为高地址,第二个变量的地址为低地址. 函数参数的顺序从右到左(主要是支持可变长度参数形式).
(4)最后一个堆栈还具有“内存小,自动化且可能溢出”的特征. 堆栈顶部的地址和堆栈的最大容量通常由系统预先确定,通常不会太大. 因为局部变量主要存储在堆栈中,并且局部变量所占用的内存空间会在代码段或函数段末尾的位置由系统恢复,所以堆栈空间将通过回收自动进行管理,并且通常不需要人工操作. 如果局部变量所请求的空间超过了堆栈的剩余空间,则可能会发生“堆栈溢出”,这可能会导致意外的后果. 因此,通常不宜在堆栈中申请太多空间,例如,使用大长度的数组,递归调用重复多次的函数等.
(1)通常将动态分配的存储空间存储在程序中. 它的大小不是固定的,可以动态扩展或缩放. 当进程调用malloc / free和其他函数分配内存时,新分配的内存将动态添加到堆中(扩展堆)/释放的内存从堆中增加(减少堆).
(2)数据结构中的堆和堆是两个不同的东西,但是分配方法类似于链表.
(3)堆是从低地址扩展到高地址的数据结构,并且是不连续的内存区域. 在标准C语言中,内存分配功能(例如malloc)用于从堆中分配内存. 在Objective-C中,使用new创建的对象也会从堆中分配内存.
(4)堆具有“大内存,手动分配管理c语言内存分配对应,任意应用程序大小和可能的泄漏”的特征. 堆内存由分为堆管理器的操作系统管理,管理器定向到用户(用户进程)c语言内存分配对应,提供API(malloc,free等)以使用堆内存. 程序员需要手动分配和释放. 如果程序员在应用程序使用完堆内存后没有及时释放它,那么这部分内存会丢失(进程本身认为该内存未使用,但应该在堆内存记录中仍属于该内存)这个过程,所以当它需要分配空间时,它将重新申请新的内存,而不是重新使用此内存),这就是我们常说的内存泄漏,因此内存泄漏是指堆内存正在泄漏.
被划分为多个区域的原因主要是基于以下考虑因素:
++++++++++++++++++++++++++++++++++++++++++++++++ ++++ ++++++++++++++++++++++++++++++++
通常来说,函数可以返回局部变量. 局部变量的范围仅在函数内部. 函数返回后,将释放局部变量的内存,因此,如果函数返回局部变量的值且不涉及地址,则程序不会出错. 但是,如果返回局部变量的地址(指针),则程序运行后将发生错误. 因为该函数只是复制指针并返回,但是指针所指向的内容已经释放,因此指针所指向的内容是不可预测的,因此调用将是错误的,准确地说:
下面是一些典型的例子,用函数的指针返回局部变量来说明:
1. 返回字符串常量指针
#include
char * returnStr(){
char * p =“世界你好!”;

返回p;
}
int main(void){
char * str = NULL;
str = returnStr();
printf(“%s \ n”,str);
返回0;
}
这没有问题,因为“ hello world!”是存储在只读数据段中的字符串常量,并且将存储在字符串常量中的只读数据段的第一个地址分配给指针,因此returnStr函数将退出. 此时,存储字符串常量的内存位于的位置将无法恢复,因此可以通过指针对其进行访问而不会出错.
2. 返回堆栈指针
#include
char * returnStr(){
char p [] =“世界你好!”;
返回p;
}
int main(){
char * str = NULL;
str = returnStr();
printf(“%s \ n”,str);
返回0;
}

“你好,世界!”是存储在堆栈中的局部变量. 当returnStr函数退出时,应清除堆栈,还应清除局部变量的内存,因此该函数返回此时已释放的内存地址. ,因此可以打印出乱码.
3. 返回局部变量和指针
返回值: 局部变量(仅变量,不包括指针,无论是静态还是自动的),指向静态局部变量的指针
不可返回的条件: 指向局部变量的指针(无静态限定符,默认为自动类型)
//返回非静态局部变量
int f1(){
int a;
....
返回a; //允许
}
//返回静态局部变量
int f2(){
static int a;
....
返回a; //允许
}
//返回指向非静态局部变量的指针
int * f3(){
int a;
....
返回&a; //毫无意义,不应该这样做

}
//返回静态局部变量指针
int * f4(){
static int a;
....
返回&a; //允许
}
局部变量也分为局部自动变量和局部静态变量. 由于a返回值,因此可以返回本地变量(无论是自动还是静态的),因为此时会返回此本地变量的值,但不会返回,因此应返回指向本地自动变量的指针,因为函数调用结束后,将丢弃局部自动变量,并且该指针指向不再存在的对象,这毫无意义. 但是可以返回指向局部静态变量的指针,因为静态变量的生命周期从定义到程序结束.
4. 返回静态堆栈指针
#include
char * returnStr(){
static char p [] =“世界你好!”;
返回p;
}
int main(void){
char * str = NULL;
str = returnStr();
printf(“%s \ n”,str);
返回0;
}
如果函数的返回值必须是局部变量的地址,则必须将局部变量声明为静态类型

5. 返回数组指针
当不使用静态限定符时,默认情况下该数组存储在堆栈中,因此返回值没有意义. 如果要返回数组,则需要添加静态限定符以使数组成为静态. 用法如下
int * func(void){
static int a [10];
// int a [10];没有意义,函数退出,堆栈被破坏
........
返回a;
}
6. 返回堆指针
程序运行时,使用malloc申请任意数量的内存. 程序员负责何时释放内存. 动态存储器的寿命由程序员确定. 使用非常灵活.
字符* GetMemory3(int num){
char * p =(char *)malloc(sizeof(char)* num);
返回p;
}
void Test3(void){
char * str = NULL;
str = GetMemory3(100);
strcpy(str,“ hello”);
cout << str << endl;
free(str);
}
发布于2020-03-15 21:14王气弹簧阅读(...)评论(...)编辑
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-258542-1.html
包括水域
哈哈