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

Ⅰ 调试的介绍

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. 运行时错误

  • 一般为程序运行起来时发现的错误
  • 只能借助调试定位到问题所在,这种问题最难解决。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值