c语言学习笔记-调试的艺术以及const在指针上的应用

目录

前言

一、从阶乘累和入手

二、越界访问的函数

 三、const在指针上的应用

总结


前言

在学习c语言的过程中,我发现了调试的重要性,也愈发感觉到在Visual Studio 下功能的强大,它能够让我们更好的找到出错的地方,并且也能够对代码进行进一步的优化,接下来,跟我一起来见识一下调试的魅力吧!(使用工具-Visual Studio 2022)

一、从阶乘累和入手

首先来上手一个1.0版本的代码:

//阶乘累和算法 1.0版本
int factorial_Tiredand(int n)
{
	int sum = 0;
	int ret = 1;
	for (int i = 0; i < n; i++)
	{
		for (int j = 1; j <= i + 1; j++)
		{
			ret *= j;
		}
		sum += ret;
	}
	return sum;
}

 从这里单纯的看的话其实很慢才会发现问题,所以在代码打出来的时候就测试几个自己能够口算的数来验算一下,看看是否正确。

 我们使用了3做测试,3!+2!+1!=9,可是实际上程序给我们的答案是15,错误了,与答案相差6,这个时候我们应该怎么做呢?这个时候不应该和程序死磕,死看代码,这个时候就要灵活的使用调试这个功能,ctrl+F10 F11 F5.....

 随后使用ctrl加F10打开调试,打开窗口中的监视,将局部变量名输入进去准备观察它们的变化,输入3后F11跳转至我们这个累和函数准备进行第一次循环:

 第一次十分正常,1!正确,累加也正确,继续

 这一次也十分正确:2!=2,累加之后1!+2! = 3。

 但是其实此时已经发现了ret似乎执行上一次之后没有发生变化,并没有还原成1,而调试是指向下次循环的,那么就有可能出了问题,继续。

果然出了问题,证明了我们的想法,3!=6才对,而此时是12,明显多乘了一个2,而这2的来源正是上次ret留存下来的2,所以,通过调试,我们就发现了ret每次算完一个数的阶乘之后没有回到初始位置而是继续乘了下去,所以经过修改我们就可以的得到2.0的版本。

//阶乘累和算法 2.0版本
int factorial_Tiredand(int n)
{
	int sum = 0;
	for (int i = 0; i < n; i++)
	{
		int ret = 1;
		for (int j = 1; j <= i + 1; j++)
		{
			ret *= j;
		}
		sum += ret;
	}
	return sum;
}

 我们将ret的创建直接放在累和的循环中,这样就能保证每次累加到下一个阶乘时会是从初始值1开始累乘的。

相反的,我们就可以思考得到另一个问题:既然是累乘之后相加,并且每次都是乘以上一次的结果,比如3!=2!*3,4!=3!*4,在算阶乘的时候同时相加岂不更妙?

于是乎,全新的3.0版本问世:

//阶乘累和算法 3.0版本
int factorial_Tiredand(int n)
{
	int sum = 0;
	int ret = 1;
	for (int i = 1; i <= n; i++)
	{
		ret *= i;
		sum += ret;
	}
	return sum;
}

相比前两个版本,没有出现误算,并且只用了一个循环,代码简洁明了,这正是状态十分的代码!健壮性强大! 

二、越界访问的函数

int main()
{
	int i = 0;
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	for (i = 0; i <= 12; i++)
	{
		arr[i] = 0;
		printf("hh\n");
	}
	return 0;
}

 首先,你来猜猜在Visual Studio 2022 x86环境下会是怎么样的一种情况呢?

测试一下,会发现竟然是无限循环啊!这是怎么一回事呢?你单单看代码能够将它给看破吗?看不破,你需要用到调试

如果不考虑越界访问的话(本身其实会报越界访问的错误的),乍一眼看hh应该被打印13次的,可是运行结果是无限循环,我们可以先想想其中的原因:

这个循环是由i的值来确定的,既然无限循环,证明了i永远不能超过12,可是i在++,除了for循环中的i++能控制i的值外,还有什么地方能够控制i的值呢?那么,只有一个地方了,就是循环体内的arr[i]出了问题,因为一直它就是被赋给0的,于是乎,我们调试一番便就可以发现问题的所在:

在调试到12之前都没有问题,知道当i = 12的时候,我们发现:

 

进行下一步之后:

 i和arr[12]同时变成0了,果然是这里出了毛病!,我们取地址一看:

 果然i和arr[12]是住在一间房子里面的(doge)!怪不得每次i到达12都被arr拖回0了,永远也到达不了13的那个结束循环的时候。

上面这种情况视环境而变:这里出现的情况就是在创建局部变量时,在栈区先创建高地址的空间,也就是i,随后创建低地址的空间arr,然后每次读取arr地址都会前进一步,而纯巧合的是arr[12]的空间刚好和i的空间重合了,所以才会出现这种情况,图解一下或许会更加清楚:

 三、const在指针上的应用

 const是可修饰常变量的,不可被改哦。
*注意const int* p只限制*p,不限制p,表示p指向的对象不能通过p来进行改变。但是p变量中的地址是可以改变的。
int *const p=这里就只限制p,不限制*p了。表示p的内容不能被改变,但是p指向的对象可以通过p来改变。
左定值右定向

总结

总之,调试这个功能是非常强大的,在找程序错误的时候有奇效!小萌新一枚,一起加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值