实用调试技巧

发布于:2022-11-28 ⋅ 阅读:(208) ⋅ 点赞:(0)

目录

实用调试技巧::

                  1.调试的定义及重要性

                  2.Debug和Release版本的介绍

                  3.Windows环境调试介绍

                  4.调试实例

                  5.如何写出易于调试的代码

                  6.编程常见的错误


实用调试技巧::

1.调试的定义及重要性

调试的定义及重要性

调试(Debug/Debugging),又称除错,是发现;和减少计算机程序或电子仪器设备中程序错误的一个过程。程序调试时将编译的程序投入实际运行前,用手工或编译程序等方法进行测试,修正语法错误和螺距错误的过程,是保证计算机信息系统正确性的必不可少的步骤。

调试的基本步骤

1.发现程序错误的存在 2.以隔离,消除等方式对错误进行定位 3.确定错误产生的原因 

4.提出纠正错误的解决办法 5.对程序错误予以改正 再重新测试

2.Debug和Release版本的介绍

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

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

3.Windows环境调试介绍

最常用的几个快捷键:

F5:启动调试,经常用来直接跳到下一个断点处.F5一般要和F9配合使用 单独使用会直接显示调试结果 甚至会一闪而过.

F9:创建断点和取消断点.

断点的作用:可以在程序的任意位置设置断点。可以使得程序在想要的位置随意停止执行,继而一步步执行下去.也可以创建多个断点,设置条件断点或禁用断点.

F10:逐过程执行.

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

F11:逐语句执行.

就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部.

Ctrl + F5:开始执行不调试.

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

1.查看临时变量的值:在调试开始之后查看临时变量的值.

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

3.查看调用堆栈:调用堆栈 右击显示外部代码 能反馈函数是如何调用的,通过调用堆栈,可以清晰的反应函数的调用关系以及当前调用所处的位置.

4.查看汇编信息:在调试开始之后,有两种方式转到汇编 . 第一种:右击鼠标,选择“转到反汇编”

第二种:调试 转到反汇编 可以切换到汇编代码

5.查看寄存器信息:可以查看当前运行环境寄存器的使用信息.

4.调试实例

实例一:求1!+ 2!+ 3!…… +n!;不考虑溢出.

#include<stdio.h>
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 *= i;
		}
		sum += ret;
	}
	-
	printf("%d\n", ret);
	return 0;

程序输出结果:输入3,期待输出结果为3,但实际输出结果为15.

原因:上一次的ret的执行结果会影响到下一次的阶乘运算.

调试结果:将ret置为1 即int ret = 1

实例二:来源于《C陷阱与缺陷》的一道经典题目

#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("haha\n");
	}
	return 0;
}

如果是Debug模式去编译,程序的结果是死循环.

如果是Release模式取编译,程序没有死循环,运行结果为打印13个haha.

Debug模式调试结果:死循环的打印haha是因为arr[12]和 i ,共用一块地址空间,将arr[12]值改为0,i的结果也会跟着置为0.

原因:在Debug版本中 i的地址比arr的地址要大一些,在Realse版本中 i的地址比arr的地址要低一些,在Realse版本中 对代码进行了优化 使得内存开辟的方式与默认开辟的方式不同.

图示分析原因:

2016年Nice公司的校招笔试题目出自于此:

5.如何写出易于调试的代码

优秀的代码:

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

常用的代码技巧:

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

示范:模拟实现库函数:strcpy 和 strlen

#include<stdio.h>
模拟实现strlen
int my_strlen(const char* str)
{
    int count = 0;
    assert(str != NULL);
    while(*str)
    {
        count++;
        str++;
    }
    return count;
}
int main()
{
    const char* p = "abcdef";
    int len = my_strlen(p);
    printf("len = %d\n", len);
    return 0;
}
模拟实现strcpy
//char* strcpy(char* destination,const char* strsource)
//strcpy不用返回值来接收 strcpy在拷贝字符串的时候会把原字符串的\0也拷贝过去
代码1
void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;
}
代码2
void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')
	{
		*dest++ = *src++;
	}
	*dest = *src;
}
代码3
void my_strcpy(char* dest, char* src)
{
	while (*dest++=*src++)
	{
		;
	}
}
代码4
#include<assert.h>
void my_strcpy(char* dest,const char* src)
{
	断言 assert不止用于判断空指针 判断两个变量相不相等也可以
	assert(src != NULL);
	assert(dest != NULL);
	while (*dest++ = *src++)
	{
		;
	}
}
代码5:为了实现链式访问
 char* my_strcpy(char* dest,const char* src)
{
  strcpy函数返回的是目标空间的起始地址
  char* start = dest
	断言 assert不止用于判断空指针 判断两个变量相不相等也可以
	assert(src != NULL);
	assert(dest != NULL);
	while (*dest++ = *src++)
	{
		;
	}
 return start;
}
 int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello world";
	printf("%s\n", my_strcpy(arr1, arr2));
	return 0;
}
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello world";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

6.编程常见的错误

1.编译型错误:直接查看错误提示信息,解决问题.

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

3.运行时错误:借助调试,逐步定位问题.

本文含有隐藏内容,请 开通VIP 后查看