c语言怎么申请内存_c语言申请内存malloc

c语言怎么申请内存_c语言申请内存mallocC语言(六):动态内存管理(九)动态内存管理前面讲的存储类型,其作用就是限定变量的作用域、生命周期、链接属性。所以我们的代码都需要服从预先定制的内存管理规则来编写。通俗的说就是,你写程序,你需要什么变量、要多少个变量,这些都需要提前声明和定义好,不能在程序运行的时候才

C语言(六):动态内存管理
  (九)动态内存管理

  前面讲的存储类型,其作用就是限定变量的作用域、生命周期、链接属性。所以我们的代码都需要服从预先定制的内存管理规则来编写。通俗的说就是,你写程序,你需要什么变量、要多少个变量,这些都需要提前声明和定义好,不能在程序运行的时候才定义。即使后来的c99标准中,虽然程序运行中可以声明和定义,但是一旦定义完毕也是就不能再改动的。所以,就需要有一种更灵活的内存管理方式,也就是可以动态管理内存,在stdlib.h头文件里面,有下面的这几个库函数就可以实现动态内存管理:malloc :申请动态内存空间free :释放动态内存空间calloc : 申请并初始化一系列内存空间realloc : 重新分配内存空间1、malloc :申请动态内存空间函数原型: void *malloc(size_t size);malloc函数向系统申请分配size个字节的内存空间。如果函数调用成功,就返回一个指向这块空间的void类型的指针(void *);如果函数调用失败,就返回NULL。并且,如果size参数设置为0,返回值也可能是NULL(不同编译器可能返回结果不一样),但这并不意味着函数调用失败。返回的指针是一个void类型的指针,就是一个无类型的指针。因为无类型指针可以指向任何数据类型,而且void类型指针可以转化为任何一种有类型的指针。malloc只是申请了一块空间,并无初始化这块空间,所以,这块空间里存储的数据还是随机的,并没有都初始化为0。下面有代码示例3展示。malloc函数申请的内存空间,并没有规定这块空间要存储的数据的数据类型,就是malloc申请的空间可以放整型、字符串、浮点型等,随便你放。下面有代码示例1展示。malloc函数还可以申请一块任意尺寸的内存空间。由于申请的空间都是连续的,所以我们用数组就可以索引了。下面有代码示例2。(1)代码示例1:用malloc函数申请一块能存一个整型数据的内存空间,然后用指针变量ptr指向这块空间。c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  A:这里的malloc函数和exit函数都需要stdlib.h这个头文件B:先声明一个指向整型的指针变量ptrC: 先给malloc函数传入一个“只能存储一个整型数据的大小空间”的参数,malloc函数就返回一个void *的无类型指针。这里可以不写(int *)也是可以的,因为void类型的指针本来就是可以赋值给其他任何类型的指针。写上就表示强制把void类型的指针转化为int类型指针,再赋值给ptr。写上代码更容易阅读。D:exit就是直接退出程序,这里也可以写成return(2)代码示例2:用malloc函数申请一块能存若干个整型数据的空间,到底多少个数据,看用户的录入吧。c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  (3)代码示例3:用malloc函数申请内存空间,并初始化。malloc函数只能申请内存空间,不能初始化。要想初始化,你得自己写个循环初始化。但是我们也可以直接用C语言标准库有现成的函数帮我们初始化,直接使用多香。

  以mem开头的函数都被编入字符串标准库,函数的声明包含在string.h这个头文件中:memset: 使用一个常量字节填充内存空间,通常我们就用0这个常量来填充内存空间。memcpy:拷贝内存空间。      函数原型:void *memcpy(void *dest, const void *src, size_t n)        参数dest:指向目标内存空间        参数src:指向源内存空间        参数n:指定要拷贝到dest指向空间的前n个字节        返回:无类型的指针memmove:拷贝内存空间memcmp:比较内存空间memchr:在内存空间中搜索一个字符

  之前我们还学了几个字符串标准库的几个处理字符串的函数:strlen, strcpy, strncpy, strcat, strncat, strcmp, strncmp。它们虽相似但偏重点不一样,一个偏向对内存空间的处理,一个是偏向对字符串的处理。所以返回的是一个无类型的指针,而以str开头的返回的是一个字符指针。c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  2、free :释放动态内存空间说明:malloc函数申请的内存空间是位于内存的“堆”上(C语言在内存上的布局是:像一些局部变量是存放在“栈”上的,而申请的动态内存是存放在"堆"上的),如果你不主动释放堆上的数据,那这个数据就会一直存在,直到程序运行完毕。所以当我们不需要这块内存的时候一定要记得释放free它,不然容易内存泄漏。释放就是用free函数:函数原型:void free(void *ptr);free函数释放ptr参数指向的内存空间。该内存空间必须是由malloc、calloc或realloc函数申请的。否则,比如你要释放一个栈上的普通局部变量,该函数将导致未定义行为。如果ptr参数是NULL,则不执行任何操作。注意:该函数并不会修改ptr参数的值,所以调用后它仍然指向原来的地方(只是这个地方变成了一个非法的空间,就是这个指针还是指向这个地址,只是这个地址已经是非法空间了,已经无任何意义了)。c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc内存泄露:下面2种情况可以导致内存泄露一是,隐式内存泄露。就是用完内存块,你没有即使用free函数释放,导致内存泄露。有的人的程序越跑消耗的内存越多,就很有可能是这种隐式的内存泄露,直到程序卡死cpu或者内存,程序被kill才结束。二是,丢失内存块地址。一旦内存块地址丢失,你再free释放,都不知道去哪儿释放了。c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  3、calloc : 申请并初始化内存空间函数原型:void *calloc(size_t nmemb, size_t size);calloc函数在内存中动态的申请nmemb个长度为size的连续内存空间。也就是申请的总空间尺寸是nmemb*size,并且把这些内存空间全部初始化为0(也就是calloc函数会自动调用memset函数帮我们初始化,我们就省事了)。c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  4、realloc : 重新分配内存空间

  为啥要重新分配内存空间呢,比如你原来指定的内存空间里面已经存了数据了,但是原空间大小不够了,需要扩展,你就需要重新分配内存空间,就用realloc函数。否则,你就得再次调用malloc函数,申请一个更大的内存空间,然后把之前的数据拷贝memcpy到新申请的空间。c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  realloc函数是把上面的步骤已经给我们封装好了,我们直接调用即可。函数原型:void *realloc(void *ptr, size_t size)第一个参数是原空间的地址;第二个参数是新空间的尺寸。realloc函数将移动内存空间的数据,并返回新的指针。如果第一个参数ptr为NULL,那么realloc函数就相当于调用malloc(size)。如果ptr不为NULL,ptr的值必须为先前调用malloc、calloc或realloc函数的返回值。意思就是移动的数据必须是堆中的数据,必须是堆函数申请的空间,这个空间里面的数据才能被移动,栈中的数据是不能移动的。如果第二个参数size为0,并且第一个参数ptr不为NULL,那么调用realloc函数就相当于调用free(ptr)如果新分配的空间比原来的大,则旧内存块的数据直接拷贝过去,数据就不会发生改变;如果新空间尺寸小于旧空间,可能会导致数据丢失,所以要慎用。代码示例: 让用户输入一些整数,我们提前也不知道用户到底要输入几个整数,当用户输入-1时就表示用户输入完毕,然后我们把用户的输入给打印出来。思路:一开始我们只需申请1个整数的存放空间,当用户第二个数没有输入-1时,我们再申请第二个空间,这个空间尺寸就申请2个整数存放空间的大小,然后把用户输入的第一个数据拷贝到新空间,并释放第一个空间。当用户第三个数没有输入-1,就再申请第三个空间…依次类推。就是根据用户的需要不断拓宽存储空间。c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  小结:有没有一种终于进化出了草履虫的感觉?!!!这种方式是不是真正实现了动态!它的底层还是不动的,不动不动不动就竟然动起来了!而且这种"动"其实还是不动,只是我们人类感觉到"动"了。之前的程序都是一个不动、两个不动、三个不动…不动和不动之间没啥关系,都是独立的,所以我们也感觉不到动。但是从这个程序起,不动和不动之间有了继承,就是后一个不动拷贝了前一个不动,所以就动起来了!!!有一种生命的诞生的神圣和庄严,神奇、奇妙。。。。突然脑洞大开,是不是对时间和空间进行无限小的分隔,时空就不存在了,此时空间永恒了时间也永恒了……天,我不喜欢这种永恒,太绝望了。。。

  (十)C语言的内存布局规律

  1、用个代码实例将各种变量及函数的地址打印出来:c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  上述代码中的变量,在内存中的位置如下:c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  2、一个典型的C语言程序的内存布局规律c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc代码段:text segment, 通常是指用来存放程序执行代码的一块内存区域,比如上面代码中的func函数,这个函数就是程序代码的一部分,所以func的地址就位于代码段区域。这部分区域的大小在程序运行前就已经确定了,并且该区域通常属于只读。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。比如上面代码中的str1和str2字符串常量。数据段:Initialized data segment, 通常用来存放已经初始化的全局变量和局部静态变量。BSS段:Bss segment/uninitialized data segment, 通常是指用来存放程序中未初始化的全局变量、局部静态变量的一块内存区域。BSS是英文block started by symbol的简称,这个区段中的数据在程序运行前将被自动初始化为数字0。堆heap:堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩展或缩小。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上;当利用free等函数释放内存的时候,被释放的内存就从堆中被剔除。栈stack:平时经常听到的堆栈,一般指的是栈。栈是函数执行的内存区域,通常和堆共享同一片区域。所以,栈里面存放的就是局部变量、函数的参数、函数的返回值。这些数据都是放在栈里面的。可以用size 编译文件,来查询程序在内存中各段占用内存的大小:c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  3、堆和栈的区别:申请方式:堆由程序员手动申请;栈由系统自动分配。释放方式:堆由程序员手动释放;栈由系统自动释放。生存周期:堆的生存周期由动态申请到程序员主动释放为止,不同函数之间均可自由访问。栈的生存周期由函数调用开始到函数返回时结束,函数之间的局部变量不能互相访问。我们前面也说过,函数的局部变量是放在栈里面的,函数调用完毕后,栈空间就释放了,变量也就不存在了,所以别的函数无法访问到这块区域。发展方向:堆和其他区段一样,都是从低地址向高地址发展。栈则相反,是从高地址向低地址发展。c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  说明:在数据结构中也有堆和栈的概念,但和现在我们说的内存布局中的堆栈是不一样的东西。

  4、内存池(1)频繁调用malloc函数再释放free内存,很容易产生大量的内存碎片。至于这句话怎么理解,我们可以发挥想象力通俗的理解,看下图:c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  比如我们再堆里存放了变量A和变量B,后来我们又free释放了A,那A区域是不是就空了,后面如果我们又要申请CDEFG..呢,当申请的空间小于等于12kb,好,可以放到A的区域;如果申请的空间大于12kb是不是就得继续在B上面的空间申请啊,假设频繁发生这种申请了又释放,那是不是就产生很多锁片。

  这是我们想象的,也是对的,但也不完全对,因为实际中比这要复杂得多,没有这么简单。碎片产生的真正逻辑是小甲鱼的一篇文章《malloc内存分配原理及内存碎片产生的原因》,写得非常认真和严谨,可见作者认真去探究了这2个函数,但是这篇文章是付费的,只要2元,所以我看了。这里我也不想写了,一是要扩展的东西是真的非常多,看完收获很多,我已经知道了就不想写了,写笔记是为了我自己日后参考的;二是人家是卖钱的,我都在白嫖人家的视频了,不能再断人家财路呀。白嫖了人家是要感恩的。(2)频繁调用malloc和free函数,开销也很大。时间上的开销:频繁调用malloc和free函数,实际是频繁向操作系统申请堆内存,应用程序要经历从应用层到内核层的切换,切换到内核层,操作系统才会分配内存空间,分配完内存空间后,还要再切回到应用层,才能完成堆空间的申请过程。这个过程的实际消耗是很高的。那么如果规避内存碎片和高开销呢?内存池。(3)内存池内存池其实就是让程序额外维护的一个缓存区域。c语言怎么申请内存_c语言申请内存mallocc语言怎么申请内存_c语言申请内存malloc

  当程序调用malloc申请ABC三块区域时,是真真切切的切换到内核层申请的;当程序又free了ABC三块区域时,其实并没有切换到内核层去释放,而是把这三块区域暂时放到程序维护的一个叫内存池的缓存区。这样做的目的就是,如果程序继续往下执行又要malloc申请DEFG..等空间时,就先到内存池看看有没有合适的空间,如果有就直接用了,就不需要切换到内核层去申请了,也就是不需要调用malloc函数了,一是节省了开销而是内存碎片最大限度地减少了。(5)如何实现内存池我们可以通过单链表来实现一个内存池。就是可以使用单链表来维护一个简单地内存池。只需要将没有用的内存空间地址依次用一个单链表记录下来;当再次需要的时候,从这个单链表中即可。其实就是让程序额外维护一个缓存区域。我们后面讲单链表的时候,会写一个通讯录管理程序,届时我们给这个程序里加上一个函数来实现内存池管理。

2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/92857.html

(0)
上一篇 2024年 5月 29日 下午4:16
下一篇 2024年 5月 29日 下午4:28

相关推荐

关注微信