赞
踩
目录
1、堆区(heap)——由程序员分配和释放, 若程序员不释放,程序结束时一般由操作系统回收。注意它与数据结构中的堆是两回事
2、栈区(stack)——由编译器自动分配释放 ,存放函数的参数值,局部变量等。其操作方式类似于数据结构中的栈
3、静态全局区
1)未初始化静态全局区 —— 静态变量,全局变量,没有初始化的存在此区
2)初始化的静态全局区 —— 静态变量、全局变量,赋过初值的存放在此区
4、文字常量区——常量、字符串就是放在这里的。 程序结束后由系统释放
5、(程序)代码区——用于存放函数体的(二进制)代码
图例如下:
在数组一章中,介绍过数组的长度是预先定义好的,在整个程序中固定不变。但是在实际的编程中,往往会发生所需的内存空间取决于实际输入的数据,而无法预先确定 。为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态的分配内存空间,也可把不再使用的空间回收再次利用。而动态分配内存就是在堆区分配空间
1、在程序编译或运行过程中,按事先规定大小分配内存空间的分配方式。如:int a[10]
2、必须事先知道所需空间的大小。
3、一般以数组的形式,分配在栈区或静态全局区。
4、按计划分配。
1、在程序运行过程中,根据需要大小自由分配所需空间。
2、分配在堆区,一般使用特定的函数进行分配。
3、堆区开辟空间,手动申请手动释放,更加灵活。
4、按需分配。
注意:malloc calloc relloc 动态申请的内存,只有在free或程序结束的时候才释放。
size:指要开辟的空间的大小
指针 = (指针类型*)malloc(数据数量 *sizeof(指针类型))
- char* rec1=(char*) malloc(20*sizeof(char));//这种方法默认计算字节数
- char* rec2=(char*) malloc(20);//这种方法也可以
在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。即在堆区开辟指定长度的连续空间。其中计算字节数是为了确定到底需要分配多少空间。
如果返回值是所开辟空间的首地址,说明申请空间成功;如果返回值是NULL,则说明申请空间失败
free函数是释放内存函数
案例如下:
free函数释放ptr指向的内存(默认指的是堆区的内存)
1.free只能释放堆区的空间,即必须是malloc calloc relloc动态申请的内存。且无法部分释放空间,只能释放全部空间
2.free后,原指针还是指向原先动态申请的内存,但是内存已经不能再用了,就变成野指针了。所以一般为了防止野指针,会free完毕之后对其赋为NULL,例如:
- char* rec=(char*) malloc(20*sizeof(char));//申请内存
-
- free(rec);//释放内存
- rec=NULL;//定义为空指针,防止出现野指针
3.野指针就是总是占其它指针的内存(地址)或者随机出现一个地址的指针
4.一块动态申请的内存只能free一次,不能多次free
size_t :无符号整型,它是在头文件中,是用typedef定义出来的
nmemb:要申请的空间的块数
size:每块的字节数
calloc(nmemb,size);
函数用法与malloc类似,具体示例如下:
- char* p2=(char*)calloc(2,10);在堆中申请了2块,每块大小为10个字节,即20个字节连续的区域
- char* p2=(char*)calloc(2*sizeof(char),10);
- char* p2=(char*)calloc(2,10*sizeof(char));
- char* p2=(char*)calloc(2*sizeof(char),10*sizeof(char));
- //以上四种方法都可行
在堆区申请nmemb块,每块的大小为size个字节的连续区域,即总nmemb*size个字节连续的区域
malloc 申请的内存中存放的内容是随机的。而calloc 函数申请的内存中的内容为0。所以calloc消除了野指针存在的风险,案例如下:
如果返回值是所开辟空间的首地址,说明申请空间成功。如果返回值是NULL,则说明申请空间失败
s:原本开辟好的空间的首地址
newsize:重新开辟的空间的大小
realloc(s,newsize);
- char* p1=(char*)malloc(80*sizeof(char));//申请80个字节的内存
- p1=(char*)realloc(p1,100);//将内存重新开辟为100个字节,可以认为是增加了20个字节
-
- p1=(char*)realloc(p1,50);//将内存重新开辟为50个字节,可以认为是减少了30个字节
在原先 s 指向的内存基础上重新申请内存,新内存的大小为 newsize个字节。
当newsize比原先的内存大时,如果原先内存后面有足够大的空间,就追加。如果后边的内存不够大,则relloc函数会在堆区找一个newsize个字节大小的内存申请,将原先内存中的内容拷贝过来,并释放原先的内存,最后返回新内存的地址。
当newsize 比原先的内存小时,则会释放原先内存的后面的存储空间,只留前面的newsize个字节。
如果调整成功则返回值为新申请内存的首地址。失败则返回NULL
因为NULL是一个特殊的指针值,表示指针没有指向任何有效的对象或地址。对NULL指针解引用会导致程序崩溃或未定义的行为,因为程序在试图访问一个不存在的内存地址。
因此,在使用指针之前,应检查其是否为NULL,并确保指向有效的内存地址。
对动态内存的越界访问可能会导致程序崩溃或产生未定义的行为。
这是因为动态内存分配需要在运行时进行,并且程序员需要手动管理内存的分配和释放。如果程序员在访问动态内存时越界,就会导致访问到未分配的内存或者已经释放的内存,从而可能导致程序崩溃或出现未定义的行为。
此外,动态内存的越界访问还可能会导致数据损坏、安全漏洞等问题。因此,程序员需要注意动态内存的边界,并且避免越界访问。
因为非动态开辟的内存是在程序运行时从栈上分配的,而不是从堆上分配的。栈上分配的内存是由系统自动管理的,程序员无法控制其释放。因此,如果试图使用free函数来释放栈上的内存,会导致程序崩溃或不可预测的行为。所以只有动态开辟的内存才能使用free函数进行释放。
对同一块动态内存多次释放会导致程序崩溃或出现未定义的行为。因为在第一次释放后,操作系统会将该内存块标记为可用,此时这块内存空间就可以被其他变量所占用。所以再次释放时该内存块由于已经被标记为可用,所以释放操作将无法成功,从而导致程序出现异常。
此外,多次释放同一块内存还会导致内存泄漏和程序性能下降的风险。因此,程序员需要确保只释放已经分配的内存,且只释放一次。
其中需要注意的是,free释放的是free释放的是内存空间,而不是指针。free之后,指针仍然存在,指针指向也不变,而指针指向的内容要视情况而定,可能存在也可能不存在,具体还要看环境和编译器(VS2022是将其置为随机值的)。所以释放后的输出可能和原来的内容一样,也可能是乱码。但是综合考虑,为了安全起见还是不要有对同一块动态内存多次释放这种操作。
动态分配的内存是由程序员手动分配的,而不是由系统自动管理的。如果程序员忘记释放动态分配的内存,那么这些内存将一直占据系统资源,导致内存泄漏和程序性能下降。此外,如果程序员在使用未初始化的动态分配内存时发生访问错误,会导致程序崩溃或出现不可预测的行为。因此,释放动态分配的内存是程序员的责任,必须确保释放内存以避免这些问题。下面是两个常见的内存泄漏案例剖析。
案例一:
- char* p=(char*)malloc(100);
- p="hellow world!";
案例分析:开始定义了一个指针型变量p在堆区开辟了100个字节的空间,而 p="hellow world!" 之后,p指向了 hellow world! 的文字常量区,p指向的地址内存分区发生变化,那么p在堆区申请的100个字节的内存(的首地址)就丢了,即发生了内存泄漏。
注意:这是一个很容易就会犯的错误!
案例二:
- void fun()
- {
- char* p=(char*)malloc(80);
- }
- int main()
- {
- fun(); //第一次调用
- fun(); //第二次调用
- //每调用一次则内存泄漏一次(80字节)
- return 0;
- }
案例分析:fun函数每调用一次内存就会泄漏一次。因为fun函数中定义了一个指针型变量p在堆区开辟了80个字节的空间,而主函数调用完fun函数之后,既没释放也没返回,所以调用完之后开辟的空间就丢了,就会发生内存泄漏
解决方案:可以设置一个函数的返回值,主调函数接收这个返回值并对其使用、处理或者释放。
在C语言中free(NULL)的操作是合法的,C语言标准规定:如果free的参数是NULL,那么这个函数就什么也不做。
在C语言中malloc(0)的语法也是对的,而且确实也分配了内存,但是内存空间是0,这个看起来说法很奇怪,但是从操作系统的原理来解释就不奇怪了。
在内存管理中,内存中有栈和堆两个部分,栈有自己的机器指令,是一种先进后出的数据结构。而malloc分配的内存是堆内存,由于堆没有自己的机器指令,所以要由自己编写算法来管理这片内存,通常的做法是用链表在每片被分配的内存前加个表头,里面存储了被分配内存的起始地址和大小。malloc等函数返回的就是表头里的起始指针(这个地址是由一系列的算法得来的,而这些操作又是由编译器的底层为我们做的,我们并不需要关心如何操作)
动态分配内存成功之后,就会返回一个有效的指针。而对于分配0空间来说,算法会得出一个可用内存的起始地址,但可用的空间为0,而操作系统一般不知道其终止地址,一般是根据占用大小来推出终止地址的。所以对malloc(0)返回的指针进行操作就是错误的。
但需要注意,即使malloc(0)也要记得free掉,因为malloc还会额外分配内存来维护申请的空间,malloc(0)时并不是什么也不做。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。