关于C语言函数返回char *的踩坑记录(堆、栈)

今天在教学妹C语言的时候,重新写了一道题目,在没有任何准备的情况下,毫不意外地翻车了。
翻车的点是关于函数返回char *类型时,并不能够如期得到期望的结果。

发现问题

题目:
写一个函数fun,实现将整型数int转化成字符串char * 。要求使用函数递归。
顺带一提,本文暂时认为这个整数是大于等于1 的,并且长度不固定。

功能上并没有复杂性,但是因为一定要使用递归,就一定涉及char * 返回值类型的函数。

先说一下一开始的思路:
一位一位地转换。然后拼凑起来变成一个完整的字符串。
如:fun(1234) -> fun(123) +“4” -> fun(12) + “34” ->fun(1) +“234” -> “1234” -> return
如果fun其中的加号可以理解成strcat,这要求前后两个都是字符串。

明确了这种思路以后,代码很容易就可以写好:

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

char* num_to_ascii(int n) {
	// 1 -> "1\0";  0 -> "0->"0\0"
	// assert n<= 9 
	char c[2];
	c[0] = n+48;
	c[1] = '\0';
	return &c[0];
}

char* fun(int num) {
	if (num <= 9) {
		return num_to_ascii(num);
	}
	char* s1 = fun(num / 10);
	char* s2 = num_to_ascii(num % 10);

	return strcat(s1,s2);
}

int main() {
	int num = 789;
	printf("%s", fun(num));
	return 0;
}

解释一下,这里面因为想用string.h中的库函数strcat,所以特意把num_to_ascii函数的返回类型从char变成了char*,说实话,这非常不符合我代码方面的审美。但是自己写一个char *和char的合并又显得有些愚蠢,折衷后写成了现在的样子。
现在编译运行一下:
在这里插入图片描述
结果错误了,而且莫名其妙。重新编译运行一下:
在这里插入图片描述
同一段代码,用一个编译器编译运行后,出现不同的结果。

分析问题

毫无疑问,一定是哪里出了问题,代码似乎没有什么随机元素,为什么输出结果会不一样呢?。

但是bug为什么会产生呢?经过仔细地排查,原来是num_to_ascii函数有问题。
可以写一个愚蠢的小代码测试一下:

#include<stdio.h>

char* fun1() {
	char c[2];
	c[0] = '5';
	c[1] = '\0';
	return &c[0];
}

int main() {
	printf("%s", fun1());
	return 0;
}


输出结果会是‘5’嘛?很显然不是。
为什么会出现这样的结果呢?
先看看下面这个小幅度修改的代码吧:

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

char* fun1() {
	char* c = (char*)malloc(2 * sizeof(char));
	c[0] = '5';
	c[1] = '\0';
	return c;
}

int main() {
	char* p = NULL;
	printf("%s", p = fun1());
	free(p);
	return 0;
}


这个代码为什么又是能够输出正确结果呢?

当函数返回作为数组名的c之后,临时字符数组的空间就会被释放。之后又重新调用了新的函数printf,导致最终根据返回指针所输出的结果变成了刷新后的值,而这个值几乎可以认为是随机的。

更加专业地说,num_to_ascii中的常量c是建立在栈上的,在函数结束后就会被释放。如果我们希望保留其中的字符串,应该把它的变量建立在堆上,从而实现手动管理变量寿命。

解决问题

实际上,这种问题可以通过静态变量申明或者malloc来解决。

我们使用index记录当前循环的次数。然后把静态数组对应的位置用相同的赋值转换。
解决过后,实际代码如下:

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

char num_to_ascii(int n) {
	// 1 -> "1";  0 -> "0->"0"
	// assert input n <= 9 
	char c= n+ '0';
	return c;
}

char* fun(int num) {
	static char s[100];
	static int index = 0;

	if (num / 10 != 0) {
		fun(num / 10);
	}
	s[index++] = num_to_ascii(num%10);
	s[index] = '\0';

	return s;
}

int main() {
	int num = 123456;
	printf("%s", fun(num));
	return 0;
}

经过测试时可以正确运行的。
在这里插入图片描述
通过这种写法,也回避掉了因为想使用strcat函数而不得不把num_to_ascii函数的尴尬。

至此,问题已经全部解决了。

结论

堆和栈,这块一定要弄清楚!大厂面试最喜欢追着这两点问了!
掌握得越熟练,调试这种bug就越快!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邵政道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值