C 语言内存管理

发布于 2021-07-12 12:59:47 字数 4148 浏览 1160 评论 0

一、内存管理

  1. malloc:申请动态内存空间
  2. free:释放动态内存空间
  3. calloc:申请并初始化一系列内存空间
  4. realloc:重新分配内存空间

注意这些函数都是在头文件中

1.malloc:申请动态内存空间 存放在堆中

函数原型:void *malloc(size_t size);

malloc 函数向系统申请分配size个字节的空间,并返回一个指向这块空间的指针。

如果函数调用成功,返回一个指向申请的内存空间的指针,由于返回类型是void指针(void *),所有它可以被转换成任何类型的数据;如果函数调用失败,返回值是NULL。另外,如果size参数设置为0,返回值也可能是NULL,但这并不意味着函数调用失败。

2.free:释放动态内存空间

动态内存是存放在堆中的,如果你不主动去释放堆上的内存资源,那么他将永远的存在,直到程序关闭。

函数原型:void free(void *ptr);

free函数将释放ptr参数指向的内存空间。该内存空间必须是由 malloc、calloc、realloc 函数申请的。否则该函数将导致未定义行为。如果ptr参数是NULL,则不执行任何操作。注意:该函数并不会修改ptr参数的值,所以调用后任然指向原来的地方(变为非法空间)。

#include<stdio.h>
#include<stdlib.h>
int main(){
    int *ptr;
    ptr = (int *)malloc(sizeof(int));
    if(ptr == NULL){//一般内存不够用的时候会失败
        printf("分配内存失败!\n");
        exit(1);
    }
    printf("请输入一个整数:");
    scanf("%d",ptr);

    printf("你输入的整数是:%d\n",*ptr);//520
    free(ptr);//释放
    printf("你输入的整数是:%d\n",*ptr);//0
    return 0;
}

3.内存泄露

导致内存泄露主要有两种情况:

  • 一、隐式内存泄露(即用完内存块没有及时free函数释放)
  • 二、丢失内存块地址

为防止内存泄露,我们要在使用完这块内存空间之后,立刻调用free函数释放(高级语言有垃圾回收机制,而C语言则没有)

int main(void){
    while(1){
        malloc(1024);//循环申请内存空间,并且没有及时释放,会导致内存爆掉
    }
    return 0;
}

4.calloc:申请并初始化一系列内存空间

函数原型:void *calloc(size_t nmemb,size_t size);

calloc函数在内存中动态申请nmemb个长度为size的连续内存空间(即申请的总空间尺寸为nmemb*size),这些内存空间全部被初始化为0

calloc函数在申请完内存后,自动初始化该内存空间为零

5.realloc:重新分配内存空间

函数原型:realloc(void ptr,size_t size);

6.初始化内存空间

/**
 以mem开头的函数被编入字符串标准库,函数的声明包含在string.h这个头文件中。
 --memset-- 使用一个常量字节填充内存空间
 --memcpy-- 拷贝内存空间
 --memmove--
 --memcpm-- 比较内存空间
 --memchr-- 在内存空间汇总搜索一个字符
 */

二、内存布局

#include<stdio.h>
#include<stdlib.h>

int global_uninit_var;
int global_init_var1 = 520;
int global_init_var2 = 880;

void func(void);
void func(void){
    ;
}

int mian(void){
    int local_var1;
    int local_var2;

    static int static_uninit_var;
    static int static_init_var = 456;

    char *str1 = "I love FishC.com!";
    char *str2 = "You are right!";

    int *malloc_var = (int *)malloc(sizeof(int));

    printf("addr of func -> %p\n",func);
    printf("addr of str1 -> %p\n",str1);
    printf("addr of str2 -> %p\n",str2);

    printf("addr of global_uninit_var -> %p\n",global_uninit_var);
    printf("addr of global_init_var1 -> %p\n",global_init_var1);
    printf("addr of global_init_var2 -> %p\n",global_init_var2);

    printf("addr of static_init_var -> %p\n",static_init_var);
    printf("addr of static_uninit_var -> %p\n",static_uninit_var);

    printf("addr of local_var1 -> %p\n",&local_var1);
    printf("addr of local_var2 -> %p\n",&local_var2);
}

代码段(Text segment) 通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。在代码段中,也有可能包含一些只读的常量。

数据段(Initialized data segment) 通常用来存放已经初始化的全局变量和局部静态变量。

BSS段(Bss segment/Uninitialized data segment) 通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称,这个区段中的数据在程序运行前将被自动初始化为数字0。

堆:是用来存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩展或缩小。当进程调用malloc等函数分配内存是,新分配的内存就被动态添加到堆上;当利用free等函数释放内存时,被释放的内存从堆中被剔除。

栈:大家平时可能经常听到堆栈这个词,一般指得就是这个栈。栈是函数执行的内存区域,通常和堆共享一片区域。

堆和栈的区别

一、申请方式:

--堆是由程序员手动申请
--栈是由系统自动分配

二、释放方式:

--堆是由程序员手动释放
--栈由系统自动释放

三、生存周期:

--堆的生存周期由动态申请到程序员主动释放为止,不同函数之间均可自由访问
--栈的生存周期由函数调用开始到函数返回时结束,函数之间的局部变量不能相互访问

四、发展方向:

--堆和其他区段一样,都是从低地址向高地址发展
--栈则相反,是由高地址向低地址发展

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84959 人气
更多

推荐作者

花开柳相依

文章 0 评论 0

zyhello

文章 0 评论 0

故友

文章 0 评论 0

对风讲故事

文章 0 评论 0

Oo萌小芽oO

文章 0 评论 0

梦明

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文