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工具,妨碍其他整…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...

spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...