C语言:实用调试技巧(VS2019)

您所在的位置:网站首页 锣鼓有什么调式的 C语言:实用调试技巧(VS2019)

C语言:实用调试技巧(VS2019)

2024-06-30 15:51:03| 来源: 网络整理| 查看: 265

目录 什么是bug世界上第一个bug 调试是什么?有多重要?我们是如何写代码的?调试是什么?调试的基本步骤 debug和release的介绍windows环境调试介绍学会快捷键调试的时候查看程序当前信息 调试代码的实例如何写出好(易于调试)的代码编程常见的错误 前言:

相信很多小伙伴在初学编程语言时,常常会被自己写出的代码,因各种问题而导致的bug而搞得头痛不已,但想查找错误的时候又是无从下手的的迷茫,今天这篇文章将彻底打消你的疑惑,帮助你快速且准确的锁定你代码中的错误信息点并修改。 同时在文章后半部分也会教大家如何避免bug的产生,以及高质量的写出易于运行和调试的代码。 接下来就让我们一起来看看吧。

什么是bug

这里先科普一个计算机小历史,bug的由来。

世界上第一个bug

1944年世界上第一台计算机马克1号诞生,在世界上第一位女程序员葛丽丝·霍普(Grace Hopper)接手下,顺利改造成马克二号。

1946年的一天,霍普敲代码的时候发现计算机发生了故障,就在马克二号的继电器触点里,找到了一只被夹扁的小飞蛾。 正是这只小虫子卡住了机器的运行。

霍普顺手将飞蛾夹在工作笔记里,而备注的意思是臭虫,正是这一奇怪的称呼,奠定了Bug这个词在计算机世界的地位,bug也变成无数苦逼工程师的噩梦。这就是第一个bug的诞生。

在这里插入图片描述

调试是什么?有多重要?

所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了,如果问心有愧, 就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径。 顺着这条途径顺流而下就是犯罪,逆流而上,就是真相。

一名优秀的程序员是一名出色的侦探。 每一次调试都是尝试破案的过程。

我们是如何写代码的?

在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

拒绝-迷信式调试!!!!

调试是什么?

调试(英语:Debugging / Debug),调试是保证所提供的设备能够正常运行的必须程序。又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。

调试的基本步骤 发现程序错误的存在以隔离、消除等方式对错误进行定位确定错误产生的原因提出纠正错误的解决办法对程序错误予以改正,重新测试 debug和release的介绍

Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。

Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。

对于下列代码debug和release的区别:

#include int main() { int a = 10; printf("%d\n", a); return 0; }

在上述代码所属的项目下分别产生debug和release的应用程序 在这里插入图片描述

在这里插入图片描述

在这里插入图片描述 从上图中两种不同版本的相同代码所产生的应用程序的大小却相差30kb。

而且两种版本的反汇编代码也有差异: debug: 在这里插入图片描述 release: 在这里插入图片描述

release版本对于代码的优化,可用下列代码验证:

#include int main() { int i = 0; int arr[10] = {0}; for(i=0; i int i = 0; for (i = 0; i int i = 0; for (i = 0; i int a = 10; test(); printf("%d ", a); return 0; }

当我们按F10进入调试界面后,再继续按F10,程序运行到test函数时。 在这里插入图片描述

我们按一下F11。 在这里插入图片描述

箭头就会进入到test函数内部,再按F10就可以一行一行的执行代码了。

F5

启动调试,经常用来直接跳到下一个断点处。

F9

创建断点和取消断点 断点的重要作用,可以在程序的任意位置设置断点。 这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。

F5与F9一般是配合来使用的。 示例:

#include int main() { int i = 0; for (i = 0; i int n = 3; int i = 0; int ret = 1; int sum = 0; for (n = 1; n ret *= i; } sum += ret;//阶乘的和 } printf("%d ", sum); return 0; }

大家观察一下上述代码,有没有什么不妥的地方,我们来看看打印结果: 在这里插入图片描述 我们发现结果与正确结果不相同,明明我们求一个数阶乘的代码没有问题啊,可是错误在哪呢?这时就需要使用到调试来进行排错了。 调试: 按F10进入调试界面,打开监视观察各个变量的值。 在这里插入图片描述 在未进入循环时各个值。

在这里插入图片描述

当第一层for循环完,ret 中为1的阶乘,值为1。

在这里插入图片描述 当第二层for循环完,ret 中为2的阶乘,值为2。

在这里插入图片描述 当第三层for循环完,ret 中为3的阶乘,值为12。 此时,ret中的值发生异常,说明程序出错在第三次for循环内部,当我们重新调试时, 在这里插入图片描述 从第二层for循环进入到第三层for循环时,发现ret 的初始值为2,为上一次循环遗留下的值,所以该程序问题在于每次计算一个数的阶乘时,需要把ret的值重新初始化为1。 修改:

#include int main() { int n = 3; int i = 0; int sum = 0; for (n = 1; n ret *= i; } sum += ret;//阶乘的和 } printf("%d ", sum); return 0; }

在这里插入图片描述 这就是一次完整的写代码及调试纠错的过程。在自己编写代码时可以参考步骤。

如何写出好(易于调试)的代码

优秀的代码:

1. 代码运行正常 2. bug很少 3. 效率高 4. 可读性高 5. 可维护性高 6. 注释清晰 7. 文档齐全

常见的coding技巧:

1. 使用assert 2. 尽量使用const 3. 养成良好的编码风格 4. 添加必要的注释 5. 避免编码的陷阱。

示范:

模拟实现库函数:strcpy

这里先了解一下strcpy 函数,在cplusplus.com网站上搜索一下这个函数。 在这里插入图片描述 从函数功能介绍来看,这个函数是将一个字符数组的内容复制给另一个字符数组,直到遇到’\0’后结束。第一个参数数组是被放入字符的数组,第二个参数数组是要复制的数组。 在这里插入图片描述

根据上述描述先实现一下核心功能,这里命名为my_strcpy函数。 在这里插入图片描述

这里可以用while循环把arr1的每一位复制到arr2数组中,最后再给arr2赋值一个’\0’。

#include void my_strcpy(char* dest, char* src) { while (*src) { *dest = *src; dest++; src++; } *dest = '\0'; } int main() { char arr1[] = "abcdefg"; char arr2[10]; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }

在这里插入图片描述

思考:这样写就可以了吗?能否对代码进行再优化一下? 修改:

#include void my_strcpy(char* dest, char* src) { while (*dest++ = *src++)//这样是不是更简洁 { ; } } int main() { char arr1[] = "abcdefg"; char arr2[10]; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }

在这里插入图片描述

思考:这样写就完善了吗?如果函数传参传来的是空指针(野指针)怎么办? 在这里插入图片描述 给函数传空指针(NULL),程序就崩溃了。 为了避免他人在使用my_strcpy函数时万一传空指针,就需要在函数内部判断一下传过来的参数。 修改:

#include void my_strcpy(char* dest, char* src) { if(dest == NULL || src == NULL)//判断是否为空指针 { return; } while (*dest++ = *src++) { ; } } int main() { char arr1[] = "abcdefg"; char arr2[10]; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }

在这里介绍一个库函数assert(断言),可以判断参数的真假。 在这里插入图片描述 在这里插入图片描述

这样就又可以对上述代码进行修改了。

#include #include void my_strcpy(char* dest, char* src) { assert(dest && src); while (*dest++ = *src++) { ; } } int main() { char arr1[] = "abcdefg"; char arr2[10]; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }

在这里插入图片描述

在这里插入图片描述 这样在传参时不小心传入空指针时,程序就能及时的检测和让程序停止下来了。 当完善到这里的时候,就不要以为代码已经很完善了,我们对比一下my_strcpy和strcpy的函数参数和返回值。 在这里插入图片描述 这里my_strcpy函数没有设计返回值,而strcpy函数返回了一个**char*的指针,我们思考一下,把一个源数组的内容复制给目标数组,那返回的地址应该是目标数组的地址才更符合实际。 这个问题解决了,再看一下第二个参数的类型不完全相同,strcpy函数的第二个参数前加了一个const** 。 const是什么意思呢?

const含义:只要一个变量前用const来修饰,就意味着该变量里的数据只能被访问,而不能被修改,也就是意味着“只读”(readonly)。

const规则: const在谁后面谁就不可以修改,const在最前面则将其后移一位;const修饰一个变量时,一定要给这个变量初始化,若不初始化,在后面也不能初始化。

在这里插入图片描述

const作用:

1:可以用来修饰变量,修饰函数参数,修饰函数返回值,且被const修饰的东西,都受到强制保护,可以预防其它代码无意识的进行修改,从而提高了程序的健壮性(是指系统对于规范要求以外的输入能够判断这个输入不符合规范要求,并能有合理的处理方式。ps:即所谓高手写的程序不容易死); 2:使编译器保护那些不希望被修改的参数,防止无意代码的修改,减少bug; 3:增强代码的可读性,给读代码的人传递有用的信息,声明一个参数,是为了告诉用户这个参数的应用目的。

当const 修饰指针时,有两种情况:

const 在*左边 #include int main() { int n = 10; int m = 100; const int* p = &n;//const 和 int 位置可互换 //*p = 20; p = &m; printf("%d\n", *p); return 0; }

在这里插入图片描述 在这里插入图片描述 结论: 当const 在 * 左边,*p不能修改,也就是p指向的内容,不可以通过p来改变了。但是p是可以改变的,p可以指向其他的变量。

const 在*右边 #include int main() { int n = 10; int m = 100; int* const p = &n; *p = 20; //p = &m; printf("%d\n", *p); return 0; }

在这里插入图片描述 在这里插入图片描述

结论: 当const 在 * 右边,p不能修改,也就是p不能再指向其他变量,但是*p可以修改,也就是可以通过解引用p来改变p指向的变量的值。

通过对const有了深入了解,我们再次对my_strcpy函数进行修改:

#include #include char* my_strcpy(char* dest, const char* src) { assert(dest && src); char* ret = dest; while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[] = "abcdefg"; char arr2[10]; printf("%s\n", my_strcpy(arr2, arr1)); return 0; }

在这里插入图片描述 这就是一个完整的对库函数strcpy的模拟实现,你以后设计函数时,也可以根据我的思路,进行思考实现。

练习:

模拟实现一个strlen函数

这里给大家一个相似的练习,可以参照my_strcpy函数的思路来编写代码,后面还放上了参考代码以供大家对照。 参考代码:

#include #include int my_strlen(const char* str) { int count = 0; assert(str != NULL); while (*str)//判断字符串是否结束 { count++; str++; } return count; } int main() { const char* p = "abcdef"; //测试 int len = my_strlen(p); printf("len = %d\n", len); return 0; } 编程常见的错误

编译型错误

直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。

如代码的拼写错误,少写了分号,少打空格、括号之类的。 在这里插入图片描述

链接型错误

看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不存在或者拼写错误。

就像这样: 在这里插入图片描述 这么写会把读代码的人气死吧,hhh。

运行时错误

借助调试,逐步定位问题。最难搞。

本篇关于实用调试技巧的讲解就到此结束了,感兴趣的的小伙伴点点赞,点点关注,谢谢大家的阅读哦!!! 几千字长文写下来博主也不容易,点点赞吧!😘

下一篇将会进入C语言进阶的学习,会对浮点数在内存中的存储方式进行详细介绍,点点关注,后期不错过哦。😘 你们的鼓励就是我的动力,欢迎下次继续阅读!!!😘😘😘



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭