内存的动态申请

 

目录

前言

如何动态申请

内存释放——free

如何判断一个内存是否已经被释放

calloc函数

relloc函数


前言

    为什么要动态申请内存?我们知道函数的调用是C语言里非常常见的,有些函数调用时需要传递参数比如数组的地址,如果我们在函数里只用静态申请数组的话会使程序的可移植性降低,为了灵活使用函数,所以会用到动态申请。

    例如:我们要写一个函数,函数的传参为numsSize,函数的功能是申请一个大小为numsSize大小的整形数组,这个numsSize并不是个常数,这时就必须用到动态申请了。

如何动态申请

    错误示范:

int numsSize;
int arr[numsSize];

这样写的话编译器是不会让程序通过的,因为numsSize不是个常数。

这时就要用到动态申请的函数malloc了。该函数被包含在#include <stdlib.h>头文件里。用法如下

int *arr=malloc(Size);

其中Size是需要申请的空间大小,单位是字节,一般我们不这样写,我们一般写成:

int *arr=malloc(sizeof(int)*numsSize);

我们需要申请一个大小为numsSize的整形数组,所以数组的大小为sizeof(int)*numsSize,这样写直观明了。

内存释放——free

    动态申请内存和一般申请相比不同点在于,静态申请的内存是放在栈区的,计算机内存里除了栈区还有堆区,堆区就是动态申请内存的存放地,栈区的特点是申请的内存不需要我们手动释放,一个变量的生命周期结束后系统会自动将其释放;而堆区需要我们手动释放,如果我们不手动释放,则有可能会造成内存泄漏等重大问题(内存泄漏是指一个程序在运行时所占用的内存不断增大,不及时处理会导致计算机内存占用过多)。

    所以我们使用动态申请的内存结束后,一定要记等将其释放:

free(arr);

    注意free释放空间只对动态申请的空间起用,不可以手动释放静态申请的空间,且对动态申请的空间只能手动释放一次,不可重复释放。

如何判断一个内存是否已经被释放

动态申请的空间被释放后,如果不将其置空,则其会变成一个野指针,所以我们可以养成一个习惯:每次free释放空间后再将其置空

free(arr);
arr = NULL;

    要判断arr是否已经被释放,我们可以增加一个判断:

if(arr!=NULL){
    free(arr);
	arr = NULL;
}

    所以针对以上的案例:申请一个大小为numsSize的数组,我们可以给出以下答案:

int main(){
	int numsSize;
	scanf("%d", &numsSize);
	int *arr = malloc(sizeof(int)*numsSize);
	free(arr);
	return 0;
}

calloc函数

    malloc函数是动态申请常见的函数,除此之外还有一些函数也能动态申请,但具体功能不同,比如calloc函数:

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

size_t是无符号整形的定义,该函数的功能是在堆区中动态申请nmemb块,每块大小为size的内存,所以申请的空间总大小为nmemb*size。该函数使用后也需要手动释放内存。

relloc函数

    原型 void* realloc(void* s, unsigned int newsize);

s是已经动态申请过了的空间地址,该函数的功能是对已经动态申请过的空间s重新申请一个大小为newsize的空间。如果原空间地址后面的空间足够多,则该函数直接在原空间进行扩容,如果不够,则开辟一个新空间,然后将原空间销毁,返回新空间地址。

以上便是对动态申请的简单了解。

在多线程环境下进行内存动态申请和释放时,需要注意以下几个方面: ### 线程安全 多个线程同时访问和修改内存分配器的数据结构可能会导致数据不一致和程序崩溃。因此,内存分配和释放操作必须是线程安全的。在C和C++中,标准库提供的`malloc`、`free`、`new`和`delete`通常是线程安全的,它们内部使用了锁机制来保证同一时间只有一个线程可以进行内存分配或释放操作。例如: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> void* thread_function(void* arg) { int* ptr = (int*)malloc(sizeof(int)); if (ptr != NULL) { *ptr = 10; free(ptr); } return NULL; } int main() { pthread_t thread; pthread_create(&thread, NULL, thread_function, NULL); pthread_join(thread, NULL); return 0; } ``` ### 避免内存泄漏 在多线程环境中,由于线程的并发执行,可能会出现某个线程申请内存,但由于异常或其他原因没有正确释放的情况,从而导致内存泄漏。因此,需要确保在每个线程中都正确地释放所申请内存。可以使用RAII(资源获取即初始化)技术来管理内存,确保在对象生命周期结束时自动释放内存。在C++中,可以使用智能指针来实现RAII,例如`std::unique_ptr`和`std::shared_ptr`: ```cpp #include <iostream> #include <memory> #include <thread> void thread_function() { std::unique_ptr<int> ptr(new int(10)); std::cout << *ptr << std::endl; } int main() { std::thread t(thread_function); t.join(); return 0; } ``` ### 死锁问题 如果在内存分配和释放过程中使用了锁,需要注意避免死锁的发生。死锁是指两个或多个线程相互等待对方释放资源,从而导致程序无法继续执行的情况。为了避免死锁,需要确保线程按照相同的顺序获取锁,并且在不需要锁时尽快释放。 ### 内存碎片 多线程环境下频繁的内存分配和释放可能会导致内存碎片问题,即内存中存在大量不连续的小块空闲内存,使得无法分配较大的连续内存块。为了减少内存碎片,可以采用内存池技术,预先分配一大块内存,然后在需要时从内存池中分配小块内存,释放时将内存块返回给内存池。 ### 同步问题 在某些情况下,多个线程可能需要共享同一块内存,此时需要进行同步操作,确保一个线程在使用内存时,其他线程不会同时修改该内存。可以使用互斥锁、读写锁等同步机制来保证线程安全。例如,使用`std::mutex`来保护共享内存: ```cpp #include <iostream> #include <thread> #include <mutex> std::mutex mtx; int shared_memory = 0; void increment() { for (int i = 0; i < 100000; ++i) { std::lock_guard<std::mutex> lock(mtx); ++shared_memory; } } int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "Shared memory: " << shared_memory << std::endl; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值