实用调试技巧(2)

简介: 实用调试技巧(2)


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

6.1 优秀的代码:

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

常见的coding技巧:

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

6.2 示范:

模拟实现库函数:strcpy

我们先来看一下strcpy是如何使用的:

#include <stdio.h>
#include <string.h>
int main()
{
  char arr1[] = "hello bit";
  char arr2[20] = "xxxxxxxxxxxxx";
  //strcpy(arr2, arr1);
  //printf("%s\n", arr2);
  printf("%s\n", strcpy(arr2, arr1));
  return 0;
}

接下来我们来实现它:

#include <stdio.h>
void my_strcpy(char* dest, char* src)
{
  
  while (*src != '\0')
  {
    *dest = *src;
    dest++;
    src++;
  }
  *dest = *src;// \0 的拷贝
}
int main()
{
  char arr1[] = "hello bit";
  char arr2[20] = "xxxxxxxxxxxxx";
  my_strcpy(arr2, arr1);
  printf("%s\n", arr2);
  
  return 0;
}

我们还可以使用assert对它进行优化:

#include <stdio.h>
#include <assert.h>
void my_strcpy(char* dest, char* src)
{
  //断言
  assert(dest != NULL);
  assert(src != NULL);
  while (*src != '\0')
  {
    *dest = *src;
    dest++;
    src++;
  }
  *dest = *src;// \0 的拷贝
}
int main()
{
  char arr1[] = "hello bit";
  char arr2[20] = "xxxxxxxxxxxxx";
  char* p = NULL;
  //my_strcpy(p, arr1);
  my_strcpy(arr2, arr1);
  printf("%s\n", arr2);
  return 0;
}

通过assert,我们可以确保某些事情不会发生,一旦发生,它就会报错,这样就能方便我们快速找到错误。

我们还可以将字符和\0的拷贝放到一起:

#include <stdio.h>
#include <assert.h>
void my_strcpy(char* dest, char* src)
{
  //断言
  assert(dest != NULL);
  assert(src != NULL);
  while (*dest = *src)//赋值表达式,比如把h赋给*dest,表达式的结果就是h的ASCII码值
  {
    dest++;
    src++;
  }
  
}
int main()
{
  char arr1[] = "hello bit";
  char arr2[20] = "xxxxxxxxxxxxx";
  char* p = NULL;
  //my_strcpy(p, arr1);
  my_strcpy(arr2, arr1);
  printf("%s\n", arr2);
  return 0;
}

还可以这样写:

#include <stdio.h>
#include <assert.h>
void my_strcpy(char* dest, char* src)
{
  //断言
  assert(dest != NULL);
  assert(src != NULL);
  while (*dest++ = *src++)//赋值表达式,比如把h赋给*dest,表达式的结果就是h的ASCII码值
  {
    ;//空语句
  }
  
}
int main()
{
  char arr1[] = "hello bit";
  char arr2[20] = "xxxxxxxxxxxxx";
  char* p = NULL;
  //my_strcpy(p, arr1);
  my_strcpy(arr2, arr1);
  printf("%s\n", arr2);
  return 0;
}

此外,我们还可以对返回类型进行优化:

#include <stdio.h>
#include <assert.h>
//函数返回的是目标空间的起始地址
char* my_strcpy(char* dest, char* src)
{
  char* ret = dest;
  //断言
  assert(dest != NULL);
  assert(src != NULL);
  while (*dest++ = *src++)// 赋值表达式,比如把h赋给*dest,表达式的结果就是h的ASCII码值
  {
    ;//空语句
  }
  return ret;
}
int main()
{
  char arr1[] = "hello bit";
  char arr2[20] = "xxxxxxxxxxxxx";
  char* p = NULL;
  //my_strcpy(p, arr1);
  //my_strcpy(arr2, arr1);
  //printf("%s\n", arr2);
  printf("%s\n", my_strcpy(arr2, arr1));
  return 0;
}

另外,为了保证传进去的arr1不被修改,我们还可以加上const进行修饰:

#include <stdio.h>
#include <assert.h>
//函数返回的是目标空间的起始地址
char* my_strcpy(char* dest, const char* src)
{
  char* ret = dest;
  //断言
  assert(dest != NULL);
  assert(src != NULL);
  while (*dest++ = *src++)// 赋值表达式,比如把h赋给*dest,表达式的结果就是h的ASCII码值
  {
    ;//空语句
  }
  return ret;
}
int main()
{
  char arr1[] = "hello bit";
  char arr2[20] = "xxxxxxxxxxxxx";
  char* p = NULL;
  //my_strcpy(p, arr1);
  //my_strcpy(arr2, arr1);
  //printf("%s\n", arr2);
  printf("%s\n", my_strcpy(arr2, arr1));
  return 0;
}

6.3 const的作用

有以下两种方式可以修改num的值:

int main()
{
  int num = 10;
  num = 20;
  int* p = &num;
  *p = 200;
  
  return 0;
}

现在我们加上const:

#include <stdio.h>
int main()
{
  const int n = 100;
  //n = 200;//err
  int* p = &n;
  *p = 20;
  printf("%d\n", n);
  
  return 0;
}

加上const是为了不让n的值发生变化,但是现在我们却可以通过地址的方式来改变它,于是我们可以进行以下操作:

int main()
{
  const int n = 100;
  //n = 200;//err
  //int* p = &n;
  //*p = 20;
  //printf("%d\n", n);
  const int* p = &n;
  //*p = 20;//err
  return 0;
}

通过以上代码我们可以发现const是可以修饰指针的:

//const 修饰指针的时候
//当const 放在*的左边的时候,限制的是指针指向的内容,不能通过指针变量改变指针指向的内容,但是指针变量本身是可以改变的
//当const 放在*的右边的时候,限制的是指针变量本身,指针变量本身是不能改变的,但是指针指向的内容是可以通过指针来改变的
#include <stdio.h>
int main()
{
  int m = 10;
  int n = 100;
  //const可以修饰指针
  const int* p = &m;
  //*p = 0;//err
  p = &n;//ok
  printf("%d\n", m);
  return 0;
}
#include <stdio.h>
int main()
{
  int m = 10;
  int n = 100;
  //const可以修饰指针
  int* const p = &m;
  *p = 0;//ok
  //p = &n;//err
  printf("%d\n", m);
  return 0;
}
#include <stdio.h>
int main()
{
  int m = 10;
  int n = 100;
  //const可以修饰指针
  const int* const p = &m;
  //*p = 0;//err
  //p = &n;//err
  printf("%d\n", m);
  return 0;
}

练习:

模拟实现一个strlen函数

//模拟实现一个strlen函数
//assert
//const
//size_t 是专门为sizeof设计的一个类型
//size_t --> unsigned int / unsigned long
//>=0
#include <stdio.h>
#include <assert.h>
size_t my_strlen(const char* str)
{
  assert(str != NULL);
  size_t count = 0;
  while (*str != '\0')
  {
    count++;
    str++;
  }
  return count;
}
int main()
{
  char arr[] = "abc";
  size_t len = my_strlen(arr);
  printf("%zd\n", len);//zd是专门用来打印size_t类型的值的
  return 0;
}
//%u 无符号整数的
• 36

7. 编程常见的错误

7.1 编译型错误

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

int main()
{
  int a = 10//编译期间找到的一般都是语法问题
  return 0;
}

7.2 链接型错误

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

//链接型错误是在链接期间发现的错误
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  int ret = add(2, 3);
  return 0;
}

7.3 运行时错误

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

#include <stdio.h>
int Add(int x, int y)
{
  return x - y;
}
int main()
{
  int ret = Add(2, 3);
  printf("%d\n", ret);
  return 0;
}


目录
相关文章
|
8月前
|
存储 人工智能 JSON
RAG Logger:专为检索增强生成(RAG)应用设计的开源日志工具,支持查询跟踪、性能监控
RAG Logger 是一款专为检索增强生成(RAG)应用设计的开源日志工具,支持查询跟踪、检索结果记录、LLM 交互记录和性能监控等功能。
368 7
RAG Logger:专为检索增强生成(RAG)应用设计的开源日志工具,支持查询跟踪、性能监控
|
9月前
|
存储 传感器 人工智能
「AI实践派」产品生态伙伴Zilliz联合活动
阿里云与向量搜索领域明星企业Zilliz将在杭州阿里巴巴西溪园区共同举办“中外AI产品应用实践和出海实战”分享沙龙。
|
12月前
|
存储 NoSQL 关系型数据库
数据存储架构的演进和实操(一)
数据存储架构的演进和实操(一)
|
SQL 数据库
【SQL】已解决:SQL分组去重并合并相同数据
【SQL】已解决:SQL分组去重并合并相同数据
289 1
|
存储 算法 Java
JVM组成结构详解:类加载、运行时数据区、执行引擎与垃圾收集器的协同工作
【8月更文挑战第25天】Java虚拟机(JVM)是Java平台的核心,它使Java程序能在任何支持JVM的平台上运行。JVM包含复杂的结构,如类加载子系统、运行时数据区、执行引擎、本地库接口和垃圾收集器。例如,当运行含有第三方库的程序时,类加载子系统会加载必要的.class文件;运行时数据区管理程序数据,如对象实例存储在堆中;执行引擎执行字节码;本地库接口允许Java调用本地应用程序;垃圾收集器则负责清理不再使用的对象,防止内存泄漏。这些组件协同工作,确保了Java程序的高效运行。
158 3
|
存储 机器学习/深度学习 人工智能
香橙派——雕琢智能时代的瑰宝为AI而生(一)
香橙派——雕琢智能时代的瑰宝为AI而生(一)
396 0
|
机器学习/深度学习 人工智能 自然语言处理
AI初探:人工智能的定义、历史与未来展望
【7月更文第15天】在科技飞速发展的今天,人工智能(Artificial Intelligence, AI)已经成为推动社会进步的关键力量,渗透到我们生活的方方面面,从智能家居到自动驾驶汽车,从精准医疗到智能金融,无不展现出其深远的影响。本文旨在为读者揭开人工智能的神秘面纱,从基本概念出发,回顾其发展历程,并探索未来的无限可能。
1740 2
|
Java 程序员 C++
【Java】Java与C++:比较与对比
【Java】Java与C++:比较与对比
415 0
|
存储 缓存 Linux
探秘Linux块设备驱动程序:成为内核开发大师的第一步
探秘Linux块设备驱动程序:成为内核开发大师的第一步
999 1
|
Java
Gradle笔记 八 Gradle 插件(一)
Gradle笔记 八 Gradle 插件
337 0