动态内存管理

动态内存函数

malloc和free

void *malloc(size_t size);

这个函数向内存申请了一块连续可用的空间, 并返回指向这块空间的指针

  • 如果开辟成功, 则返回一个指向开辟好的空间
  • 如果开辟失败, 则返回一个NULL指针, 因此malloc的返回值一定要做检查
  • 返回值的类型是void *, 所以malloc函数并不知道开辟空间的类型, 具体在使用的时候由使用者自己决定
  • 如果参数size为0, malloc的行为是标准未定义的
void free(void *ptr);

释放动态内存开辟的空间

  • 如果参数ptr指向的空间不是动态开辟的, 则free函数的行为是未定义的
  • 如果参数ptr数NULL指针, 则函数什么事都不做

calloc

void *calloc(size_t num, size_t size);
  • 函数的功能是为num个大小为size的元素开辟一块空间, 并且把空间的每个字节初始化为0
  • 与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为0

realloc

realloc函数可以做到对动态开辟内存大小的调整

void *realloc(void *ptr, size_t size);
  • ptr是要调整的内存地址
  • size是调整之后新大小
  • 返回值为调整之后的内存起始位置
  • 这个函数调整原内存空间大小的基础上, 还会将原来内存中的数据移动到新的空间
  • realloc在调整空间存在两种情况
    • 原有空间之后又足够大的空间, 要扩展内存就直接原有内存之后直接追加空间, 原来空间的数据不会发生变化
    • 原有空间之后没有足够的空间, 在堆空间上另找一个合适大小的连续空间来使用, 这样函数返回的是一个新的内存地址

常见的动态内存错误

对NULL指针的解引用操作

// 例
void test() {
	int *p = (int *)malloc(INT_MAX/4);
	*p = 20;	// 如果p的值为NULL, 会出现问题
	free(p);
}

对动态开辟空间的越界访问

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

对非动态开辟内存使用free释放

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

使用free释放一块动态开辟内存的一部分

void test() {
	int *p = (int *)malloc(100);
	p++;
	free(p);	// 此时p不再指向起始位置
}

 


我们的征途是星辰大海!