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工具,妨碍其他整…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
