一定要掌握的实用调试技巧(vs2019)(下)

简介: 一定要掌握的实用调试技巧(vs2019)(下)

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


       6.1、优秀的代码


优秀的代码应具有:1.代码运行正常

                                2.bug很少

                                3.效率高

                                4.可读性高

                                5.可维护性高

                                6.注释清晰

                                7.文档齐全

我们常见的coding技巧有:1.使用assert

                                          2.尽量使用const

                                          3.养成良好的编码风格

                                          4.添加必要的注释

                                          5.避免编码陷阱


       6.2、示范


对于上述我们所说coding技巧我们将在下面这个例子进行实现

这里呢我们模拟实现库函数:strcpy

首先呢,我们先看一下这个函数的参数和返回值类型



返回值类型为char*,参数都为char*,const后续会讲到,接下来我们进行实现,先写出主函数如下:


1. //int main()
2. //{
3. // char arr1[] = "hello bit";
4. // char arr2[20] = "xxxxxxxxxxxxx";
5. // printf("%s\n", my_strcpy(arr2, arr1));
6. // return 0;
7. //}


当我们不知道使用const时我们的写法是这样的


1. //char* my_strcpy(char* dest, char * src)
2. //{
3. // char* ret = dest;//由于后面++操做会使dest所指向的地址发生变化,所以这儿提前将首地址存入ret
4. // while (*dest = *src) 
5. // {
6. //      dest++;
7. //      src++;
8. //  }
9. // return ret;//返回ret,也就是最初的dest,方便后续printf打印
10. //}


而这个代码其实我们还可以进行优化,优化如下


1. //char* my_strcpy(char* dest, const char * src)
2. //{
3. // char* ret = dest
4. // while (*dest++ = *src++) //进行了优化
5. //   ;//空语句
6. // return ret;
7. //}


但是呢使用者不知道我们应该怎么给参数,如果使用者一不小心给了一个空指针呢比如下面



我们发现代码并没有报错,还是走了下去,但这是不是我们需要的呢?显然不是,这时候我们就需要使用前面所讲的assert(断言),搭配头文件#include <assert.h>使用

断言(assertion)是一种在程序中的一阶逻辑(如:一个结果为真或假的逻辑判断式),目的为了表示与验证软件开发者预期的结果——当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息

使用如下


1. 
2. char* my_strcpy(char* dest, const char * src)
3. {
4.  //  //断言
5.  assert(dest != NULL);
6.  assert(src != NULL);
7.  char* ret = dest;
8.  while (*dest++ = *src++) //进行了优化
9.    ;//空语句
10.   return ret;
11. }
12. 
13.


我们测试一下看一下使用效果



我们发现它不在不作为,而是给你报出你的代码在什么路径下哪里出现错误,我们这里显示第8行出现错误,而我们地8行接受到了空指针, 所以程序会中止执行,并给出错误信息



当我们搞定这些之后我们又出现了问题, 有个粗心的小伙伴在实现时,把*dest++ = *src++写反了,写成了 *src++=*dest++ ,此时我们发现程序正常运行,但给出的结果时错误的,者我们该怎么解决呢?这时候就需要用到我们接下来所要讲到的const了


       6.3、const的作用


const是一个C语言(ANSI C)的关键字,具有着举足轻重的地位。它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一定帮助。

const修饰的数据类型是指常类型,常类型的变量或对象的值是不能被更新的。

主要作用

1、可以定义const常量,具有不可变性

2、便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。例如: void f(const int i) { …} 编译器就会知道i是一个常量,不允许修改;

3、可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。

4、节省时间和提高效率,编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

我们先简单看一下吧


我们发现在对n进行修改时,编译器进行报错了,因为n不能被更新


如何使用const和理解const int* 和int *const 的区别


以一个简单的代码例子来解释


1. #include <stdio.h>
2. 
3. void test1()
4. {
5.  int n = 10;
6.  int m = 20;
7.  int *p = &n;
8.  *p = 20;
9.  p = &m; 
10. }
11. void test2()
12. {
13.   int n = 10;
14.   int m = 20;
15.   const int* p = &n;
16.   *p = 20;
17.   p = &m; 
18. }
19. void test3()
20. {
21.   int n = 10;
22.   int m = 20;
23.   int *const p = &n;
24.   *p = 20; 
25.   p = &m; 
26. }
27. 
28. int main()
29. {
30.   //测试无cosnt的
31.   test1();
32.   //测试const放在*的左边
33.   test2();
34.   //测试const放在*的右边
35.   test3();
36.   return 0;
37. }


 


我们测试后发现

test1()函数可以正常运行,而test2 ( )和test3()不可以。

test2()中的const int* p = &n;的const的作用是使指针不能改变指向当前地址的值,也就是说,此时n等于10,不能通过指针p来改变n的值。*p=20;此时这句话会报错。

test3()中的int *const p = &n;的const的作用是使指针不能改变当前所指向的地址。也就是说,此时p指向的是n的地址,而不能再改变去指向m的地址。p = &m; 这句话会报错

总结如下


/const 修饰指针的时候
  129 当const 放在*的左边的时候,限制的是指针指向的内容,不能通过指针变量改变指针指向的内容,但是指针变量的本身是可以改变的
  130 当const 放在*的右边的时候,限制的是指针变量本身,指针变量的本身是不能改变的,但是指针指向的内容是可以通过指针来改变的


则my_strcpy()函数完整实现如下



1. //char* my_strcpy(char* dest, const char * src)
2. //{
3. // char* ret = dest;
4. // //断言
5. // assert(dest != NULL);
6. // assert(src != NULL);
7. //
8. // while (*dest++ = *src++) 
9. //   ;//空语句
10. //
11. //  return ret;
12. //}


7、编程常见的错误


       7.1、编译型错误


直接看到错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单



双击错误就可以跳到问题行


       7.2、链接型错误


看错误提示信息,主演是在代码中找到错误信息中的标识符,然后定位问题所在。一般标识符不存在或者拼写错误



如上述拼写错误,我们进行编译后发现


7.3、运行时错误


借助调试,逐步定位问题,最难搞。需要慢慢积累经验。

 

制作不易,一键三连!!一起加油!!!

相关文章
|
2月前
|
存储 NoSQL 安全
【C++调试】深入探索C++调试:从DWARF到堆栈解析
【C++调试】深入探索C++调试:从DWARF到堆栈解析
65 1
|
2月前
|
程序员 编译器 C语言
编程最重要的技术之一 — 调试(以C语言代码为例)
编程最重要的技术之一 — 调试(以C语言代码为例)
42 0
|
2月前
|
存储 编译器 程序员
【调试方法】基于vs环境下的实用调试技巧
【调试方法】基于vs环境下的实用调试技巧
|
5月前
|
C#
C#调试与测试 | DebuggerDisplay使用技巧
DebuggerDisplay可以让你在调试器中显示你自己定义的字符串,代替默认的显示方式。换句话说,它可以让你在调试器中更加方便地查看对象的信息。 当你在调试一个复杂的对象时,往往会发现默认的显示方式不能满足你的需求。这时,你可以使用 DebuggerDisplay 来自定义你想要显示的信息。例如,你可以将一些比较重要的属性或字段的值显示在调试器中,这样你就可以更加方便地了解对象的状态。另外,如果你使用了一些自定义的类,这些类可能没有默认的 ToString 方法,调试器默认的显示方式就会非常简陋,这时你可以使用 DebuggerDisplay 来定义一个更加友好的显示方式。
31 0
|
10月前
|
安全 编译器 程序员
一定要掌握的实用调试技巧(vs2019)(上)
一定要掌握的实用调试技巧(vs2019)(上)
|
10月前
|
NoSQL 程序员 Linux
学C的第十九天【实用调试技巧:1. 调试;2. Windows环境调试介绍;3. 一些调试的实例;4. 一些调试的实例】-1
1. 调试 (1). 调试是什么? 调试(Debugging / Debug),又称出错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程(一系列动作)。
|
10月前
|
程序员 Windows
学C的第十九天【实用调试技巧:1. 调试;2. Windows环境调试介绍;3. 一些调试的实例;4. 一些调试的实例】-2
查看调用堆栈(调用堆栈) 通过调用堆栈,可以清晰地反应函数的调用关系以及当前调用所处的位置。
|
11月前
|
监控 程序员 编译器
代码的调试技巧
代码的调试技巧
|
11月前
|
程序员 C语言 C++
编程最重要的技术—调试(以C语言代码为例)
编程最重要的技术—调试(以C语言代码为例)
|
Java 开发工具 C#
如何以超快的方式调试?
如何以超快的方式调试?