VS编译系统 实用调试技巧
目录
什么是bug?
调试是什么?有多重要?
debug和release的介绍
windows环境调试介绍、
一些调试实例
如何写出(易于调试)的代码
编程常见的错误
什么是bug?
其实bug在英文翻译中有表示臭虫的含义,因为第一次被发现的导致计算机程序错误的是飞蛾,也是第一个计算机程序错误。
调试是什么?有多重要?
写代码就是破案的过程,错误都是有迹可循的。迹象越多就越容易顺藤而上,这就是推理的途径。
发现错误-->调试-->解决错误
2.1调试的基本步骤:
发现程序错误的所在
以隔离、消除等方式对错误进行定位
确定错误产生的原因
提出错误的解决办法
对程序错误予以改正、重新测试
Debug和release的介绍(掌握)

Debug通常称为调试版本,它包含调试信息,并且不做任何优化,便于程序员调试。
Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便于用户很好的使用。
Debug和Release的区别就是Release的速度更快,所占的内存更少,但是Release不能调试,Debug可以进行调试。
windows环境调试介绍
3.1调试环境的准备

在环境中选择debug选项,才能使代码正常调试。
3.2调试快捷键

1.F5 - 开始调试
2.ctrl + F5 开始执行(不调试)
3.F9 设置断点/取消断点
4.F10 逐过程
5.F11 逐语句
使用Fn辅助功能键

F10和F11有什么区别呢?
他们在执行的过程F10是逐过程进行的,F11是逐语句进行的,所以在函数的调用时,F11可以更加细节的进入到函数的内部进行逐语句的调试。
F5是和F9配合使用的
F9是设置断点,F5是开始执行并跳到第一个断点
举例:
光标放到16行,按Fn+F9,我们就在16行加了一个断点,意思是F5开始调试直到断点处停止调试。

按F5开始调试

得到了我们的程序执行结果

在以上的基础上继续按F5开始调试结果会是什么样的呢?

在循环里面的断点,每次循环都会继续停到断点处。
抛开上面我们所说的,依然是下面的程序:

如果循环的次数很大,达到几百上千时,而我们的错误刚好在第400次循环时该怎么办呢?
在VS编译器中我们也可以设置断点。



如果我们不明白条件断点,我们也可以在代码中加上一条条件语句,之后将断点打在条件语句处,如果满足条件则断点被触发。

3.3调试时查看当前程序的信息

如果我们想在调试过程中观察i的值,点击调试->窗口->自动窗口

在我们按F10进行逐过程调试时,自动窗口会自动的显示我们在这个过程中,当前程序的信息。
如果我们想在调试过程中观察程序的局部变量的变化,点击调试->窗口->局部变量

如果我们想在调试过程中监视任何变量,点击调试->窗口->监视


监视不仅可以监视变量的值,也可以监视变量的地址,表达式的值
内存

调用堆栈
就是数据结构里面的栈。

反映数据的调用逻辑。
调试实例
求1!+2!+......+10!
int main()
{int n = 0;int sum = 0;scanf("%d", &n);int ret = 1;for (n = 1; n <= 10; n++){for (int i = 1; i <= n; i++){ret *= i;}sum += ret;}for (int i = 1; i <= n; i++){ret *= i;}printf("%d\n", sum);return 0;
}经过计算,我们发现我们的代码是错误的,错误在于我们每一次循环没有及时修改ret的值为1。
但是如果我们不能一眼看出来错误在哪里,还是要依据调试来解决问题的。
1.在监视窗口中添加我们想要监视的变量

2.F11逐语句并且观察监视窗口中变量的值。

我们在调试的过程中发现ret在执行过程中,继续进行阶乘运算时,初始值不是1,这就是我们通过调试发现的问题所在,经过修改,代码如下:
int main()
{int n = 0;int sum = 0;//scanf("%d", &n);int ret = 1;int i = 0;for (n = 1; n <= 10; n++){ret = 1;for (int i = 1; i <= n; i++){ret *= i;}sum += ret;}for (int i = 1; i <= n; i++){ret *= i;}printf("%d\n", sum);return 0;
}如何写出(易于调试)的代码
代码运行正常
bug很少
效率高
可读性高
可维护性高
注释清晰
文档齐全
常见coding技巧
使用assert
尽量使用const
养成良好的编码风格
添加必要的注释
避免编码的陷阱
示例:模拟实现strcpy函数
void my_strcpy(char* dest, char* scr)
{while (*scr != '\0'){*dest = *scr;dest++;scr++;}*dest = *scr;//'\0‘的拷贝
}
int main()
{char arr1[20] = "hello world";char arr2[40] = { 0 };my_strcpy(arr2, arr1);printf("%s\n", arr2);return 0;
}但是在我们看来这样的代码并不是一个好的代码,我们可以继续将代码进行优化。
my_strcpy函数部分我们可以将它改为以下形式
void my_strcpy(char* dest, char* scr)
{while (*scr != '\0'){*dest++ = *scr++;//dest++;//scr++;}*dest = *scr;//'\0‘的拷贝
}除了上述的代码形式外,我们还可以将代码改为以下形式:
void my_strcpy(char* dest, char* scr)
{while (*dest++ = *scr++){;}}
int main()
{char arr1[20] = "hello world";char arr2[40] = { 0 };my_strcpy(arr2, arr1);printf("%s\n", arr2);return 0;
}但是我们在传递参数的时候不能够保证我们传递过来的指针是否为空指针,也不能保证指针的有效性,所以在这个时候我们就要使用assert断言,来保证指针的有效性。
为什么使用assert断言?
如果在传参的这个过程中,我们真的传递了一个空指针,或者说是一个无效的指针,那么使用断言就会提示我们这样的问题所在,但是如果你没有使用断言来判断指针的有效性,整个程序运行起来的最终结果就会崩掉,并且同时我们也不会知道它为什么会运行失败。
#include <assert.h>
void my_strcpy(char* dest, char* scr)
{//assert(dest != NULL);//assert(scr != NULL);assert(dest && scr);while (*dest++ = *scr++){;}}
int main()
{char arr1[20] = "hello world";char arr2[40] = { 0 };my_strcpy(arr2, arr1);printf("%s\n", arr2);return 0;
}当然断言不仅仅是应用于指针的判断,断言是依据assert后面表达式真假而言的。对于我们程序员来讲是一个很好的编程习惯。
other:
我们在C语言的库中发现strcpy函数和我们自己写的my_strcpy函数有所出入。详细请看下图:

使用const修饰和不使用const又有什么区别呢?
const修饰指针的作用:
const放在*的左面,const int* p或者int const* p,const放在*的左面限制的是*p而不是p。
*p不能改,是p指向的内容;但是p可以改,p可以指向其他变量
int main()
{const int m = 0;int n = 0;//m = 20;errconst int* p = &m;//*p = 20;错误p = &n;//okreturn 0;
}const放在*的右面,int* const p ,如果const放在*右边,限制的是p而不是*p,*p可以改,p不可以改。
int main()
{int m = 0;int n = 0;int* const p = &m;*p = 20;//okp = &n;//noreturn 0;
}在我们strcpy中是如何使用的呢?
如果我们将源头和目的地写反,就出现了一个bug,这个时候使用const修饰char* scr就可以避免这个错误。提高了代码的健壮性。
#include <assert.h>
void my_strcpy(char* dest, const char* scr)//意思是*scr不能被改变
{//assert(dest != NULL);//assert(scr != NULL);assert(dest && scr);while (*dest++ = *scr++){;}}
int main()
{char arr1[20] = "hello world";char arr2[40] = { 0 };my_strcpy(arr2, arr1);printf("%s\n", arr2);return 0;
}
#include <assert.h>
char* my_strcpy(char* dest, const char* scr)//意思是*scr不能被改变
{//strcpy函数返回的是目标空间的起始地址assert(dest && scr);char* ret = dest;//记住起始空间的地址while (*dest++ = *scr++){; }return ret;
}
int main()
{char arr1[20] = "hello world";char arr2[40] = { 0 };//链式访问printf("%s\n", my_strcpy(arr2, arr1));return 0;
}
编程常见的错误
6.1编译型错误
例如:缺少分号等语法错误,可以直接查看错误信息,解决问题。
6.2链接型错误
例如:拼写错误,或者标识符不存在提示有无法解析的外部命令。

主要在代码中找到错误信息的标识符,然后定位问题所在。
ctrl+f--搜索

6.3运行时错误
借助调试,逐步定位问题。
感谢阅读,欢迎大家批评指正!
相关文章:
VS编译系统 实用调试技巧
目录什么是bug?调试是什么?有多重要?debug和release的介绍windows环境调试介绍、一些调试实例如何写出(易于调试)的代码编程常见的错误什么是bug?其实bug在英文翻译中有表示臭虫的含义,因为第一次被发现的导致计算机…...
【华为OD机试模拟题】用 C++ 实现 - GPU 调度(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明GPU 调度题目输入输出示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。...
腾讯前端必会react面试题合集
React-Router的路由有几种模式? React-Router 支持使用 hash(对应 HashRouter)和 browser(对应 BrowserRouter) 两种路由规则, react-router-dom 提供了 BrowserRouter 和 HashRouter 两个组件来实现应用的…...
Linux搭建SVN服务器,并内网穿透实现公网远程访问
文章目录1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6.2 配置…...
C++STL之list的模拟实现
目录 一.list准备 二. iterator迭代器 1._list_iterator 2.begin()、end() 3.const_begin()、const_end() 4.!&& 5. && -- 6.operator* 7.operator-> 三.Modify(修改) 1.insert() 2.erase() 3.push_back() && push_front() 4.pop_bac…...
为什么硬件性能监控很重要
当今的混合网络环境平衡了分布式网络和现代技术的实施。但它们并不缺少一个核心组件:服务器。保持网络正常运行时间归结为监控和管理导致网络停机的因素。极有可能导致性能异常的此类因素之一是硬件。使用硬件监控器监控网络硬件已成为一项关键需求。 硬件监视器是…...
HTTP缓存
HTTP缓存HTTP缓存引发的一个问题HTTP缓存的作用HTTP缓存的分类强制缓存协商缓存(解决强缓存下资源不更新问题)缓存策略HTTP缓存引发的一个问题 有一次在开发移动端H5项目,UI提了几个UI问题,经过样式调试,android上没有…...
SPI设备树处理过程
SPI设备树处理过程 文章目录SPI设备树处理过程参考资料:一、 spi_device结构体二、 SPI设备树格式2.1 SPI Master2.2 SPI Device2.3 设备树示例三、设备树实例3.1 使用GPIO模拟的SPI控制器3.2 IMX6ULL SPI控制器四、 设备树处理过程致谢参考资料: 内核头…...
数据有哪些重要的作用?
我们正处在科技高速发展的时代,如今互联网已经与我们的生活息息相关,我们每天在互联网产生大量的数据,这些数据散落在网络中看似没有怎么作用,但是这些数据经过系统的处理整合起来确实非常有价值的。 一、 发展大数据技术可以提高…...
spring面试题总结
1、spring是什么? spring是一个轻量级IOC和AOP容器框架,是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用的开发,开发者只需要关注业务需求即可: core container 容器组件 spring context,…...
使用MUI与H5+构建移动端app
前言 通过mui构建APP 效果图: <!DOCTYPE html> <html> <head><meta charset...
第17篇:Java变量总结
目录 1.变量的概念 1.1 变量来源 1.2 计算机中的变量 1.3 变量如何在内存中存储 2.Java变量...
使用51单片机的GPIO输出占空比可调节的PWM波
一、前言 在一些单片机或微控制器中,通用GPIO可以被配置为产生PWM信号。PWM即脉冲宽度调制,是一种用于模拟输出的技术。它可以通过改变输出信号的脉冲宽度来控制电路中的电平,从而实现对电路的控制。 二、什么是PWM波? PWM波&a…...
从产品经理的角度如何提升项目的交付质量?
提高交付质量 ,对于每个IT公司都是永恒的话题。 交付质量其实包含2重意义, 一是交付的高质量(客户角度),即客户的满意度;二是高质量的交付(交付团队的角度),这里是指如何…...
JavaScript BOM【快速掌握知识点】
目录 Window对象的常用属性 语法: Window对象的常用方法 语法: open()和close()方法 History对象 常用属性和方法 示例 Location对象 常用属性 常用方法 Document对象的常用方法 定时函数 超时调用:setTimeout() 间歇调用&…...
【算法】哈希表
作者:指针不指南吗 专栏:算法篇 🐾或许会很慢,但是不可以停下来🐾 文章目录1.定义2.优点3.数字哈希3.1拉链法3.2开放寻址法3.3 例题4.字符串哈希1.定义 哈希表(Hash table),是根据键…...
彻底搞懂React-hook链表构建原理
写在前面的小结 每一个 hook 函数都有对应的 hook 对象保存状态信息useContext是唯一一个不需要添加到 hook 链表的 hook 函数只有 useEffect、useLayoutEffect 以及 useImperativeHandle 这三个 hook 具有副作用,在 render 阶段需要给函数组件 fiber 添加对应的副…...
【数据挖掘实战】——应用系统负载分析与容量预测(ARIMA模型)
项目地址:Datamining_project: 数据挖掘实战项目代码 目录 一、背景和挖掘目标 1、问题背景 2、传统方法的不足 2、原始数据 3、挖掘目标 二、分析方法与过程 1、初步分析 2、总体流程 第一步:数据抽取 第二步:探索分析 第三步&a…...
【华为OD机试模拟题】用 C++ 实现 - 九宫格按键输入(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明九宫格按键输入题目输入输出示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高…...
Linux: config: CONFIG_SYN_COOKIES
文章目录 CONFIG_SYN_COOKIESLinux kernel里的超时设置Huawei SBC详细工作机制CONFIG_SYN_COOKIES config SYN_COOKIES,布尔值;是否支持IP:TCP syncookie功能。 详解:一般来说TCP/IP网络不能够阻挡SYN flooding工具。这个工具很容易被利用,而且会导致DOS工具,妨碍其他整…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...
ThreadLocal 源码
ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物,因为每个访问一个线程局部变量的线程(通过其 get 或 set 方法)都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段,这些类希望将…...
