【C语言初阶(19)】实用的 VS 调试技巧

news2024/11/23 15:32:57

文章目录

  • Ⅰ 调试的介绍
  • Ⅱ 常用调试快捷键
  • Ⅲ 调试的时候查看程序当前信息
    • ⒈查看临时变量的值
    • ⒉查看内存信息
    • ⒊查看调用堆栈
    • ⒋查看汇编信息
    • ⒌查看寄存器信息
  • Ⅳ 观察形参指针指向的数组
  • Ⅴ 易于调试的代码该如何编写
    • ⒈const 修饰指针变量
    • ⒉良好代码示范
  • Ⅵ 编程中常见的错误

Ⅰ 调试的介绍

1. 什么是调试

  • 调试(Debug),又称为除错,是发现和减少计算机程序或电子仪器设备中程序的错误的一个过程。

2. 调试的基本步骤

  1. 发现程序的错误的存在(开发 → 测试 → 用户)。
  2. 以隔离、消除等方式对错误进行定位。
  3. 确定错误产生的原因。
  4. 提出纠正错误的解决办法。
  5. 对程序错误允以改正,重新测试。

3. Debug 和 Release 的介绍

  • Debug 称为调试版本,它包含有各种调试信息,且不作任何优化,所占空间更大。
  • Release 称为发布版本,此版本进行了优化,不包含调试信息,用户使用的以及测试人员检测的都是这个版本。

在这里插入图片描述

4. 调试环境的准备

  • 在环境中选择 Debug 选项,只有在这里才可以进行调试。

Ⅱ 常用调试快捷键

  • 注意:以下所有快捷键如果不起作用的话,请搭配 FN 键使用。

F5

  • 启动调试:用于直接跳到下一个断点处,经常和 F9 成对使用。

F9

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

F10

  • 逐过程调试:通常用于处理一个过程,一个过程可以是一次函数调用,或是一条语句。
  • 当碰到函数调用时,不会进到函数内部,无法观测到函数体内部的细节。

F11

  • 逐语句调试:每次都执行一条语句,可以进入函数体观察函数内部的细节。

CTRL + F5

  • 开始执行不调试:用于让程序直接运行起来而不进行调试,学习编程初期使用的最多的就是这个。

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

  • 进入调试之后,在调试窗口中才能看到下面的信息。

在这里插入图片描述

⒈查看临时变量的值

  • 在调试开始之后,用于观察变量内存放的值的变化。
  • 自动窗口与监视窗口都能够观察到局部变量的信息。

在这里插入图片描述

自动窗口与监视窗口的区别

  • 自动窗口会自动显示所有局部变量的信息(不管你是否需要),随着调试的进程,自动窗口内所观察的变量有时会自动出现,有时又会自动消失。
  • 监视需要程序员手动输入(想观察谁就输入谁),观察的变量不会随着调试的进程而消失,强雷建议使用监视窗口来观察局部变量的值。

⒉查看内存信息

在这里插入图片描述

⒊查看调用堆栈

调用堆栈

  • 调用堆栈,反应的是函数的调用关系。
  • 后期当遇到很复杂的函数关系的时候,就可以使用调用堆栈来理清函数之间的调用关系。

举个栗子

  • 现有以下代码:

在这里插入图片描述

  • main 函数调用了 test1,test1调用了 test2,test2 → test3。
  • 而 main 函数又是被其他函数所调用的。
  • 函数调用采用的是数据结构中 的先进后出的特性,先调用的函数后结束。

⒋查看汇编信息

  • 查看 C 语言代码所翻译出来的汇编代码。

在这里插入图片描述

⒌查看寄存器信息

  • 用于查看当前运行环境的寄存器的使用信息。

在这里插入图片描述

Ⅳ 观察形参指针指向的数组

  • 众所周知,在给函数传数组名的时候,传递的是数组首元素的地址;
  • 这样一来的话,在函数内部调试的时候,只是输入个数组名就不能很好的观察到数组内部的值的变化。

在这里插入图片描述

  • 但是,只要在我们想要观察的数组指针变量名后面加上 " 逗号 " 和 " 想要观察的元素个数 " 就可以很好观察到数组内部元素的值了。

在这里插入图片描述

二维数组同理

  • 如果将二维数组名作为参数传给函数的话,观察函数形参的二维数组指针也就只能观察到原数组第一行的内容。
  • 因为二维数组的数组名为第一行的地址。

Ⅴ 易于调试的代码该如何编写

1. 什么是高质量的代码

  1. 代码能够正常运行。
  2. 尽可能少的 BUG(不可能没有BUG)。
  3. 可执行效率够高。
  4. 可读性要强。
  5. 可维护性高。
  6. 注释要清晰,能让人看得懂。
  7. 相应文档齐全。

2. 常用编程技巧

  1. 使用 assert 断言。
    • assert 的表达式如果为假则直接结束程序并报错,使用 assert 来判断指针是否指向 NULL。
  2. 尽量使用 const 修饰变量(包括变量和指针变量)。
  3. 养成良好的编码风格。
  4. 添加必要的注释。
  5. 避免编码的陷阱。

⒈const 修饰指针变量

  • const 可以修饰普通的变量,让变量的值无法被修改,从而变成常变量(本质任然是个变量)。

在这里插入图片描述

  • 同样的,const 也可以用来修饰指针变量。const 修饰指针有两种形式。

  • 对于指针变量来说,应该关注的有两点:

    1. 指针变量本身能不能改;
    2. 指针变量指向的那个变量能不能改。
  • const 放置的位置就决定了能不能修改这两种值。

1. const 放在 * 的左边

const int* p = &n;	
					//只要 const 放在 * 的左边,这两中写法就都一样。
int const* p = &n;	
  • 限制的是指针指向的内容,也就是限制了 *p,让指针指向的那个变量的值无法被修改。
  • 但是指针变量是可以修改的,指针可以指向其他变量。

在这里插入图片描述

2. const 放在 * 的右边

int* const p = &n;
  • 限制的是指针变量本身,在 p 已经指向了 n 之后,p 就不能再被修改为指向其他变量了。
  • 但是可以通过指针变量修改指向的内容。

在这里插入图片描述

  • 当在 * 的两边都加上 const 之后,p 就只能指向 n,并且*p 也不能改变 n 的值了。

在这里插入图片描述

⒉良好代码示范

模式实现 strcpy

#include <stdio.h>
#include <assert.h>

//我只想改变 str1,不想改变 str2 ,const 保证 src 指针指向的 str2 数组内的值不会一不小心被修改
char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);			//当传过来的两个指针为 NULL 时,直接结束程序并报错
	char* ret = dest;				//保存目的地首元素的地址,一会还要传回去

	while (*dest++ = *src++)		//直到 *src = \0,然后由于是后置++,再将 \0 也赋值给 dest 为止
	{
		;
	}

	return ret;						//返回 dest 的起始地址
}	

int main()
{
	char str1[20] = "***************";
	char str2[]   = "hello word!";

	printf("%s\n", my_strcpy(str1, str2));

	return 0;
}

在这里插入图片描述

模拟实现 strlen

#include <stdio.h>
#include <assert.h>

size_t my_strlen(const char* str)	//防止原数组中得内容不小心被修改
{
	assert(str);					//判断传过来的指针的有效性 (!= NULL)
	const char* end = str;			//标识字符串末尾

	while (*end++);					//当 end 指向 \0 时退出循环

	return (end - str - 1);			//指针 - 指针得到同一块空间的两个地址之间的元素个数
									//-1 是因为后置 ++ 的副作用,要把 \0 的那一个元素去掉
}

int main()
{
	char str[] = "hello word!";

	printf("%zd\n", my_strlen(str));
	
	return 0;
}

在这里插入图片描述

Ⅵ 编程中常见的错误

1. 编译型错误

  • 一般为语法错误
  • 这类错误直接查看错误提示信息(双击),解决问题,或者凭借经验就可以搞定。

2. 链接型错误

  • 一般为未包含头文件符号名(函数名、变量名)不存在或者拼写错误
  • 看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。

3. 运行时错误

  • 一般为程序运行起来时发现的错误
  • 只能借助调试定位到问题所在,这种问题最难解决。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/828531.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mr. Cappuccino的第54杯咖啡——Mybatis运行原理

Mybatis运行原理 Mybatis运行的三个阶段Mybatis运行原理图 Mybatis运行的三个阶段 初始化阶段&#xff1a;读取并解析XML配置文件和注解中的配置信息&#xff0c;创建配置对象&#xff0c;并完成各个模块的初始化工作&#xff0c;底层采用建造者模式&#xff1b;代理封装阶段&…

Qt、C/C++环境中内嵌LUA脚本、实现LUA函数的调用执行

Qt、C/C环境中内嵌LUA脚本、实现LUA函数的调用执行 Chapter1. Qt、C/C环境中内嵌LUA脚本、实现LUA函数的调用执行1、LUA简介2、LUA脚本的解释器和编译器3、C环境中内嵌LUA执行LUA函数调用4、Qt内嵌LUA执行LUA函数调用5、运行结果6、内嵌LUA脚本在实际项目中的案例应用 Chapter1…

元宇宙是概念炒作?

关键字&#xff1a;万界星空、万界星空科技、工业元宇宙、AR数字孪生、工业数字孪生、汽车数字孪生、机械加工数字孪生 引言 近两年被“元宇宙”席卷了&#xff0c;好多人问也好多人在各大媒体讨论过&#xff1a;“元宇宙到底是个啥&#xff1f;” 想必你也一定有所耳闻&…

世界少棒经典赛·棒球1号位

世界少棒经典赛 1. 世界少棒经典赛的起源 详细描述世界少棒经典赛的历史起源。 世界少棒经典赛的历史起源可以追溯到1985年&#xff0c;那个夏天&#xff0c;它首次在美国新泽西州举行&#xff0c;那时只有来自美国的12支球队参赛&#xff0c;这些球队在当地的特伦顿市体育中…

meanshift算法通俗讲解【meanshift实例展示】

meanshift算法原理 meanshift算法的原理很简单。假设你有一堆点集&#xff0c;还有一个小的窗口&#xff0c;这个窗口可能是圆形的&#xff0c;现在你可能要移动这个窗口到点集密度最大的区域当中。 如下图&#xff1a; 最开始的窗口是蓝色圆环的区域&#xff0c;命名为C1。蓝…

【Linux命令200例】split将一个大文件拆分成多个小文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主✌&#xff0c;2023年6月csdn上海赛道top4✌。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加…

Java多线程(四)

目录 一、线程的状态 1.1 观察线程的所有状态 1.2 线程状态和状态转移的意义 1.2.1 NEW、RUNNABLE、TERMINATED状态转换 1.2.2 WAITING、BLOCKED、TIMED_WAITING状态转换 1.2.3 yield()大公无私让出cpu 一、线程的状态 1.1 观察线程的所有状态 public class Demo9 {public st…

HTML5网页设计小案例:网页导航栏的设计

什么是导航栏&#xff0c;按我的理解就是位于网页顶部或者侧边一组链接或者按钮&#xff0c;用来指导大家找到网页的不同板块&#xff0c;大家可以一目了然的找到自己想看的板块内容。今天我们设计一个位于网页顶部的的导航栏。按我的生活经验来说&#xff0c;网页的顶部导航栏…

人机融合意识与人类的意识的区别

人机融合意识是指人类与计算机系统之间建立起密切的交互和协作关系&#xff0c;形成一种共同的意识和认知状态。人机融合意识与人类意识存在一些本质上的区别&#xff0c;可以从以下几个方面进行区分&#xff1a; 原始性&#xff1a;人类的意识是自然生命的产物&#xff0c;伴随…

怎样在Apipost中设计出实用又好看的API文档

Apipost一直推荐文档先行的API设计理念&#xff0c;在Apipost中可以添加Markdown格式的文本&#xff0c;用以储备文档和API文档设计。 作为一种轻量级标记语言&#xff0c;Markdown在撰写文档、博客文章、README文件以及网站内容上被广泛使用。 如何在Apipost中设计出漂亮的文…

在coopeliasim中打开场景对象属性的三种方式

一、点击[Menu bar --> Tools --> Scene object properties] 二、点击界面左侧按钮 三、双击场景层次结构中的对象图标

堆、堆栈、栈、堆和栈这些概念你还分的清楚吗?

数据结构中的堆、栈和队列 堆&#xff1a;堆是一种经过排序的树形数据结构&#xff0c;每个结点都有一个值。通常我们所说的堆的数据结构&#xff0c;是指二叉堆。堆的特点是根结点的值最小&#xff08;或最大&#xff09;&#xff0c;且根结点的两个子树也是一个堆。由于堆的…

TDosCommand 组件来执行 JavaScript 脚本(nodejs)

可以在 Delphi 中使用 TDosCommand 组件来执行 JavaScript 脚本。但是&#xff0c;由于 JavaScript 是一种脚本语言&#xff0c;它通常在浏览器中运行&#xff0c;因此您需要使用一种 JavaScript 引擎来执行 JavaScript 脚本。常见的 JavaScript 引擎有 Node.js、Rhino、V8 等等…

定档!WAVE SUMMIT 2023@全球开发者,8月16日北京见!

潮汐涌动时&#xff0c;变化悄然发生。2023年全球AI浪潮迭起&#xff0c;大语言模型热度空前&#xff0c;生成式人工智能为千行百业高质量发展带来更多想象空间&#xff0c;一个蓬勃创新、重构万物的“大模型时代”正蓄势待发。 滴滴滴&#xff5e;飞桨全球开发者&#xff0c;…

CMIP6数据处理教程

详情点击链接&#xff1a;CMIP6数据处理及在气候变化、水文、生态等领域中的应用教程 一&#xff1a;CMIP6中的模式比较计划 1.1 GCM 全球气候模型&#xff08;Global Climate Model, GCM&#xff09;&#xff0c;也被称为全球环流模型或全球大气模型&#xff0c;是一种用于…

科研周报1

时间&#xff1a;2023-07-26至2023-08-02 overleaf (LaTex) 生成并排子图 查看以下这段与chatgpt的对话&#xff1a; https://chat.openai.com/share/e7fbdccd-2847-4dbb-b816-db2b7455c628 如果要生成上下排列的子图&#xff0c;将\hfill更换为\即可 其他 前馈控制 参考…

Edge浏览器安装vue devtools

1. 下载地址 GitHub - vuejs/devtools: ⚙️ Browser devtools extension for debugging Vue.js applications. 2. 下载后的压缩包解压并打开文件夹&#xff0c;右键选择&#xff1a;git bush here 3. 安装依赖 npm install 4. 成功安装依赖后打包 npm run build

自动化测试中的数据驱动

DDT 当测试框架是unittest时&#xff0c;可以使用ddt。ddt 这个类装饰器必须装饰在 TestCase 的子类上&#xff0c;TestCase 是 unittest 框架中的一个基类&#xff0c;它实现了 Test Runner 驱动测试运行所需的接口&#xff08;interface&#xff09;。 DDT 的使用步骤如下&…

在屏幕上输出9*9乘法口诀表

//在屏幕上输出9*9乘法口诀表 int main() {int i 0;int j 0;for (int i 1; i < 9; i) {for (int j 1; j < 9; j)printf("%d*%d%d\t ", i, j, i * j);printf("\n");}}

人像抠图 + OpenGL ES 还能这样玩?没想到吧(附带源码)

OpenGL ES 利用抠图算法实现人像留色 人像留色的原理 现在人像分割技术就像当初的人脸检测算法一样,称为广泛使用的基础算法。 今天本文介绍的人像留色其实就是三年前某 AI 巨头利用 video 分割技术展示的应用场景:人体区域保留彩色,人体区域之外灰度化。所以人像留色的关…