C语言----实用调试技巧

此文是作者最初学习C语言时的笔记博文,如有错误请积极指正。谢谢!!!



目录

1,什么时bug

2. 调试是什么?有多重要?

2.1 调试是什么?

2.2 调试的基本步骤

2.3 Debug和Release的介绍。

3. Windows环境调试介绍

 3.2 学会快捷键

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

4,多多动手,尝试调试,才有进步

5,实例

5.1实例一:

5.2 实例二:

6. 如何写出好(易于调试)的代码。

6.1 优秀的代码:

6.2 示范:

7,编程常见的错误

7.1 编译型错误

7.2 链接型错误

7.3 运行时错误

8.拓展


1,什么时bug

bug是计算机领域专业术语,原意是臭虫,现在用来指代计算机上存在的漏洞。原因是系统安全策略上存在的缺陷,有攻击者能够在未授权的情况下访问的危害。



2. 调试是什么?有多重要?

所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了,如果问心有愧, 就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径。 顺着这条途径顺流而下就是犯罪,逆流而上,就是真相。

一名优秀的程序员是一名出色的侦探。

每一次调试都是尝试破案的过程

2.1 调试是什么?

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

2.2 调试的基本步骤

1,发现程序错误的存在

2,以隔离、消除等方式对错误进行定位

3,确定错误产生的原因

4,提出纠正错误的解决办法

5,对程序错误予以改正,重新测试

2.3 Debug和Release的介绍。

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

所占用的内存更大,因为包含调试信息。

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

占用的内存就相对小一些了 ,不包调试信息。



3. Windows环境调试介绍

3,1准备调试环境

(修改下文件环境改为debug(调试)版本),

release版本为发布版本是不能进行调试的,以便用户更好的使用。

 3.2 学会快捷键

vs中有很多的快捷键提供给我们使用

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

f9:创建断点和取消断点

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

所以f5一般不能单独使用,要和f9配合使用,

 条件断点

右键点击红点可以设置条件断点

f10:逐过程,一个过程可以是一次函数调用,或者是一条语句。

f11:逐语句(可以进入函数内部)

 CTRL + F5:开始执行不调试,不受断点的影响。

注意有时候我们需要一些健来配合使用

win-->fn+F5 ,fn+F10-----

mac--->command+F5----

 vs中还有很多的快捷键,建议大家去实际操作一下。

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

当代码调试起来的时候,我们在调试---窗口-里面可以看到各种各样的一些表示程序信息的窗口供我们使用,例如常用的 监视, 自动监视, 局部变量, 内存, 调用堆栈, 反汇编, 寄存器------



4,多多动手,尝试调试,才有进步

一定要熟练掌握调试技巧。

初学者可能80%的时间在写代码,20%的时间在调试。

但是一个程序员可能20%的时间在写 程序,但是80%的时间在调试。

我们所讲的都是一些简单的调试。

以后可能会出现很复杂调试场景:多线程程序的调试等。

多多使用快捷键,提升效率。



5,实例

5.1实例一:


int main()
{
	int i = 0;
	int sum = 0;//保存最终结果
	int n = 0;
	int ret = 1;//保存n的阶乘
	scanf("%d", &n);
	for (i = 1; i <= n; i++)
	{
		int j = 0;
		for (j = 1; j <= i; j++)
		{
			ret *= j;
		}
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

这时候我们如果3,期待输出9,但实际输出的是15。原因:ret的定义在循环的外面,ret存的是每一次n的阶乘,但是定义在外面,他就会记住上一次n-1的阶乘的结果,这就会出错,应该在循环内部重新赋值或者定义在循环内部。

5.2 实例二:

#include <stdio.h>
int main()
{
    int i = 0;
    int arr[10] = {0};
    for(i=0; i<=12; i++)
   {
        arr[i] = 0;
        printf("hehe\n");
   }
    return 0;
}

第二个实例。可能会导致死循环(依赖环境有时候不会死循环),死循环的原因是:

这个程序是先创建 i 再创建arr ,栈区的使用习惯是先使用高地址的空间再使用低地址的空

间,数组地址的习惯是随着下标的增长地址逐渐增大,刚好的 i 变量的地址,比arr最后一个元素的地址刚好大两个整型的地址空间,所以arr[12]在越界访问的时候刚刚好访问到 i 的地址修改了 i 的数值,把 i 修改为0;循环刚好有往复了,这就会造成死循环。

当前代码在不同的环境下效果是不一样的,

1,在vc6.0中 i 和arr最后一个元素地址没有空间

2,在gcc中 i 和arr最后一个元素地址空出了一个整型的空间。

3,把arr和 i 的定义顺序调换一下就不会死循环了,这时会越界访问。

4,在release版本下他也不会死循环,release版本会优化代码,vs在release版本下内存布局会发生改变  , i 的地址比arr的地址小,也就不会出现死循环了。被优化了。

5.在x64 i 和arr的地址也没谱,实际运行会报错越界访问(vs),在x86下就会死循环,说明这个代码是很依赖环境的。

推荐一个条调试的方法:就是模拟给别人讲自己的代码的这个过程来看自己写的代码,在讲的过程中可能就会找到自己的错误,和自己的代码是否合理。



6. 如何写出好(易于调试)的代码。

6.1 优秀的代码:

1. 代码运行正常

2. bug很少

3. 效率高

4. 可读性高

5. 可维护性高

6. 注释清晰

7. 文档齐全

常见的coding技巧:

1. 使用assert

2. 尽量使用const

3. 养成良好的编码风格

4. 添加必要的注释

5. 避免编码的陷阱。

6.2 示范:

模拟实现strcpy

三个版本!!

//减配版 
void my_strcpy(char* arr2, char* arr1)
{
	while (*(arr1 -1) != '\0')
	{
		*arr2 = *arr1;
		arr1++;
		arr2++;
	}
}

//中配版
#include<assert.h>
void my_strcpy(char* arr2,const char* arr1)//const修饰的常量就不可被更改
{
	assert(arr1 && arr2);
	while (*arr2 = *arr1)
	{
		arr1++;
		arr2++;
	}
}
 //高配版
#include<stdio.h>
#include<assert.h>

char* my_strcpy(char* arr2,const char* arr1)
{
	char* mid = arr2;
	assert(arr1 && arr2);//断言一下arr1 和arr2不是空指针

	while(*arr2 = *arr1)
	{
		arr1++;
		arr2++;
	}
	return mid;
}
//顶配版
 

char* my_strcpy(char* arr2,const char* arr1)
{
	char* mid = arr2;
	assert(arr1 && arr2);//断言一下arr1 和arr2不是空指针

	while(*arr2++ = *arr1++)
	{
		;
	}
	return mid;
}

7,编程常见的错误

7.1 编译型错误

编译型错误就是语法错误,比如少个括号,或者少个是其他的什么符号。这个错误一般编译器就会给你找出来,直接看错误信息的提示,含容易就可以解决了。他们都是固定的常见的错误。双击错误信息就会跳到错误附近。再修改即可。

7.2 链接型错误

在链接期间发生的错误,在我们运行程序的时候源文件test.c 会生成一个可执行程序test.exe。这个过程有俩个步骤第一个叫编译 ,第二个叫链接。例如:

1,在未包含头文件的时候,编译器会报错说无法解析的外部符号。

2,在引用自定义函数时,尚未编写就开始运行,这个时候也会报错,无法解析的外部符号x'x'x。该函数未定义。

3,或者自定义函数名写错了,也一样的。这个时候我们可以搜索一下,ctrl+f  就会出现搜索框。搜索想要找的函数名。

这种错误一般解决要看错误信息提示,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符不存在或者拼写错误。如果是库函数,也可能是未引头文件。

7.3 运行时错误

运行时错误,也就是结果不是我们想要的, 这个时候就是我们说的程序有bug了,这个错误可能就是逻辑错误或者是其他错误了。

这种错误 想要解决一般要通过调试来观察代码的错误了,这个错误才是最致命的。

8.拓展

1,在内存中栈区的使用习惯是先使用高地址的空间,再使用低地址的空间。

数组随着下标的增长地址是由低到高变化的

strcpy:库函数,字符串拷贝。<string.h>,包含'\0'。

const:就是修饰指针的。

const char* str = &a;
char const *str = &a;

const 这样写的时候,也就代表*str指向的空间不可被更改((就是a的值不能更改),但是str可以被更改可以改为&b。

char * const str = &a;

const这样写的时候,也就是代表*str可以被更改(就是a的值可以被改),但是str不可被更改也就是&a不能改。

assert:库函数<assert.h> 意思是断言一些条件。

const修饰的常量

const int num = 10;
//这里不能用num = 20;直接去更改他
//但是可以这样改
int * pa =&num;
*num = 20;
//这样a的值就被改掉了  感觉这个操作不合理的
这个仿佛是C语言的一个bug

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小峰同学&&&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值