C语言-malloc(申请函数)free(释放函数)

前言

  1.  这里主要讲解的是开辟空间和销毁空间
  2. 主要是语法结构的讲解,比较简单,主要就是使用
  3. 这里是老博客翻新为新博客

malloc的讲解:

语法结构讲解:

前言

  1. 我们打开C语言文档,注意这里不是官方文档
    malloc - C++ Reference
  2. 我们发现在C语言文档里面的语法结构是这个样子的void* malloc (size_t size);下面我会进行讲解
  3. 从C语言的文档里面我们可以看见,头文件cstdlib

返回类型:void * 

  1. 含义void *是一种特殊的指针类型。在 C 语言中,指针用于存储变量的内存地址。void *类型的指针表示它可以指向任意类型的数据。对于malloc函数来说,它返回的指针指向了新分配的内存块的起始地址。虽然从技术上讲它是void *,但实际上在使用中通常会根据要存储的数据类型进行类型转换。例如,如果要存储int类型的数据,可以将malloc返回的指针转换为int *类型。
  2. 示例:假设要为一个int数组分配内存。可以这样使用mallocint *arr = (int *)malloc(10 * sizeof(int));。这里将malloc返回的void *指针强制转换为int *,因为我们打算在这块内存中存储int类型的数据。

函数名:malloc

  1. 含义:这是函数的名称,用于执行内存分配操作。它是 C 语言标准库中的一个函数,在程序运行时动态地从堆(heap)中获取指定大小的内存空间。与静态内存分配(如定义数组int arr[10];这种在编译时就确定内存大小的方式)不同,malloc允许程序在运行过程中根据实际需要获取内存。

参数:size_t size

  1. size_t类型size_t是一种无符号整数类型,它在 C 语言标准库中被定义,用于表示对象的大小或数组的长度等。其具体的类型定义可能因不同的系统或编译器而有所不同,但它总是能够保证足够大,以容纳任何有效的内存大小值。在大多数常见的系统中,size_t通常等同于unsigned int或者unsigned long
  2. size参数含义size参数指定了要分配的内存大小,单位是字节。这个值决定了malloc函数从堆中划分出多大的内存块。例如,如果要分配足够的内存来存储一个包含 100 个char类型元素的数组,可以使用malloc(100 * sizeof(char)),这里sizeof(char)的值通常为 1 字节,所以总共请求分配 100 字节的内存。如果要分配内存用于存储一个复杂的结构体,可以使用sizeof(struct_name)来获取结构体的大小作为size的值。

malloc代码实现讲解

初步讲解语法结构使用

  1. 代码
  2. 下面进行代码解释:
  3. 我们需要知道,malloc创建内存空间的时候,是不初始化内存空间的,realloc才是初始化内存空间的。
  4. 我们创建内存空间之后需要用一个指针指向内存空间
     
  5. malloc我们需要强制类型转换为int*类型,malloc函数返回的是一个void*类型的指针,即通用指针。这意味着它可以指向任何类型的数据,但本身没有特定的类型信息。 要将其转换为int*类型(或其他特定类型指针)用来存储整数
  6.  如果没有创建成功,那么我会使用一个判断,最后我们使用perror打印出错误,并且返回正常值
  7. 注意事项2:malloc函数是 C/C++ 中用于动态分配内存的函数,它从堆上分配指定大小的内存空间。malloc在创建内存空间的时候是不初始化内存空间的。这意味着分配得到的内存空间中的内容是未定义的,可能是任意值。
  8. 注意事项2:realloc函数用于调整已分配内存块的大小。realloc并不一定初始化内存空间。如果是将内存块扩大,新增加的部分是未初始化的;如果是缩小内存块,超出原有范围的部分内容会丢失,但原有范围内的内容保持不变,也不会进行初始化。
  9. 注意事项3:动态内存的开辟空间都在堆区

分配存储字符串的内存

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

int main() {
    // 分配足够存储长度为 20 的字符串的内存(包括'\0')
    char *str = (char *)malloc(20 * sizeof(char));
    if (str!= NULL) {
        // 使用strcpy将字符串复制到分配的内存中
        strcpy(str, "Hello, world!");
        printf("%s\n", str);
        // 使用完毕后释放内存
        free(str);
        str = NULL;
    }
    return 0;
}
头文件包含
  1. <stdio.h>:这个头文件提供了标准输入输出函数相关的声明,比如printf函数用于在控制台输出信息。
  2. <stdlib.h>:它包含了mallocfree等函数的声明,用于动态内存分配和释放操作。
  3. <string.h>:提供了字符串处理函数的声明,例如strcpy函数用于复制字符串。
main函数
  1. main函数是 C 程序的入口点。程序从main函数开始执行,并且在main函数执行完毕后结束。这里main函数返回0,通常表示程序正常结束。
内存分配
  1. 这行代码使用malloc函数从堆中动态分配内存。
  2. 20 * sizeof(char)sizeof(char)通常为 1 字节,所以这里请求分配 20 字节的内存空间。这是为了能够存储一个长度为 20 的字符串(包括字符串结尾的'\0'字符)。
  3. (char *)malloc函数返回的是void *类型的指针,需要将其强制转换为char *类型,因为我们要存储字符数据,这样在后续操作中编译器才知道如何正确处理这个指针所指向的内存。
  4. char *str:定义了一个字符指针str,它将指向malloc分配的内存块的起始位置。如果malloc分配成功,str将指向有效的内存;如果分配失败(比如内存不足),str将为NULL
字符串复制与输出
  1. 条件判断:首先检查str是否为NULL,这是一个良好的编程习惯,因为如果malloc分配内存失败,继续操作NULL指针会导致程序崩溃。
  2. 字符串复制strcpy(str, "Hello, world!");使用strcpy函数将字符串"Hello, world!"复制到str所指向的内存区域。strcpy函数会把源字符串中的每个字符(包括'\0')复制到目标内存地址,直到遇到源字符串的'\0'为止。
  3. 字符串输出printf("%s\n", str);使用printf函数输出str所指向的字符串。%s是格式化字符串,表示输出一个字符串,\n是换行符,使输出更美观。
内存释放(下面会讲解)
  1. free(str);:调用free函数释放之前通过malloc分配的内存。这是非常重要的一步,释放内存可以避免内存泄漏,使程序占用的内存资源得到合理的管理。
  2. str = NULL;:将str指针设置为NULL。这是一种安全的编程实践,因为在释放内存后,str所指向的内存已经被归还给操作系统,再访问str可能会导致错误(比如野指针问题)。将其设置为NULL可以明确表示这个指针不再指向有效的内存。

为结构体数组分配内存(进阶)

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

// 定义一个结构体
typedef struct Student {
    int id;
    char name[20];
} Student;

int main() {
    // 分配可以存储 5 个Student结构体的内存
    Student *students = (Student *)malloc(5 * sizeof(Student));
    if (students!= NULL) {
        // 初始化结构体数组中的元素
        for (int i = 0; i < 5; i++) {
            students[i].id = i + 1;
            strcpy(students[i].name, "Student");
        }
        // 使用完毕后释放内存
        free(students);
        students = NULL;
    }
    return 0;
}
头文件包含
  1. <stdio.h>:这个头文件包含了标准输入输出函数的相关声明,例如printf函数等,但在这段代码中没有使用到stdio.h中的函数。不过在实际的程序开发中,可能后续会添加一些输入输出相关的操作,所以在这里先包含了这个头文件。
  2. <stdlib.h>:用于提供mallocfree等动态内存管理函数的声明,是实现本程序动态内存分配和释放功能所必需的。
结构体定义
  1. 这里定义了一个名为Student的结构体。结构体是一种复合数据类型,可以将不同类型的数据组合在一起。
  2. Student结构体中有两个成员:
    1. int id:用于存储学生的编号,是一个整数类型。
    2. char name[20]:用于存储学生的姓名,是一个长度为 20 的字符数组。这里可以存储长度不超过 19 的字符串(因为需要留一个字节用于存储字符串结尾的'\0'字符)。

main函数

  1. main函数是 C 程序的入口点,程序从这里开始执行。main函数返回0表示程序正常结束。
动态内存分配
  1. 这行代码使用malloc函数从堆中动态分配内存。
  2. 5 * sizeof(Student)sizeof(Student)会计算出Student结构体的大小(包括id成员和name成员所占的空间),然后乘以5,表示要分配足够存储 5 个Student结构体的内存空间。
  3. (Student *)malloc函数返回的是void *类型的指针,通过强制类型转换将其转换为Student *类型,因为我们要将这块内存用于存储Student结构体类型的数据。这样,students指针就指向了分配的内存块的起始位置,它可以被当作一个Student类型的数组来使用。如果malloc分配成功,students指向有效的内存;如果分配失败(例如内存不足),students将为NULL
结构体数组初始化
  1. 首先进行条件判断if (students!= NULL),检查内存分配是否成功。如果students不为NULL,说明malloc成功分配了内存,可以对这块内存进行操作。
  2. 然后使用for循环遍历students数组(这里把students当作一个Student结构体类型的数组),对每个结构体元素进行初始化:
    1. students[i].id = i + 1:为每个Student结构体的id成员赋值,值为当前循环次数i加 1。
    2. strcpy(students[i].name, "Student");:使用strcpy函数将字符串"Student"复制到每个Student结构体的name成员中。
内存释放
  1. free(students);:调用free函数释放之前通过malloc分配的内存。这一步非常关键,它将分配的内存归还给操作系统,避免内存泄漏。
  2. students = NULL;:将students指针设置为NULL。这是一种良好的编程习惯,因为在释放内存后,students所指向的内存已经被释放,如果不小心再次使用students指针,可能会导致程序错误(例如出现野指针问题)。将其设置为NULL可以明确表示这个指针不再指向有效的内存。

分配二维数组的内存(进阶)

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

int main() {
    // 分配内存用于存储一个3行4列的二维数组(模拟)
    int **matrix = (int **)malloc(3 * sizeof(int *));
    if (matrix!= NULL) {
        for (int i = 0; i < 3; i++) {
            matrix[i] = (int *)malloc(4 * sizeof(int));
        }
        // 使用二维数组
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 4; j++) {
                matrix[i][j] = i * 4 + j;
            }
        }
        // 释放内存
        for (int i = 0; i < 3; i++) {
            free(matrix[i]);
        }
        free(matrix);
        matrix = NULL;
    }
    return 0;
}
头文件包含
  1. <stdio.h>:虽然此代码中没有使用到标准输入输出函数(如printf),但在实际开发中,可能会需要添加相关功能,所以先包含这个头文件。
  2. <stdlib.h>:这个头文件是必需的,因为代码中使用了mallocfree函数进行动态内存分配和释放。
 
main函数主体

main函数是 C 程序的入口点,程序从这里开始执行,return 0表示程序正常结束。

 
二维数组内存分配(模拟方式)
  1. 这一步是为了创建一个指针数组,用于模拟二维数组的行指针。malloc(3 * sizeof(int *))分配了足够的内存来存储 3 个int *类型的指针(每个指针将指向二维数组的一行)。
  2. (int **)是对malloc返回的void *指针进行类型转换,将其转换为int **类型,以便可以作为指向int *类型指针的指针来使用。如果内存分配成功,matrix将指向这块新分配的内存;如果分配失败(如内存不足),matrix将为NULL
为每行分配内存
  1. 首先检查matrix是否为NULL,以确保第一步的内存分配成功。只有在成功分配了指针数组的内存后,才继续为每一行分配内存。
  2. for循环中,matrix[i] = (int *)malloc(4 * sizeof(int));matrix的每个元素(即每个行指针)分配内存,用于存储 4 个int类型的数据,这样就模拟出了一个 3 行 4 列的二维数组。这里同样需要将malloc返回的void *指针转换为int *类型。
使用二维数组
  1. 这里使用两个嵌套的for循环遍历整个模拟的二维数组。matrix[i][j]用于访问二维数组中的每个元素,其中i表示行索引,j表示列索引。通过matrix[i][j] = i * 4 + j为每个元素赋值,这里的值是根据其在二维数组中的位置计算得到的。
内存释放
  1. 内存释放需要分两步进行。首先,通过for循环for (int i = 0; i < 3; i++) { free(matrix[i]); }释放每一行所占用的内存。这是因为每一行的内存是单独分配的,需要逐个释放。
  2. 然后,使用free(matrix)释放最初分配的用于存储行指针的内存。
  3. 最后,将matrix设置为NULL,这是一种良好的编程习惯。释放内存后,matrix所指向的内存已经归还给操作系统,将其设置为NULL可以避免出现野指针问题,即防止程序意外地访问已释放的内存。

free的讲解:

语法结构讲解:

我们讲解完malloc,此时我们创建了空间,就像住酒店一样,我们住一晚用完了,那么我们就要退房,谁负责退房,那就是free,我们不可能一直霸占房间。接下来让我们进入free的讲解。

前言

  1. 我们打开C语言文档,注意这里不是官方文档
    malloc - C++ Reference
  2. 我们发现在C语言文档里面的语法结构是这个样子的free(void *ptr),下面我会进行讲解
  3. 从C语言的文档里面我们可以看见,头文件cstdlib

返回类型

  1. free函数没有返回值,它的作用是释放之前通过malloccallocrealloc等动态分配内存函数所分配的内存空间。

函数名

  1. free是 C 语言标准库中用于内存释放的函数名。当程序不再需要使用通过动态分配得到的某块内存时,就需要调用free函数来将其归还给操作系统,避免内存泄漏问题。

参数

  1. void *ptr:这是一个指向要释放内存的指针。这个指针必须是之前通过malloccalloc或者realloc等内存分配函数成功分配内存后返回的指针。如果传入的指针是NULLfree函数会直接返回,不执行任何操作。例如:

总结

  1. free是使用其实很简单,在malloc的使用里面我们每次都会释放内存空间,那就是使用free来释放内存空间,这样做的目的是防止内存泄露
  2. 什么时候使用free,free函数用于释放通过malloccallocrealloc等函数动态分配的内存,这时候是创建了空间,我们需要释放空间,释放资源

注意事项:

  1. 只能释放动态分配的内存free只能用于释放通过动态内存分配函数分配的内存,不能用于释放栈上分配的内存(如局部变量)或者静态分配的内存(如全局变量)。
  2. 不能重复释放内存:对同一块已经释放过的内存再次调用free函数会导致未定义行为,可能会使程序崩溃。
  3. 释放内存后将指针置空:释放内存后,将指针设置为NULL是一种良好的编程习惯,可以避免因指针仍指向已释放的内存而导致的野指针问题。

补充说明

前言;

这一篇文章是刚学习计算机写的文章,偶然间有一个计算机同伴评论了,说是看不懂,我就仔细回顾了一下文章,发现存在很大的问题,所以我就把文章布局重新整理一下,整理完之后发现剩一点之前老文章的文字,觉得很有意思,所以就保留下来。这一点可以看,可以不看,我这里是当做纪念留用。

malloc初步讲解语法结构使用是补充说明:

  1. 进行强制类型的转化因为从语法格式我们可以看出来,这里是的void*类型,也就是不明确的类型,为什么,因为C语言官方在设定的时候不清楚你开辟内存空间的目的是干什么用的,不知道是用于整数的存储,还是用于字符的打印,还是干什么。所以这里给出无返回类型,只需要你在使用的时候进行强制类型转化就可以。这样可以提升代码的兼容性和健壮性。
  2. 对于空间的释放,释放空间之后尽量的让指针指向NULL,也就是空指针,因为当指针指向的空阿金释放之后,本质上他是不指向任何的数值了,也就是此时他是野指针了,虽然你不使用他了,但是最好指向空指针。防止内存的泄露
  3. 使用空间的时候,你不能让指针跟着走,我们可以看到代码里面,
  4. 使用的是这个代码*(p+i)=I+1;。而不是*(p)=i;p++;这样的代码。
  5. 为什么,因为当指针走远之后,你进行释放空间的时候,本质上释放的是指针移动后指向的空间,你让指针一直移动,移动到开辟的空间的最后一个位置,然后再进行释放空间的时候,其实本质上释放的是开辟空间之后的空间,也就是没有释放空间。
  6. 你创建了空间,但是没有释放空间,此时会导致内存泄露。
  7. 内存泄露的问题,也就是内存占用少的时候还不明显,但是当有大量程序运行的时候,就会导致内存一直占用,但是得不到释放。从而导致崩盘。

malloc第两种写法

可以是sizeof(int)=四个字节

然后5*sizeof(int)=20个字节

第二种就是上述的写法malloc(20)

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值