栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言进阶(十三) - 动态内存管理例题分析

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言进阶(十三) - 动态内存管理例题分析

目录
  • 前言
  • 1.
    • 结果分析
    • 改正或改进
  • 2.
    • 结果分析
    • 改进或改正
  • 3.
    • 结果分析
  • 4.
    • 结果分析
    • 改进或改正
  • 结语

前言

本节主要分析几个例题,以此来稍微加深对动态内存管理的认识。


1.
#include 
#include 

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

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

int main() {
	Test();
	return 0;
}

结果分析

main()函数调用Test()函数,之后Test()函数调用GetMemory()函数。

字符指针str传入了函数GetMemory(),GetMemory()函数的参数也是字符指针,故字符指针str实际上是传值调用,传的是str的值。相当于在内存的栈区创建了以个字符指针p接收了字符指针str的值NULL。

在GetMemoey函数内部,指针p指向了malloc()函数开辟的100个字节的空间的起始位置。p中存放的是该内存空间的起始地址。之后GetMemory函数调用结束,并且没有返回值,指针p由于是在GetMemory()函数内部,是局部变量,所以p其所在的内存块就随着函数调用结束而销毁了。此时p所指向的空间还未被释放free(),并且由于函数GetMemory()调用已经结束了没有任何一个指针指向这块开辟的空间,属于内存泄漏的情况。

回到Test()函数内部继续执行程序,此时由于指针p是指针str的一份临时拷贝,指针p发生什么都不会影响指针str的值,故字符指针str的值还是NULL。接着函数strcpy()执行操作:把字符串"hello world"拷贝到指针str所指向的空间。但是str的值是NULL,指针str就是一个空指针,没有明确的指向,拷贝会发生错误导致程序停止。

改正或改进
#include 
#include 
//返回动态开辟的空间的起始地址
char* GetMemory(){
	char* p = (char*)malloc(100);
	return p;
}

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

int main() {
	Test();
	return 0;
}

运行结果:


2.
#include 
#include 

char* GetMemory(void) {
    char p[] = "hello world";
    return p;
}
void Test(void) {
    char* str = NULL;
    str = GetMemory();
    printf(str);
}
int main() {
    Test();

	return 0;
}

运行结果:

结果分析

main()函数调用Test()函数,之后Test()函数调用GetMemory()函数。

在GetMemory()函数内部,p是局部字符数组名,该数组存放了一个字符串"hello world",GetMemory()函数返回了局部字符数组名,也就是数组首元素的地址。

在GetMemory函数结束调用之后其内部所有数据都被销毁,包括局部字符数组,其所在内存区域被操作系统回收了,Test()函数内字符指针str接受的是已经被回收内存区域的地址,故str是野指针,指向了不属于本程序的已经被回收的内存区域。此时这个内存区域存放的是什么我们并不知道,输出的内容当然也是未知的。

改进或改正

定义函数时可以返回局部变量的值,这个值可以被外部变量接收;不要返回局部变量的地址,这没有意义。还会导致接受这个地址的指针成为野指针。

#include 

int* test() {
    int a = 20;
    return &a;
}
int main() {
    int* p = test();
    //printf("%dn", 10);
    printf("%dn", *p);

    return 0;
}

运行结果:

#include 

int* test() {
    int a = 20;
    return &a;
}
int main() {
    int* p = test();
    printf("%dn", 10);
    printf("%dn", *p);

    return 0;
}

运行结果:

每一次调用函数都会开辟函数栈帧,函数调用完成返回后栈帧被操作系统回收。回收之后,开辟且已经被回收的空间里存放的值是否还是原来的值是未知的,如果又开辟了新的函数栈帧,那么会覆盖原来的已被回收的(旧的)函数栈帧。


3.
#include 
#include 

void GetMemory(char** p, int num){
	*p = (char*)malloc(num);
}

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

运行结果:

结果分析

main()函数调用Test()函数,之后Test()函数调用GetMemory()函数。

Test()函数中字符指针str的地址和整型100传入了GetMemory()函数。GetMemory()函数分别形参用二级指针p和整型num正确接收了。之后在GetMemory()函数内部,二级指针p解引用一次找到了Test()函数内的一级指针str,相当于一级指针str接收malloc()分配的内存空间的起始地址。之后GetMemory()函数调用结束回到Test()函数。

指针str中存放的是动态开辟的内存的起始地址。strcpy()函数把字符串"hello"拷贝到指针str所指向的空间。printf()函数打印指针str所指向的字符串。之后Text()函数调用结束并返回到main()。之后main()函数执行结束并返回0,程序结束。

4.
#include 
#include 

void Test(void){
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
    //free之后直接没有把指针str置为空指针,str就是野指针。
    //直接使用野指针会出问题。
	free(str);
    
	if (str != NULL){
		strcpy(str, "world");
		printf(str);
	}
}

int main() {
	Test();

	return 0;
}
结果分析

main()函数调用Text()函数。

Test()函数内部,指针str指向了malloc()分配的内存空间的起始地址。但是也会有malloc()分配内存失败的情况,此时str存放的是NULL。应该要有对str是否是NULL的检查。

strcpy()函数把字符串"hello"拷贝复制到指针str所指向的内存空间,之后free()函数释放掉指针str所指向的动态分配的内存空间。注意之后的指针str没有被置为NULL,指针str还是指向了已经被释放掉的那片内存区域的起始地址。这时指针str就是一个野指针。很危险。

if判断str是否是空指针,我们已经直到str不是空指针,指向了已经不属于本程序的内存的地址。判断成立执行以下操作:strcpy()函数将会把字符串"world"拷贝复制到指针str所指向的空间,这个操作属于非法访问内存,程序虽然还能够把字符串输出到控制台,但这个程序也是有着问题的。

改进或改正
#include 
#include 

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

int main() {
	Test();

	return 0;
}

结语

有关动态内存管理中代码可能会出现显而易见的错误,这当然很好改正。比如对接收malloc()、calloc()、realloc()的空间的起始地址的指针进行有效性检查、释放指针所指向的动态开辟的内存空间后及时把指针置为NULL等。也有写逻辑上的漏洞并不好看出来,需要我们认真思考和寻找。


END

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1019217.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号