栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言初阶——实用调试技巧

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言初阶——实用调试技巧

目录

前言

注意:

正文

调试的基本步骤为:

对于问题(Bug)的发现一般有以下三种方式:

Debug版本与Release版本

Debug(调试版本)

Release(发布版本)

调试使用的各种快捷键

F5:

Ctrl+F5

F9

F10

F11

断点

调试时查看程序信息

要想写出好代码,就得多动手、多调试才能有进步!

调试示例1

调试示例2

预防错误

优秀的代码往往具有以下特征:

常见的Coding(代码)技巧:

这里举一个绝妙代码的例子

编程常见错误

总结

鸣谢


前言

有一句话是这样说的:一个入门级的程序员可能发80%的时间在写代码,花20%的时间在调试;而一个老练的程序员则是花20%的时间写代码,花另外80%的时间在调试上面。可能这种比喻比较夸张,但能很好的说明调试的重要性,一段优秀的代码是由一个程序员构思+不断重复的调试找错误共同组合而成的,话不多说,我们直接进入调试。

注意:

文中图片上的代码所引用的头文件#include位于代码最顶部,因截图不方便,所以没有显示出来,如果读者想尝试其中的代码,记得加上#include!!!*

正文

调试(Debug/Debugging),又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程,一名优秀的程序员就像一名出色的侦探一样,把调试当作破案。

调试的基本步骤为:

1.发现程序错误的存在

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

3.确定错误产生的原因

4.提出纠正错误的解决方法

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

对于问题(Bug)的发现一般有以下三种方式:

1.程序员自己调试发现——自己解决,没啥影响

2.软件测试人员测试发现——反馈给程序员解决,影响不大

3.用户使用时发现——影响严重!这里@下某凡

Debug版本与Release版本

这两个版本是我们写程序时编译器所提供的,Debug(调试版本)是开发人员用的版本,Release(发布版本)是测试人员和用户所用的版本,两者虽说代码内容一样,但功能上差别还是挺大的。

Debug(调试版本)

这个版本主要特征就是可供开发者进行调试,包含了调试信息,并且不作任何优化,便于程序员调试程序, 同时因为其内含了调试信息,代码所产生的文件也会更大 

Release(发布版本)

发布版本一般是由已经写好的程序打包而成,往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好的使用 

调试使用的各种快捷键

在VS中提供了各种快捷键,比如最常用的Ctrl+F5让代码运行起来,下面我就简单介绍下使用频率极高的几个快捷键

F5:

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

Ctrl+F5

开始执行但不进行调试快捷键,如果你想让程序直接运行起来而不调试就直接使用的话,可以使用这个快捷键

F9

创建断点和取消断点的重要快捷键,可以在程序的任意位设置断点,这样就可以使得程序在想要的位置随意停止执行,继而一步步的执行下去

F10

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

F11

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

 限于篇幅问题,还有很多的快捷键这里不能给大家一一介绍,下面这个链接是一个大牛总结的快捷键合集,挺全的,感兴趣的同学可以点进去看看 VS中常用的快捷键_MrLisky的博客-CSDN博客_vs快捷键

断点

既然上面的F9提到了断点,那么我就在这给大家介绍下,断点是程序运行时截断的点,用于在调试中跳过一些没必要的语句,直接进入问题区域寻找问题,断点出现于代码的左侧,是一个红点,有点红绿灯的感觉,不让程序继续执行,断点还可以进行条件限制,比如在循环中,我想停到第五次循环,这时只需要在对应语句的断点上单击鼠标右键,选择条件并设置即可。 

调试时查看程序信息

调试时我们想要一个工具来对程序中的各种变量进行监视,或者说我们想看看程序运行时的内存信息,都可以通过调试-窗口中的各种窗口来进行监视 

要想写出好代码,就得多动手、多调试才能有进步!

调试示例1

下面是一个逻辑错误代码,语法没问题,能跑。 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

//计算n的阶乘相加

int main()

{

    int n = 0;

    scanf("%d", &n);

    int i = 1;

    int ret = 1;

    int sum = 0;

    int j = 0;

    for (j = 1; j <= n; j++)

    {

        //ret = 1;不加就会出错

        for (i = 1; i <= j; i++)

            ret *= i;

        sum += ret;

    }

    printf("%dn", sum);

    return 0;

}

示例调试纠正

想要找到问题也很简单,一步步调试就行 

 经过调试找出问题:ret为初始化

改正 

 可以看到代码运行结果正确,这就是利用调试解决问题的一个例子

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

//计算n的阶乘相加

int main()

{

    int n = 0;

    scanf("%d", &n);

    int i = 1;

    int ret = 1;

    int sum = 0;

    int j = 0;

    for (j = 1; j <= n; j++)

    {

        ret = 1;//不加就会出错

        for (i = 1; i <= j; i++)

            ret *= i;

        sum += ret;

    }

    printf("%dn", sum);

    return 0;

}

调试示例2

示例2是一个很有意思的代码,很多人可能一眼就能看出它越界的问题,认为会直接报错,但实际情况真的如此吗? 

老样子,直接调试 

 问题已经找出来了,经过鹏哥的详细讲解,我重复给大家看 

1

2

3

4

5

6

7

8

9

10

11

12

//初始化数组-死循环

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("hehen");

    }//虽然越界,但一直在死循环,没有机会报错

    return 0;

}//死循环,需要在x86平台下才有用

至于为什么会出现这个问题,有两个原因:

1.i 和 arr 是局部变量,局部变量是放在栈区之上的,栈区的使用习惯是:先使用高地址空间,再使用低地址空间。栈区和栈不是同一个概念

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

在vs x86平台下,i 和 arr 间隔有2个整型,arr[12]可触发死循环

在vc 6.0 平台下,i 和 arr 间隔有0个整型,arr[10]就可以触发死循环

在 GCC 平台下,i 和 arr 间隔有1个整型,arr[11]可触发死循环

预防错误

有错误就会有解决错误的方法,通过编码的技巧,减少错误

优秀的代码往往具有以下特征:

1.代码运行正常

2.Bug很少

3.效率高

4.可读性高

5.可维护性高

6.注释清晰明了

7.文档齐全

常见的Coding(代码)技巧:

1.使用assret

2.尽量使用const

3.养成良好的编程风格

4.添加必要的注释

5.避免编码的陷阱,比如上面那个例2

这里举一个绝妙代码的例子

版本1,欠优化

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

//模拟strcpy函数

void my_strcpy(char* dest, char* src)

{//版本1

    while (*src != 0)

    {

        *dest = *src;

        dest++;

        src++;

    }

    *dest = *src;

}

int main()

{

    char arr1[20] = "xxxxxxxxxxxxxxx";

    char arr2[] = "Hello Word";

    my_strcpy(arr1, arr2);

    strcpy(arr1, arr2);

    printf("%sn",arr1);

    return 0;

}

版本2,稍微优化了下,但还不够

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

void my_strcpy(char* dest, char* src)

{//版本2

    while (*src != 0)

    {

        *dest++ = *src++;

    }

    *dest = *src;

}

int main()

{

    char arr1[20] = "xxxxxxxxxxxxxxx";

    char arr2[] = "Hello Word";

    my_strcpy(arr1, arr2);

    strcpy(arr1, arr2);

    printf("%sn",arr1);

    return 0;

}

版本3,称得上是绝妙代码了,集成度高,风格简洁

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

void my_strcpy(char* dest,char* src)

{//版本3

    while (*dest++ = *src++)

    {

        ;

    }

}

int main()

{

    char arr1[20] = "xxxxxxxxxxxxxxx";

    char arr2[] = "Hello Word";

    my_strcpy(arr1, arr2);

    strcpy(arr1, arr2);

    printf("%sn",arr1);

    return 0;

}

为什么说版本3是绝妙代码呢?因为它巧妙的运用了C语言中的多个特征,做到了一行代码解决问题,实在是优雅实用,值得学习!

版本4,在版本3的基础上再完善

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

//模拟strcpy函数

//版本4

void My_strcpy(char* arr,char* ch)

{

    assert(arr!=NULL);//断言,防止空指针的传入

    assert(ch != NULL);

    while (*arr++ = *ch++)

    {

        ;

    }

}

int main()

{

    char arr[20] = { "XXXXXXXXXXXXXXXXX" };

    char ch[] = { "Hello" };

    My_strcpy(arr, ch);

    printf("%sn",arr);

    return 0;

}

代码4中使用了assert函数,这个函数可以在我们犯小错误时提醒我们错出在哪里

版本5,最终版

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//模拟strcpy函数

// 版本5

char * My_strcpy(char* arr,const char* ch)

{

    assert(arr!=NULL);

    assert(ch != NULL);

    char* ret = arr;

    while (*arr++ = *ch++)

    {

        ;

    }

    return ret;

}

int main()

{

    char arr[20] = { "XXXXXXXXXXXXXXXXX" };

    char ch[] = { "Hello" };

    printf("%pn", arr);

    printf("%sn",My_strcpy(arr,ch));

    printf("%pn", My_strcpy(arr, ch));

    return 0;

}//模拟实现标准的strcpy库函数

在版本5中进一步完善了这个函数,包括 const 对不变量进行保护,char * 返回标准类库函数地址 经过五个版本的迭代,这个函数迎来了最终版,一个真正意义上的优秀代码!!!

健壮性(鲁棒性)——增加 const 让代码更靠谱

编程常见错误

常见错误一般分为三类:编译型、链接型、运行型

编译型错误:直接看错误提示信息(在绿色虚线处双击鼠标左键),解决问题,或者凭借经验解决,相对来说犯这种错误比较好解决

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

运行型错误:借助调试,逐步定位解决问题,最难搞的问题

温馨提示:代码千万条,细心第一条。编码不规范,调试两行泪

总结

本文就调试实用技巧做了一些阐述,希望能让大家对调试这个重要概念进一步理解。

学会调试,不再求人!

希望我的文章能够为大家再写代码时提供帮助!

希望各位大牛能够及时指出我文中存在的不足和错误,我好加以改正!

最后希望以后写的文章越来越好

鸣谢

感谢比特科技鹏哥所传授的知识,让我受益匪浅,写出这篇文章!@比特鹏哥

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/991308.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号