【C语言】动态内存管理

前言
动态内存管理是计算机编程中的一个重要概念,它涉及到在程序运行时分配和释放内存。动态内存管理允许程序在需要时分配内存,而不是在编译时或运行时静态地分配固定大小的内存。接下来我们将学习几个函数,通过这几个函数来开辟和释放内存。以及柔性数组的学习。

文章目录

  • 一、malloc和free
  • 二、calloc和realloc
  • 三、 动态内存笔试题分析
    • 第一题
    • 第二题
    • 第三题
    • 第四题
  • 四、柔性数组
  • 五、总结C/C++中程序内存区域划分

一、malloc和free

malloc和free都声明在 stdlib.h 头⽂件中

  • malloc用于动态内存开辟
void* malloc (size_t size)
  1. size_t 是一个无符号整数类型,通常用来表示要分配的内存块的大小(以字节为单位)
  2. malloc 返回一个 void 指针(通常需要显式类型转换为适当的指针类型),该指针指向分配的内存块的起始地址。如果分配失败,malloc 返回 NULL 指针

  • free是⽤来做动态内存的释放和回收
 void free (void* ptr)
  1. ptr 是一个指向要释放的内存块的指针。
  2. 这个指针应该是之前由 malloc、calloc 或 realloc 返回的指针(也就是说,如果指向的空间不是动态开辟的,那free函数的行为是未定义的)

举例:

#include 
#include 
int main(){
	int num = 0;
	scanf("%d", &num);
	int arr[num] = { 0 };
	int* ptr = NULL;
	ptr = (int*)malloc(num * sizeof(int));//开辟大小为num个int,并将返回的指针赋给ptr
	
	if (NULL != ptr){//判断ptr指针是否为空
		int i = 0;
		for (i = 0; i < num; i++){
			*(ptr + i) = 0;
		}
	}
	free(ptr);//释放ptr所指向的动态内存
	ptr = NULL;//是否有必要?
	return 0;
}

二、calloc和realloc

  • calloc与 malloc 不同,它会分配一块指定大小的内存,并将所有字节初始化为零值。
void *calloc(size_t num_elements, size_t element_size);
  1. num_elements 是要分配的元素数量。
  2. element_size 是每个元素的大小(以字节为单位)。
  3. calloc 返回一个 void 指针,指向分配的内存块的起始地址。如果分配失败,calloc 返回 NULL 指针。

举例:

#include 
#include 
int main(){
	int* p = (int*)calloc(10, sizeof(int));//开辟10个int,并初始化为0
	
	if (NULL != p){
		int i = 0;
		
		for (i = 0; i < 10; i++){
			printf("%d ", *(p + i));
		}
	}
	free(p);
	p = NULL;
	return 0;
}

运行结果:
【C语言】动态内存管理_第1张图片


  • realloc用于重新分配(调整大小)动态分配内存块
void *realloc(void *ptr, size_t new_size);
  1. ptr 是一个指向要重新分配的内存块的指针,通常是先前由 malloc、calloc 或 realloc 返回的指针。
  2. new_size 是新的内存块大小(以字节为单位)。
  3. realloc 返回一个 void 指针,指向重新分配后的内存块的起始地址。如果分配失败或者 new_size 为零,realloc 返回 NULL 指针。此外,如果 ptr 为 NULL,则 realloc 的行为等同于 malloc(new_size),即它将分配一块新的内存块。

使用realloc函数需要注意一个问题:

用malloc或calloc开辟内存后,使用realloc调整大小时有两种情况:

  1. 原有空间之后有⾜够⼤的空间
  2. 原有空间之后没有⾜够⼤的空间

【C语言】动态内存管理_第2张图片

#include 
#include 

int main() {
    int* arr;
    size_t original_size = 3;
    size_t new_size = 5;

    // 分配一个包含3个整数的数组
    arr = (int*)malloc(original_size * sizeof(int));

    if (arr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }

    // 重新分配内存大小,将 ptr 的值更新为 realloc 的返回值
    arr = (int*)realloc(arr, new_size * sizeof(int));

    if (arr == NULL) {
        printf("内存重新分配失败\n");
        return 1;
    }
    // 释放内存
    free(arr);
    return 0;
}

三、 动态内存笔试题分析

第一题

请问运⾏Test 函数会有什么样的结果?

void GetMemory(char* p){
	p = (char*)malloc(100);
}

void Test(void){
	char* str = NULL;
	GetMemory(str);//将空指针作为实参
	strcpy(str, "hello world");//如果开辟动态内存成功,p是起始点,但str依然是空指针,所以不会打印
	printf(str);
	free(str);
}

第二题

请问运⾏Test 函数会有什么样的结果?

char* GetMemory(void){
	char p[] = "hello world";//很明显,p是局部变量
	return p;
}

void Test(void){
	char* str = NULL;
	str = GetMemory();//数组p在GetMemory函数结束后销毁,所以返回值是NULL
	printf(str);//因此并不会打印hello world
	free(str);
}

第三题

请问运⾏Test 函数会有什么样的结果?

//这是第一题的正确修改,结果是hello
void GetMemory(char** p, int num){
	*p = (char*)malloc(num);
}

void Test(void){
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
	free(str);
}

第四题

请问运⾏Test 函数会有什么样的结果?

void Test(void){
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);//释放过早,不会打印world
	
	if (str != NULL){
		strcpy(str, "world");
		printf(str);
	}
}

四、柔性数组

柔性数组(Flexible Array Member)是一种C语言中的高级数据结构,它允许你在结构体的末尾定义一个大小可变的数组,这个数组的大小是在运行时动态确定的。柔性数组在处理变长数据时非常有用,例如在解析网络协议、文件格式等情况下。

柔性数组的定义方式是在结构体内部的最后一个成员定义一个数组,但不指定数组的大小。这个数组的大小会在结构体实例分配内存时动态确定。
下面是一个示例:

#include 
#include 

// 定义包含柔性数组的结构体
typedef struct FlexibleArrayStruct {//将struct FlexibleArrayStruct重命名为fas
    int length;  // 数组的实际长度
    int data[];  // 柔性数组,它的大小在运行时确定
}fas;

int main() {
    // 计算结构体的总大小,包括柔性数组的空间
    size_t size = sizeof(fas) + 5 * sizeof(int);

    // 动态分配内存来创建一个包含柔性数组的结构体实例
    fas* myStruct = (fas*)malloc(size);

    if (myStruct == NULL) {
        printf("内存分配失败\n");
        return 1;
    }

    // 设置柔性数组的长度
    myStruct->length = 5;

    // 使用柔性数组存储数据
    for (int i = 0; i < myStruct->length; i++) {
        myStruct->data[i] = i * 2;
    }

    // 访问柔性数组的数据
    printf("柔性数组的内容:");
    for (int i = 0; i < myStruct->length; i++) {
        printf("%d ", myStruct->data[i]);
    }
    printf("\n");

    // 释放内存
    free(myStruct);

    return 0;
}

运行结果:
【C语言】动态内存管理_第3张图片


五、总结C/C++中程序内存区域划分

【C语言】动态内存管理_第4张图片
C/C++程序内存分配的⼏个区域:

  1. 栈区(stack):在执⾏函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。
  2. 堆区(heap):⼀般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配⽅式类似于链表。
  3. 数据段(静态区)(static):存放全局变量、静态数据。程序结束后由系统释放。
  4. 代码段:存放函数体(类成员函数和全局函数)的⼆进制代码。

在这里插入图片描述
如果你喜欢这篇文章,点赞+评论+关注⭐️哦!
欢迎大家提出疑问,以及不同的见解。

你可能感兴趣的:(c语言,算法,开发语言,学习)