适合初学者的超详细实用调试技巧(下)二

简介: 适合初学者的超详细实用调试技巧

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


6.1 优秀的代码


优秀的代码应该满足以下条件。


1. 代码运行正常


2. bug很少


3. 效率高


4. 可读性高


5. 可维护性高


6. 注释清晰


7. 文档齐全


为了达到这样的条件,我们可以使用以下常见的coding技巧:


1. 使用assert


2. 尽量使用const


3. 养成良好的编码风格


4. 添加必要的注释


5. 避免编码的陷阱。


6.2 示范


我们来模拟实现库函数strcpy:


函数的参数形式char* strcpy(char*destination,const char*source);


该参数说明了strcpy返回类型是char类型的指针,将源头(不能被改)拷贝到目的地。


strcpy特点和strlen类似,遇到‘\0’就停止。


比较容易想到的写法是:


#include<stdio.h>
#include<string.h>
#include<assert.h>
void my_strcpy(char* a, char* b)
{
  while (*a != '\0')
  {
  *b = *a;
  b++;
  a++;
  }
  *b = *a;
}
int main()
{
  char a[] = "abcdef";
  char b[10];
  my_strcpy(a, b);
  printf("%s", b);
  return 0;
}

虽然可以实现strcpy函数的内容,但是优化不怎么样,将简单的功能写的非常麻烦,且无法避免空指针的情况,下面为优化后的写法:


#include<stdio.h>
#include<assert.h>
void my_strcpy(const char a[],char b[])
{
    assert(a!=NULL&&b!=NULL);//断言函数来避免空指针的情况
  while (*b++ = *a++)
  {
  ;//当*a为\0的时候,while里的值为假,跳出循环
  }
}
int main()
{
  char a[] = "abcdef";
  char b[10]="";//定义第二个数组来拷贝数组
  my_strcpy(a,b);
  printf("%s", b);
  return 0;
}


值得注意的是,assert断言函数和const的使用,可以大大增加代码的安全性。


6.3 const的作用


#include <stdio.h>
//代码1
void test1()
{
    int n = 10;
    int m = 20;
    int *p = &n;
    *p = 20;//ok?
    p = &m; //ok?
}
void test2()
{
     //代码2
    int n = 10;
    int m = 20;
    const int* p = &n;
    *p = 20;//ok?
    p = &m; //ok?
}
void test3()
{
    int n = 10;
    int m = 20;
    int *const p = &n;
    *p = 20; //ok?
    p = &m;  //ok?
}
int main()
{
    //测试无cosnt的
   test1();
    //测试const放在*的左边
    test2();
    //测试const放在*的右边
    test3();
    return 0;
}


结论:


const修饰指针变量的时候:


1. const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改 变。但是指针变量本身的内容可变。


2. const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指 针指向的内容,可以通过指针改变。


6.4. const的实例


#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int my_strlen(const char a[])//用const来使代码更加安全
{
  int count = 0;
  while (* a++ != '\0')
  {
  count++;
  }
  return count;
}
int main()
{
  char a[]="abcdef";
  int b = my_strlen(a);
  printf("%d", b);
  return 0;
}

7.编程常见的错误


7.1 编译型错误


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


如:逗号的使用,分号的添加,括号的对应,各类操作符的使用,库函数的使用格式等。


对于这种问题,我们可以直接通过错误列表的提示来定位问题所在,解决问题。


7061185533853e088741a440e88e4c11_5dc86addf01d448ea5327cc1e51023ed.png


7.2 链接型错误


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


如:变量、头文件的包含,文件的引入,常量和宏的定义,库函数名的拼写,自定义函数名的一致等等。


就例如将main写错为mian这样的错误。


38dccda431b394734ba296f0738d6e6e_040f8ff91e8644d2afb2bf5aea558311.png


7.3 运行时错误


借助调试,逐步定位问题。最难搞。


如:栈溢出,逻辑漏洞,未指针的越界访,未初始化的变量,字符串溢出,数组越界,重复释放内存,使用无效的指针等等。


就像上文里的几个例子。


对于这样的问题,可以通过调试来解决。


说了这么多,调试的章节终于结束了!


希望大家都能成为20%的时间在写程序,但是80%的时间在调试的程序员!

264815a6a5f2bb76efd8f5ee9055f1f9_822107916c7b4ff68d081b6c4925d4c7.jpeg


相关文章
|
6月前
|
缓存 前端开发 JavaScript
【第22期】 一文读懂前端调试利器whistle
【第22期】 一文读懂前端调试利器whistle
124 0
|
2月前
|
Go iOS开发 MacOS
学习笔记-代码调试工具
dlv(delve的简称)是一个强大的Go源代码级调试工具,通过控制程序执行、计算变量及提供线程/协程状态等信息,为调试Go程序提供了简单且功能丰富的界面。在macOS上,可以通过`brew install dlv`命令安装。下面是一个使用dlv调试Go代码的例子,展示了如何设置断点并逐步执行代码,检查变量值和函数内部细节。常用命令包括:`b`(打断点)、`p`(打印变量)、`n`(下一步)、`c`(继续执行)、`args`(打印参数)、`locals`(打印局部变量)、`l`(列出代码)、`bp`(显示断点)和`q`(退出)。
43 2
|
5月前
|
存储 编译器 C语言
C语言学习记录——调试技巧(VS2019环境下)
C语言学习记录——调试技巧(VS2019环境下)
53 2
|
5月前
|
Shell 数据安全/隐私保护 开发者
详细解读ApolloGPS调试笔记
详细解读ApolloGPS调试笔记
24 0
|
6月前
|
存储 程序员 编译器
程序员必备技能——调试技巧(二)
程序员必备技能——调试技巧(二)
50 0
|
6月前
|
程序员 Windows
程序员必备技能——调试技巧
程序员必备技能——调试技巧
90 0
|
6月前
|
机器学习/深度学习 图计算 异构计算
|
IDE 程序员 编译器
必备技能:程序的调试
本节内容相对来说比较笼统一些,但是也是非常非常非常重要的一节内容,毕竟一个程序猿不能只会写bug,你写一堆bug谁来修呢?所以通过调试去排查错误的能力是一个优秀的程序猿必须要掌握的技能,而且不能仅仅是掌握,更要精通,本节内容总体来说只有两大部分,一是学习调试的方法,会有举例实际去感受一下,二是在写代码就要考虑的一些问题,养成良好的写代码习惯,防患于未然。
必备技能:程序的调试
|
程序员 编译器 C++
适合初学者的超详细实用调试技巧(上)一
适合初学者的超详细实用调试技巧
85 0
|
编译器
适合初学者的超详细实用调试技巧(下)一
适合初学者的超详细实用调试技巧
73 0