百度面试题--动态内存分配、值传递、返回栈空间地址问题-C语言

一、我们来看一道百度面试题:


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;
}

问:这串代码存在那些问题?程序最终的输出结果是什么?

1.首先,我们由main函数调用进入test(void)函数内部
char* str = NULL;//创建一级指针变量str,str变量内部存放空指针NULL且无法进行解引用操作

在这里插入图片描述

2.GetMemory(str);
str以值传递形式传给GetMemory(char *p),p相当于str的一份临时拷贝,
p里面存放的是str变量的值NULL

在这里插入图片描述

在这里插入图片描述
3.1接下来进入GetMemory函数内部,一开始p==NULL,malloc函数在堆上开辟一块内存空间并将空间地址赋给指针p来维护,我们假设地址为0x0012ff40
3.2GetMemory()函数调用完毕,内部形参变量p销毁,不再记录开辟的内存块地址,此时str仍然存放空指针NULL,并没有指向一块有用的空间

strcpy(str,"Hello World");
这里给strcpy函数传入空指针NULLs,程序崩溃,无法运行(访问非法内存)
在这里插入图片描述

总结:

  1. 运行代码会出现崩溃现象
  2. 程序存在内存泄漏问题
    str以值传递形式给了GetMemory函数的形参p
    GetMemory函数返回之后开辟的动态内存空间尚未释放且无法找到

改进:

void GetMemory(char**p) {//用二级指针接收str地址
	*p = (char*)malloc(100);
}//p销毁,str记录开辟空间地址
void test(void) {
	char* str = NULL;
	GetMemory(&str);//传str指针
	strcpy(str,"Hello World");
	printf(str);
	if (str != NULL) {
		free(str);//释放内存
		str == NULL;
	}
	else {
		return;
	}
}
int main() {
	test();
	return 0;
}

二、几个经典的面试题

题目2:返回栈空间地址问题

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

请问:Test()函数的输出结果是什么?
我的答案:程序崩溃,GetMemory()执行完毕p会被销毁,str为空指针(错了)
正确答案:输出一个随机值在这里插入图片描述
在这里插入图片描述

GetMemory()函数内部在栈上开辟一块内存空间存放字符数组 hello world,p为内存空间地址并返回给str(这里返回的是一个栈空间的地址),str成功存储这块空间地址,GetMemory()调用完毕生命周期结束,形参p销毁并将开辟的空间还给操作系统,此时仍然可以通过str找到这块内存,但这块空间内存放的是我们不清楚的一个随机值

类似的,

void* test() {
	//static int a = 10;//静态区地址返回--无error
	int a = 10;//栈区地址返回
	return &a;
}
int main() {	
	int *p= test();
	//a销毁前将栈区地址返回给p,test()调用完生命周期结束并将这块空间还给操作系统
	//p指向这块空间但属于非法访问(野指针)
	*p = 20;//error
	return 0;
}
void* test() {
	int* p = malloc(100);//堆区地址返回
	return p;
}
int main() {
	int* ptr = test();
	//p销毁前将开辟空间地址返回给ptr,ptr可以找到这块空间并且这块空间没有被还给操作系统,堆空间只有free后才会被回收
	return 0;
}

题目3:

void GetMemory(char** p, int num) {
	*p = malloc(num);//堆区地址返回
}
void Test() {
	char* str = NULL;
	GetMemory(&str, 100);//地址传递--str记录堆上开辟内存块地址
	strcpy(str, "hello");
	printf(str);
}
int main() {
	Test();
	return 0;
}

请问Test()函数运行后会有什么结果?
我的答案:hello

题目4:

void Test(void) {
	char* str = (char*)malloc(100);//str维护堆上动态开辟的一个100个字节的一块内存空间
	strcpy(str, "hello");//使用
	free(str);//free释放str指向的空间后,并不会把str置为空指针NULL,str成为野指针
	//str=NULL;//改错
	if (str != NULL) {//异常--非法访问野指针
		strcpy(str, "world");
		printf(str);
	}
}
int main() {
	Test();
	return 0;
}
//篡改动态内存区的内容,后果难以预测,非常危险

请问Test()函数运行后会有什么结果?
我的答案:输出hello,然后程序异常–访问野指针

三、C/C++程序的内存开辟

在这里插入图片描述
C/C++程序内存分配的几个区域:

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

有了这幅图,我们就可以更好的理解在《C语言初识》中讲的static关键字修饰局部变量的例子了。
实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁
所以生命周期变长。

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值