C语言_malloc动态开辟内存空间

本文介绍了C语言中动态内存管理的基础知识,包括malloc函数用于动态开辟内存,strcpy函数用于字符串拷贝,free函数用于释放内存以避免内存泄漏,以及realloc函数用于内存空间的扩容。特别强调了悬挂指针的风险和释放内存后设置指针为NULL的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

malloc动态开辟内存空间

01——malloc动态开辟内存空间函数:

通常定义数组的时候就把内存地址定好了,比如:

ind data[] = {1,2,3,4,5};

char cdata[6] = {‘h’,’e’,’l’,’l’,’o’};

有一种需求:我不希望一开始就耗费这么大的空间,我想按需求开辟内存空间,引入动态开辟字符串malloc函数。

malloc函数就是我们在堆上面动态的开辟字符串,它跟以前我们数组占用的使用的内存空间是不一样的,以前我们数组都是在栈区,那么堆是malloc对应的内存空间。

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

c库函数:void *malloc(size_t size)分配所需的内存空间,并返回一个指向它的指针。

*我们之前写过一个例子,对这个p进行赋值的时候会产生一个段错误,p是一个野指针,对野指针赋值我们会看到犯错,如果不会犯错程序中end会执行,你会看到end根本就不会执行,p没有具体的指向哪个内存空间。

断错误

我们使用malloc函数开辟一个内存空间用p来承接它:

编译我们看到:想要使用malloc函数需要包含库文件#include <stdlib.h>

此时p就不是野指针了,p就有了具体的内存指向:内存地址:0x0000000000727E80

02——strcpy拷贝函数应用:

我们可以开辟一个内存空间,当然也可以开辟多个内存空间:,这里开辟了12个内存空间,仍然用p来承接。这个时候就需要往里面放数据了,于是引入一个函数strcpy字符串拷贝函数)

函数原型:char *strcpy(char* dest, const char *src);

字符串拷贝函数strcpy(拷贝到哪里去,从哪里拷贝);

程序写完之后发现有一个警告,提示如果要使用strcpy这个函数就必须包含头文件#include <string.h>

现在看到可以了,输出demo结果:chenlichen这个字符串

——???————————但是现在有一个问题???

刚开始p指向的是你开辟的一个内存空间,但是你又开辟了12字节内存空间也让p指向了它,此时p就会和之前开辟的那一块内存空间断开,并指向新开辟的12字节内存空间;此时那一块和p断开的内存就成了悬挂无用的内存了,已经找不到这个地址了,空闲在这了。像这种空闲在这的内存我们通常需要通过free将其释放。

    因为malloc它在堆上面开辟内存空间,数组和普通的变量是在栈开辟内存空间,在函数调用结束以后它会清理栈里面的数组(回收内存),那堆只有到程序结束以后内存才会释放(有一个风险,如果你是一个死循环,不断地malloc,有可能会耗尽你计算机上堆的内存资源),所以需要通过free将其释放掉!!!

03——free函数应用:

free:c库函数void free(void *ptr);

作用:释放之前调用callocmallocrealloc所分配的内存空间,释放无用内存,防止内存泄漏,防止悬挂指针(悬挂指针是野指针的一种)。

尽量不要悬挂指针!!!

free释放掉无用内存后,一般让p = NULL;

04——realloc扩容函数应用:

比如说现在有这么多的数据需要放到内存空间中,而上面malloc只开辟了12个字节的内存空间,如果这么多数据需要放到里面的话是不行的,会发生越界,程序崩溃!!!

这个时候就可以使用realloc扩容函数来解决!

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

字符串扩容函数realloc(原本扩容地址,要继续增大多少);

作用:尝试重新调整之前调用malloccalloc所分配的ptr所指向的内存块大小。

注意:扩容前和扩容和的地址一样的!!!

05——memset函数应用:

清理内存空间memset函数应用

memset函数原型:void *memset(void *str,int c,size_t n)

void *memset(新开辟的内存地址,要初始化的内容,内存大小);

把malloc申请的12个字节的内存空间,全部清理成\0

综合demo:

<think>我们正在讨论C语言malloc函数与数组在动态开辟内存空间上的区别。根据引用内容,我们可以总结以下几点: 1. **内存分配位置**: - 数组:在栈(stack)上分配内存空间。栈空间由编译器自动管理,函数执行结束时,栈上的变量和数组会被自动回收(释放内存)。 - `malloc`函数:在堆(heap)上分配内存空间。堆空间需要程序员手动管理,使用`free`函数释放内存,否则可能导致内存泄漏。 2. **内存分配方式**: - 数组:声明数组时,数组大小必须是编译时常量(C99之前)或可以用变量(C99支持变长数组)。但变长数组在函数退出时也会被自动回收。 - `malloc`函数:可以在运行时动态决定需要分配的内存大小,分配的内存空间直到调用`free`函数或程序结束才被释放。 3. **内存分配大小**: - 数组:大小在声明时确定(对于静态数组)或由变量确定(对于变长数组,但受栈空间限制)。 - `malloc`函数:可以分配较大的内存空间(受堆空间限制),并且可以根据需要重新调整大小(使用`realloc`函数)。 4. **内存管理**: - 数组:无需手动释放,由系统自动回收。 - `malloc`函数:必须手动释放,否则会造成内存泄漏。特别是在循环中不断分配而不释放,可能导致程序耗尽内存。 5. **初始化**: - 数组:声明时可以初始化(如`int arr[5] = {0};`),未初始化的部分自动初始化为0(全局数组)或随机值(局部数组)。 - `malloc`函数:分配的内存不会自动初始化,需要使用`memset`函数初始化,或者使用`calloc`函数分配并初始化为0。 6. **灵活性**: - 数组:一旦声明,大小固定(变长数组的大小在声明时确定后也不能改变)。 - `malloc`函数:分配的内存可以通过`realloc`函数调整大小。 7. **风险**: - 数组:如果数组过大,可能导致栈溢出(因为栈空间通常较小)。 - `malloc`函数:忘记释放内存会导致内存泄漏,多次释放同一块内存会导致程序崩溃。 下面我们通过代码示例来说明: **数组(栈上分配)**: ```c #include <stdio.h> void func() { int n = 5; int arr[n]; // 变长数组(C99支持),在栈上分配 for (int i = 0; i < n; i++) { arr[i] = i; } // 函数结束,arr数组自动释放 } ``` **malloc(堆上分配)**: ```c #include <stdio.h> #include <stdlib.h> void func() { int n = 5; int *arr = (int*)malloc(n * sizeof(int)); // 在堆上分配 if (arr == NULL) { // 处理分配失败 return; } for (int i = 0; i < n; i++) { arr[i] = i; } // 使用完后必须释放 free(arr); } ``` 总结: - 数组(包括变长数组)适合小规模、生命周期与函数相同的数据,使用简单,无需手动释放。 - `malloc`适合需要动态控制生命周期、较大规模的数据,但需要手动管理内存。 §§相关问题§§ 1. 在C语言中,如何避免使用malloc时出现内存泄漏? 2. 变长数组(VLA)和malloc分配的数组在性能上有什么区别? 3. 什么情况下应该使用calloc而不是malloc?</think>### C语言中 `malloc` 函数与数组动态开辟内存空间的区别 #### 1. **内存分配位置** - **数组**: 在栈(Stack)上分配内存。 示例:`int arr[10];` 编译器自动分配连续栈空间,函数结束时自动回收[^3]。 - **`malloc`**: 在堆(Heap)上分配内存。 示例:`int* arr = malloc(10 * sizeof(int));` 需手动管理内存,程序结束前需调用 `free()` 释放[^3]。 #### 2. **内存生命周期** - **数组**: 作用域限于当前函数或代码块。函数结束时,栈内存自动释放[^3]。 ```c void func() { int arr[5]; // 函数结束,arr自动释放 } ``` - **`malloc`**: 内存持续存在,直到显式调用 `free()` 或程序结束。 ```c void func() { int* arr = malloc(5 * sizeof(int)); free(arr); // 必须手动释放,否则内存泄漏 } ``` #### 3. **大小灵活性** - **数组**: - 静态数组:大小需编译时确定(如 `int arr[10]`)。 - 变长数组(VLA,C99支持):大小可运行时决定,但仍在栈上分配,可能引发栈溢出[^4]。 ```c int n = 10; int arr[n]; // 栈分配,n过大时崩溃 ``` - **`malloc`**: 大小完全动态,支持运行时计算,适合大内存需求[^2][^5]。 ```c int n = 10000; int* arr = malloc(n * sizeof(int)); // 堆分配,避免栈溢出 ``` #### 4. **初始化与安全性** - **数组**: 可声明时初始化(如 `int arr[3] = {1,2,3};`),未初始化时值为随机数(局部变量)或0(全局变量)。 - **`malloc`**: 分配的内存未初始化,需手动置零(用 `memset` 或 `calloc`)[^5]。 ```c int* arr = malloc(5 * sizeof(int)); memset(arr, 0, 5 * sizeof(int)); // 手动初始化 ``` #### 5. **内存管理风险** - **数组**: 栈空间有限(通常几MB),大数组易导致栈溢出[^3]。 - **`malloc`**: - 忘记 `free()` 会导致内存泄漏。 - 多次释放同一指针引发崩溃。 - 分配失败返回 `NULL`,需检查[^2][^3]。 #### 6. **代码示例对比 **数组(栈分配)**: ```c #include <stdio.h> int main() { int n = 3; int arr[n]; // 变长数组(栈分配) for (int i=0; i<n; i++) arr[i] = i; // 函数结束自动释放 } ``` **`malloc`(堆分配)**: ```c #include <stdio.h> #include <stdlib.h> int main() { int n = 3; int* arr = malloc(n * sizeof(int)); // 堆分配 if (arr == NULL) exit(1); // 检查分配失败 for (int i=0; i<n; i++) arr[i] = i; free(arr); // 必须手动释放 } ``` ### 总结 | **特性** | **数组** | **`malloc`** | |------------------|------------------------|--------------------------| | 内存位置 | 栈(Stack) | 堆(Heap) | | 生命周期 | 自动回收 | 需手动 `free()` | | 大小确定时机 | 编译时或运行时(VLA) | 完全运行时动态 | | 大内存适应性 | 易栈溢出 | 适合大内存 | | 初始化 | 可声明时初始化 | 未初始化,需手动置零 | | 内存管理风险 | 低 | 需防泄漏/多次释放/空指针 | > **关键区别**:数组依赖栈空间,自动管理但灵活性低;`malloc` 使用堆空间,手动管理但支持动态大规模内存[^2][^3][^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值