实用调试技巧【下篇】
🔴本文章是在 Visual Studio 2022(VS2022)编译环境下进行操作讲解
文章目录
- 3.2.调试的时候查看程序当前信息
- 3.2.1.查看临时变量的值
- 3.2.2.查看内存信息
- 3.2.3.查看调用堆栈
- 3.2.4.查看汇编信息
- 🥳4.调试实例
- 🥳5.如何写出(易于调试)的代码
- 5.1.模拟实现库函数: strcpy
- 5.2. const 修饰指针
- 🥳6.编程常见的错误

3.2.调试的时候查看程序当前信息
3.2.1.查看临时变量的值
🔴自动窗口
👉调试–>窗口–>自动窗口


它会出现一个自动窗口,它会自动捕获变量的值,让你监视,方便的我们不用手动添加变量,但是如果它不想让你看的时候过了一会就会自动取消了,我们就监视不到了
🔴局部变量
👉在自动窗口的下面

与自动窗口比较相似,但不同的是放的是局部变量,监视的是程序执行中的局部变量
🔴监视
👉调试–>窗口–>监视

这才是我们用到最多的调试功能

我们想监视谁,就输入谁,它不会自己取消,会一直显示,来为我们提供对变量的监视


数组也可以观察,变量的地址也都可以观察到
如果有需要,可以同时打开4个监视窗口,每个窗口都可以拖动,放到你想放的位置
3.2.2.查看内存信息
🔴内存
👉调试–>窗口–>内存


☝️内存中本放的是二进制的数据,但是为了展示方便,所以是以十六进制来显示的☝️
3.2.3.查看调用堆栈
🔴调用堆栈
👉调试–>窗口–>调用堆栈

👇简单知道一下 什么是栈👇

👇看这段代码👇

👇调用堆栈👇







☝️反映的是函数的调用逻辑☝️
3.2.4.查看汇编信息
🔴反汇编
👉在内存的下面

👇看到的是c语言代码翻译出来的汇编代码👇

🥳4.调试实例
👇看下面这段代码👇
#include<stdio.h>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("hehe\n");}return 0;
}
运行起来是什么样呢?

数组已经越界了,为什么没有崩溃而是死循环了呢?
👇进入调试👇

i 等于9的时候一切都正常,我们继续👇

这个代码真的是在越界访问!胆大包天啊,如果我们继续下一步呢👇

继续一直执行

再改

i 到12之后并没有++变成13, i 再一直跟着 arr[12] 改变,陷入了死循环,这是为什么呢?有没有可能它们是在同一个空间呢?我们看一下👇

地址居然一摸一样,它俩在一个空间😕
那么底层原理什么呢?为什么会这样呢?👇
🔴原理:
1. i 和 arr 是局部变量,局部变量是放在栈区上的
2. 栈区内存的使用习惯是 使用高地址的空间,再使用低地址处的空间
3. 数组随着下标的增长,地址是由低到高变化的

🥳5.如何写出(易于调试)的代码
🥰优秀的代码:
🙌 1. 代码运行正常
🙌 2. bug很少
🙌 3. 效率高
🙌 4. 可读性高
🙌 5. 可维护性高
🙌 6. 注释清晰
🙌 7. 文档齐全
🥰常见的coding技巧:
🙌 1. 使用assert
🙌 2.尽量使用const
🙌 3. 养成良好的编码风格
🙌 4. 添加必要的注释
🙌 5. 避免编码的陷阱
5.1.模拟实现库函数: strcpy

拷贝一个字符串
示例👇
#include<stdio.h>
#include<string.h>int main()
{char arr1[] = "hello world";char arr2[20] = { 0 };strcpy(arr2, arr1);printf("%s\n", arr2);return 0;
}

👇模拟实现 strcpy👇
#include<stdio.h>
#include<string.h>
void my_strcpy(char* dest, char* src)
{while (*src != 0){*dest = *src;dest++;src++;}*dest = *src; // \0的拷贝
}
int main()
{char arr1[] = "hello world";char arr2[20] = { 0 };my_strcpy(arr2, arr1);printf("%s\n", arr2);return 0;
}
如果写成这样的代码,这算一个好的代码吗?当然不算一个好的代码,我们可以优化一下👇
void my_strcpy(char* dest, char* src)
{while (*src != 0){*dest++ = *src++;}*dest = *src; // \0的拷贝
}
我们还可以再融合一下👇
void my_strcpy(char* dest, char* src)
{while (*dest++ = *src++){;}
}
可以直接一次搞定
这样改进完好像已经足够好了,但是我们还能再改进👇
void my_strcpy(char* dest, char* src)
{if (dest == NULL || src == NULL){return;}while (*dest++ = *src++){;}
}
防止遇到空指针,但是这样处理遇到问题只是回避掉了,并不做处理👇
#include<stdio.h>
#include<string.h>
#include<assert.h>
void my_strcpy(char* dest, char* src)
{//断言assert(dest != NULL);assert(src != NULL);while (*dest++ = *src++){;}
}
int main()
{char arr1[] = "hello world";char arr2[20] = { 0 };my_strcpy(arr2, NULL);printf("%s\n", arr2);return 0;
}

使用断言处理之后,如果有问题它就会报错,把问题抛出来并且告诉你出错在哪里,如果不使用断言执行程序的话代码就崩掉了,不会告诉你哪里有问题👇

🚨断言是对程序员非常友好的东西,我们使用断言是个很好的编程习惯
🚨使用断言别忘记引用头文件 <assert.h>
void my_strcpy(char* dest, char* src)
{//断言assert(dest && src);while (*dest++ = *src++){;}
}
简便一点可以直接写成这样☝️
5.2. const 修饰指针

const修饰变量m之后,m的值就更改不了了
但是可以使用一些小聪明把m改掉👇
int main()
{const int m = 10;//m = 20;//errorint* p = &m;*p = 20;printf("%d\n", *p);return 0;
}

这个代码非常奇葩,因为 const 只是在语法层面限制了m不能改,但是通过地址是可以更改的
我们可以在语法层面把指针p 也进行限制👇

int main(){int n = 100;const int m = 10;//m = 20;//error//const 修饰指针//1. const放在*的左边,*p不能改了,也就是p指向的内容,不能通过p来改变了。但是p是可以改变的,p可以指向其他变量const int* p = &m;p = &n;printf("%d\n", *p);return 0;}

int main(){int n = 100;const int m = 10;//m = 20;//error//const 修饰指针//2. const放在*的右边,限制的是p,p不能改变,但是p指向的内容*p,是可以通过p来改变的int* const p = &m;*p = n;printf("%d\n", *p);return 0;}

🤜 1. const放在的左边,p不能改了,也就是p指向的内容,不能通过p来改变了。但是p是可以改变的,p可以指向其他变量
🤜2. const放在的右边,限制的是p,p不能改变,但是p指向的内容p,是可以通过p来改变的
void my_strcpy(char* dest,const char* src)
{//断言assert(dest && src);while (*dest++ = *src++){;}
}
所以上面模拟实现strcpy的代码这样写就更严谨了,加上了 const 来修饰char* src
🥰提高了代码的健壮性(鲁棒性)
因为如果下面的 *dest++ = *src++ 位置要是不小心写反了程序就会报错,所以这样写才是有意义的!
我们再来进行最后一次优化👇
#include<stdio.h>
#include<string.h>
#include<assert.h>//strcpy函数返回的是目标空间的起始位置
char* my_strcpy(char* dest,const char* src)
{//断言-- 保证指针的有效性assert(dest && src);char* ret = dest;//把src指向的字符串拷贝到dest指向的数组空间,包括\0字符while (*dest++ = *src++){;}return ret;
}
int main()
{char arr1[] = "hello world";char arr2[20] = { 0 };//链式访问printf("%s\n", my_strcpy(arr2, arr1));return 0;
}
🥰这些都是一些技巧,希望大家可以理解🥰
🥳6.编程常见的错误
🥰优秀的代码:
🔴1. 编译型错误
👉直接看错误提示信息 解决问题,或者凭借经验就可以搞定,相对简单
🔴2. 链接型错误
👉看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不存在或者拼写错误(ctrl + F 可以进行搜索)
🔴3. 运行时错误
👉借助调试,逐步定位问题,最难搞
🚨做个用心的人!积累排错经验!
总结🥰
本文章是在 Visual Studio 2022(VS2022)编译环境下进行操作讲解
以上就是调试技巧下篇内容啦🥳🥳🥳🥳
希望我们可以做一个用心的人💕💕💕
小的会继续学习,继续努力带来更好的作品😊😊😊
创作写文不易,还多请各位大佬uu们多多支持哦🥰🥰🥰
相关文章:
实用调试技巧【下篇】
🔴本文章是在 Visual Studio 2022(VS2022)编译环境下进行操作讲解 文章目录3.2.调试的时候查看程序当前信息3.2.1.查看临时变量的值3.2.2.查看内存信息3.2.3.查看调用堆栈3.2.4.查看汇编信息🥳4.调试实例🥳5.如何写出&…...
【数据结构期末例题】
前言 本文是博主自己在准备学校数据结构考试时的总结,各个知识点都贴有对应的详细讲解文章以供大家参考;当然文中还有许许多多的截图,这些是博主对主要内容的摘取,对于那些基础较好的同学可以直接看截图,减少跳转对应文…...
管理物理和快照备数据库(Physical and Snapshot Standby Databases)
1.打开物理备数据库 物理备数据库可以打开做只读访问,用于从主数据库卸载查询负载。 如果已经购买Oracle Active Data Guard选项的授权,当数据库打开时Redo Apply可以是激活的,因此允许查询返回与从主数据库返回的完全相同的结果…...
双目立体视觉:SAD算法
算法原理SAD(Sum of absolute differences)是一种图像匹配算法。基本思想:差的绝对值之和。此算法常用于图像块匹配,将每个像素对应数值之差的绝对值求和,据此评估两个图像块的相似度。该算法快速、但并不精确,通常用于多级处理的…...
海外问卷调查答题技巧,纯干货分享,新手小白看过来
海外问卷调查为什么别人赚得盆满钵满而我却连通过都不行?是不是经常有人发出这种疑问,东哥作为一个结交过很多做问卷调查行业的跨境人士,也了解到很多做这一行的去答题的时候都是掌握一定技巧的,而不是去乱答。今天东哥就来说说国…...
【NGINX入门指北】Nginx Web 架构实验
Nginx Web 架构实验 文章目录Nginx Web 架构实验一、动态网站结构二、LNMP 动态网站环境部署三、fastcgi & php-fpm:四、php-fpm初始化配置五、Nginx Location、六、Nginx Rewrite七、CA&HTTPS八、Nginx 的平滑升级一、动态网站结构 资源 资源文件识别——…...
rtt-nano移植
nano其他功能移植 添加finsh组件打开宏实现rt_hw_console_getchar函数添加finsh组件到工程总结问题1. 移植到stm32G0过程中出现Undefined symbol rt_hw_interrupt_disable (referred from clock.o)??2. “implict declaration of function ‘ ‘ is invalid in c99??3. 关于…...
cnn+transformer
好的,下面是使用 Transformer 加 CNN 实现语义分割的代码,使用的数据集是 Semantic Segmentation Drone Dataset。 首先,我们需要导入必要的 Python 库和模块。我们将使用 PyTorch 深度学习框架来实现模型: #python import torch import torch.nn as nn import torch.nn.fu…...
Python fileinput模块:逐行读取多个文件
前面章节中,我们学会了使用 open() 和 read()(或者 readline()、readlines() )组合,来读取单个文件中的数据。但在某些场景中,可能需要读取多个文件的数据,这种情况下,再使用这个组合࿰…...
Vue3路由传参
vue3路由和vue2差别不是很大,不过在传参形式上略有改变 在Vue3中使用路由必须引入 useRouter 和 useRoute import { useRoute, useRouter } from vue-routerconst Router useRouter() //跳转const Route useRoute() //获取到值 同Vue2一样,query使用p…...
用户管理——认证功能JWT和Session
目录用户认证功能的技术选型JWT和Session的区别基于JWT和Session的认证流程基于JWT的认证流程基于Session的认证流程基于JWT和Session的认证的优缺点基于JWT和Session的认证的安全性基于JWT和Session的认证的性能分析基于JWT的一次性和无法废弃基于JWT和Session的认证的续签选择…...
hashlib — 加密哈希算法
hashlib — 加密哈希算法 1.概述 加密可以保护消息的安全,以便验证它们的准确性并且使它们受保护不被拦截。 Python 的加密方式支持包括利用像 MD5 和 SHA 这样的标准算法对消息内容产生签名的 hashlib 和验证消息没有在传输过程中被改变的 hmac hashlib 哈希库模…...
四喜临门选股预警源码指标
{四喜临门选股预警} AP1:CROSS(MA(C,5),MA(C,10)); RSV:(CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9))*100; K:SMA(RSV,3,1); D:SMA(K,3,1); AP2:CROSS(K,D); DIFF:EMA(CLOSE,12) - EMA(CLOSE,26); DEA:EMA(DIFF,9); AP3:CROSS(DIFF,DEA); AP4:CROSS(MA(V,5),MA(V,10)); GYTJ1:…...
Kotlin新手教程五(扩展)
一、扩展 在Kotlin中可以给一个类添加一个新的方法而不用继承该类或者使用设计模式,这样的方法称为扩展。 1.扩展函数 声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。 下面代码为 MutableList 添加一个swap 函数&am…...
QT入门Containers之Widget、Frame
目录 一、QWidget界面相关 1、布局介绍 2、基本界面属性 3、特殊属性 二、QFrame 三、Demo展示 此文为作者原创,创作不易,转载请标明出处! 一、QWidget界面相关 1、布局介绍 为什么将QWidget容器放在第一个,因为目前使用过…...
数据结构与算法基础-学习-12-线性表之顺序队
一、个人理解队列是线性表的衍生之一,具有先进先出的特性,在队尾进行插入操作,在队头进行删除操作。队列的存储结构分为两个大类,一种是顺序队,就是用数组实现。另一种就是链队,使用链表实现。顺序队存在真…...
Python 字典(Dictionary)小窍门
字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值 key:value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {} 中 ,格式如下所示:d {key1 : value1, key2 : value2 }注意:dict …...
知识图谱构建技术综述
摘要 *知识图谱为实现语义化智能搜索以及知识互联打下了基础,。, *随着知识的发展,传统的基于模板和规则构建的知识图谱已经被深度学习所替代。 知识组织得原则中:知识的充分性、有序性和标准化规则。深度学习的效果在很大程度上…...
环境变量和进程地址空间
目录 环境变量: env:显示所有的环境变量: echo $环境变量名表示查看环境变量的值 理解环境变量: getenv:显示环境变量的值 export set命令:显示所有变量 unset取消变量: pwd:当…...
【数据结构】栈和队列
目录 一、栈 1、栈的定义 2、栈的模拟实现(顺序栈) 1、创建一个顺序结构的栈 2、实现压栈方法(push) 3、模拟实现pop方法(出栈) 4、模拟实现peek(查看) 5、测试上述方法 3、栈的应用场景 1、改变元…...
League-Toolkit启动故障系统性排查方案:从现象到根治的完整解决路径
League-Toolkit启动故障系统性排查方案:从现象到根治的完整解决路径 【免费下载链接】League-Toolkit 兴趣使然的、简单易用的英雄联盟工具集。支持战绩查询、自动秒选等功能。基于 LCU API。 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 问…...
OpenClaw v2026.3.24-beta.1 深度技术分析报告:体验、生态与协作的“精装修”
报告版本: 1.1分析基准: v2026.3.23 (稳定化修复版本) -> v2026.3.24-beta.1 (预发布版)核心论点: 在经历了v2026.3.22的“架构大换血”与v2026.3.23的“系统性修复”之后,v2026.3.24-beta.1标志着OpenClaw的迭代节奏进入了一个…...
XGBoost+SHAP实战:如何让机器学习模型‘看懂’地图?
XGBoostSHAP实战:如何让机器学习模型‘看懂’地图? 当机器学习遇上地理空间数据,我们常常面临一个核心矛盾:模型预测精度与人类可解释性之间的博弈。传统GIS分析方法如空间滞后模型(SLM)或地理加权回归&…...
汉字拼音转换工具选型与实战指南:用pinyinjs解决多场景字符处理难题
汉字拼音转换工具选型与实战指南:用pinyinjs解决多场景字符处理难题 【免费下载链接】pinyinjs 一个实现汉字与拼音互转的小巧web工具库,演示地址: 项目地址: https://gitcode.com/gh_mirrors/pi/pinyinjs 在数字化产品开发中…...
[小红书AI自动化教程]凌晨2点我在睡觉,AI偷偷发了篇小红书爆款:醒来99+点赞,人类社媒苦役终结?
我把小红书交给 OpenClaw,它开始自己干活了 凌晨两点,我在睡觉,它却偷偷发了一篇爆款。 醒来点赞99,评论全是“姐妹求链接”。这不是科幻。去年我还为追热点熬夜秃头,如今一句“今天发什么”,AI 就能完成选…...
保姆级教程:在Win10上用Docker Desktop搞定Dify,再接入本地DeepSeek模型
保姆级教程:在Win10上用Docker Desktop搞定Dify,再接入本地DeepSeek模型 如果你是一位Windows 10用户,同时对AI应用开发充满兴趣,那么这篇教程就是为你量身定制的。我们将一步步带你完成Dify平台的部署,并将其与本地运…...
PP-DocLayoutV3快速调用:10行Python代码实现文档解析
PP-DocLayoutV3快速调用:10行Python代码实现文档解析 你是不是经常遇到一堆扫描的PDF或者图片文档,想快速提取里面的文字、表格和图片,却不知道从何下手?手动整理不仅费时费力,还容易出错。今天,我就来分享…...
Llama-3.2V-11B-cot实战案例:金融财报图表理解与关键结论提取
Llama-3.2V-11B-cot实战案例:金融财报图表理解与关键结论提取 1. 项目概述 Llama-3.2V-11B-cot 是一款结合视觉理解和逻辑推理能力的先进模型,特别适合处理需要综合分析图像和文本信息的任务。在金融领域,它能够自动解读财报中的各类图表&a…...
TI C2000 DSP新手必看:用CCS建第一个工程时,如何避免头文件找不到的坑?
TI C2000 DSP开发避坑指南:从零构建CCS工程的正确姿势 第一次打开Code Composer Studio(CCS)时,那个充满按钮和菜单的界面就像面对一架航天飞机的控制台——每个开关都看起来很重要,但完全不知道从哪下手。特别是当你在教程指导下创建了第一个…...
实战对比:Vamana/HNSW/NSG三大图算法在百维向量搜索中的性能差异
百维向量搜索实战:Vamana/HNSW/NSG三大图算法性能横评 在当今数据爆炸的时代,高效处理高维向量搜索已成为推荐系统、图像识别和自然语言处理等领域的核心技术瓶颈。面对百维甚至更高维度的向量数据,传统暴力搜索方法早已力不从心,…...
