在C语言中我们在栈上开辟的空间是固定的,一旦确定好大小就不能随意改变,就想你创建了

动态内存函数

malloc

C语言之动态内存管理-LMLPHP

由于可能会返回NULL,一旦被解引用就是野指针的非法访问。
所以我们要对返回的指针做检查

下面我们来实践一下:

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

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	// 使用

	return 0;
}

free

介绍完malloc 函数后,我们来介绍一下如何释放空间,由于堆的空间是有限的,如果一直开辟不去释放空间,那就会一直占用堆上的空间,导致重新运行时内存泄漏。所以我们需要学会释放空间!

C语言之动态内存管理-LMLPHP

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

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	// 使用

	free(p);
	p = NULL;

	return 0;
}

calloc

C语言之动态内存管理-LMLPHP

这个函数也是申请 num 个 size 大小的字节空间,唯一和malloc不同的是,calloc会将空间全部初始化为0

C语言之动态内存管理-LMLPHP

realloc

C语言之动态内存管理-LMLPHP

这个函数就是扩大空间的,如果你觉得malloc或者calloc开辟的空间小了,就传入一个指针,输入你想要扩大的字节数,就可以了。

这里的开辟方式分两种:

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

int main()
{
	int* p = (int*)calloc(5, sizeof(int));
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	// 使用

	int* ptr = (int*)realloc(p, 50);
	if (ptr == NULL)
	{
		perror("realloc");
		return 1;
	}

	p = ptr;
	//使用

	free(p);
	p = NULL;

	return 0;
}

练习

题目1

void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	*p = 20;
	free(p);
}

如果p的值是NULL,就是对空指针解引用,发生错误

题目2

void test()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		exit(EXIT_FAILURE);
	}
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;
	}
	free(p);
}

当i是10的时候就会越界访问

题目3

void test()
{
	int a = 10;
	int* p = &a;
	free(p);
}

对非动态内存开辟的空间不能使用free

题目4

void test()
{
	int* p = (int*)malloc(100);
	p++;
	free(p);
}

由于p已经不是原先的起始位置,释放的空间不彻底,导致内存泄漏

题目5

void test()
{
	int* p = (int*)malloc(100);
	free(p);
	free(p);
}

对一块动态开辟的内存多次释放是错误的,在第一次释放的时候,空间以及还给操作系统了,free是无权访问操作系统的空间,所以在使用free后,我们可以将指针置空,避免多次释放。

题目6

void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}
int main()
{
	test();
	while (1);
}

这个就是忘记对开辟的内存进行释放,导致内存泄漏。

笔试题

题目1

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

首先str还是NULL,因为形参的改变不会影响实参,如果要改变str需要传入str的地址,对NULL解引用程序发生崩溃,并且对于开辟的内存空间要记得释放

题目2

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}

void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

临时变量p一旦出了GetMemory 函数就被销毁了,str非法访问内存空间。

题目3

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

忘记释放内存,发生内存泄漏

题目4

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

free已经释放了str所指向的内存空间,但是free不会将str置空,后面的代码段内存空间进行了非法访问!

柔性数组

       柔性数组就是数组的大小是未定义的,这种一般出现在结构体上,如果不知道结构体的大小的话,可以看我上上篇的文章《结构体详解》。

柔性数组的创建:

struct S
{
	int i;
	char arr[];
};

特点

C语言之动态内存管理-LMLPHP

使用

当我们要使用结构体是,就需要对这个柔性数组开辟空间:

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

struct S
{
	int i;
	char arr[];
};

int main()
{
	struct S* s1;
	s1 = (struct S*)malloc(sizeof(int) + 10 * sizeof(char));
	return 0;
}

这样子我们就获得了10个字节的arr数组的空间了。

优点

我们在学习链表的时候会定义下面的结构体:

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

struct S
{
	int data;
	struct S* next;
};

int main()
{
	struct S* p;
	p = (struct S*)malloc(sizeof(struct S));
	if (p == NULL)
	{
		perror("malloc p");
		return 1;
	}

	p->next = (struct S*)malloc(sizeof(struct S));
	if (p->next == NULL)
	{
		perror("malloc next");
		return 1;
	}
	//使用

	free(p->next);
	p->next = NULL;
	free(p);
	p = NULL;
	return 0;
}
03-25 20:23