目录
前言
一、从阶乘累和入手
二、越界访问的函数
?三、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来改变。 左定值右定向。
总结
总之,调试这个功能是非常强大的,在找程序错误的时候有奇效!小萌新一枚,一起加油!
|