【C语言】常见调试技巧-走进代码

  • 什么是bug?
  • 什么是调试?调试能力有多重要?
  • debug和release的简单介绍
  • windows环境下调试介绍
  • 一些调试的实例
  • 如何写出易于调试的代码?
  • C编程中常见的错误
  • 模拟实现strcpy,strlen库函数

测试环境:vs2022


一、什么是bug?

第一次被发现的导致计算机错误的飞蛾,也是第一个计算机程序错误。

二、什么是调试?调试能力有多重要?

       所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了,如果问心有 愧,就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径。顺 着这条途径顺流而下就是犯罪,逆流而上,就是真相。每个优秀的程序员都是一名出色的侦探,当遇到bug时,每一次的调试都是尝试破案的过程.

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

调试的基本步骤

  • 发现程序错误的存在
  • 以隔离、消除等方式对错误进行定位
  • 确定错误产生的原因
  • 提出纠正错误的解决办法
  • 对程序错误予以改正,重新测试

三、debug和release的简单介绍

       Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。

       Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最的,以便用户很好地使用。

代码演示:

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		arr[i] = i + 1;
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ",arr[i]);
	}
	return 0;
}

 所以我们说调试就是在Debug版本的环境中,找代码中潜伏的问题的一个过程。


 四、windows环境下调试介绍 

  1. 调试环境的准备:在环境中选择 debug 选项,才能使代码正常调试。    

     2.常用调试快捷键(熟练)

     F5:启动调试,经常用来直接调到下一个断点处。

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

    F10:逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。

    F11:逐语句,每次都执行一条语句,这个快捷键可以使我们的执行逻辑进入函数内部(最常用)。

    Ctrl + F5:开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。

    调试启动之后,才能看到窗口的相关信息.

     3. 调试时查看程序当前信息

代码演示:

 ①查看临时变量的值:在调试开始之后,用于观察变量的值。

 查看内存信息:在调试开始之后,用于观察内存信息。

 一个十六进制数字是四个二进制位,两个十六进制是八个二进制位(一个字节)

查看调用堆栈:返回的是函数的调用逻辑

代码演示:

void test2()
{
	printf("hehe\n");
}
void test1()
{
	test2();
}
void test()
{
	test1();
}
int main()
{
	test();
	return 0;
}

一些调试的实例 

实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出。

问题代码:

int main()
{
	int n = 0;
	scanf("%d", &n);
	int i = 0;
	int ret = 1;
	int sum = 0;
	int j = 0;
	for (j = 1; j <= n; j++)
	{
		for (i = 1; i <= j; i++)
		{
			ret = ret * i;
		}
		sum = sum + ret;
	}
	printf("%d\n", sum);
}

用调试的方法进行错误分析:

 

 正确代码:

运行错误:调试解决的就是运行错误
解决问题:
应该是什么结果 - 预期 调试的时候发现不符合预期就找到了问题.

实例2:以下代码的运行结果是什么?

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;
}

用调试的方法进行分析:

 简要分析:

  1. i和arr是局部变量,局部变量是放在栈区上的.
  2. 栈区内存的使用习惯是:先使用高地址空间,再使用低地址空间.
  3. 数组随着下标的增长,地址是由低到高变化的.
  4. 数组用下标访问的时候,只要适当地往后越界,就有可能覆盖到i
  5. i一旦被覆盖的话,就有可能会导致死循环
  6. 不同编译环境下的预留空间:vc6.0 0个整形;gcc 1个整形 vs 2个整形

改进代码:

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	//0 - 9
	for (i = 0; i <= 12; i++)//一直在死循环没有机会报错
	{
		// i <= 11越界了但没有造成死循环,会报错
		arr[i] = 0;
		printf("hehe\n");
	}
	return 0;
}

预防发生错误:通过编码技巧,减少错误.

六、如何写出易于调试的代码?

优秀的代码:

  1. 代码运行正常
  2. bug很少
  3. 效率高
  4. 可读性高
  5. 可维护高
  6. 注释清晰
  7. 文档齐全

常见的编码技巧:

  1. 使用assert
  2. 尽量使用const
  3. 养成良好的编码风格
  4. 添加必要的注释
  5. 避免编码的陷阱

七、C编程中常见的错误

       常见的错误分类:

        编译型错误:直接看错误提示信息(双击),解决问题.凭借编码经验就可以搞定,相对简单.

        链接型错误:看错误提示信息,主要在代码中找到错误信息中的标识符,定位问题.一般是标识符名不存在或者拼写错误

        运行时错误:借助调试逐步定位问题,相对困难.

温馨提示:做一个有心人,积累排错经验

八、模拟实现strcpy,strlen库函数

int main()
{
	char arr1[20] = "xxxxxxxxxxxx";
	char arr2[] = "hello";
	//strcpy(arr1,arr2);
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

方法一:

#include <string.h>
void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;
}

方法一中的代码优化:

void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')
	{
		*dest++ = *src++;
	}
	*dest = *src;
}

注: *解引用操作符的优先级比++操作符的优先级高,先进行赋值运算,再各自自增,无关自增顺序

方法二:

void my_strcpy(char* dest, char* src)
{
	while (*dest++ = *src++)
	{
		;
	}
}

代码简要分析:

  1. *dest++ = *src++比较字符的ASCII码值,0为假,非0为真,'\0'的ASCII码值为0.
  2. 0为假,不进入while循环.非0为真,进入循环.
  3. 空语句的作用:既拷贝了'\0',又使得循环停止.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值