程序员核心------详解调试(2)

news2025/1/23 13:11:05

在这里插入图片描述
所爱隔山海,山海皆可平,所念皆星河,星河不可及。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

上课!

接着上节课讲的调试(1),本节课进一步讲解调试(2).

文章目录


1.调试实例讲解(2)

校招笔试题

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

3.编程常见的错误

实例二:

//代码改错
int main()
{
	int i = 0;
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	for (i = 0; i <=12; i++)
	{
		printf("hehe\n");
		arr[i] = 0;
	}
	return 0;
}

请复制这段代码到本地编译器运行,看看为什么会出现问题。

在这里插入图片描述

  • 运行起来后会发现,死循环打印hehe,为什么呢? 我们调试起来

在这里插入图片描述

  • 会发现一个非常奇怪的现象:i的值和arr[12]的值居然是同步改变的,这到底是为什么呢?
  • 规定:
    1.局部变量的存储是存储于栈区上的
    2.栈区的使用习惯是:
    先使用高地址的空间
    再使用低地址的空间

在内存中大致有下面这三块空间分布,吧栈区放大后,假设顶部是高地址,则底部就是低地址,如图:

在这里插入图片描述

  • 先创建局部变量i,i就使用高地址处的一块空间,

  • 再创建数组arr,arr[0]开始创建低地址处的一块空间。

  • 然后再创建到arr[9]时,i和arr[9]之间恰好有两块整型大小的空间,也就是绿色的空间

  • 随后,arr[12]的空间创建自然而然地就和 i 在同一块空间了

  • 你一定会问,为什么在 i 和arr[9]之间恰好有两块整型大小的空间?不能是三块空间吗?不能是一块空间吗?

  • 声明:以上现象只针对于vs2019 X86 Debug调试环境下产生的现象
    在 VC6.0环境下 i 和arr 之间没有多余的空间
    在gcc环境下 i 和arr之间只有一个整型空间

说那么多东西,只为了说明一件事: 调试的重要性!!调试可以帮助你找到自己的错误!学会调试,你的能力将达到一个质的飞跃。
上节课讲过,Debug和Relese版本的不同
在这个例子中,在不同版本下所出现的情况也不同:
在这里插入图片描述
在Release版本底下,i和 arr[12]所在的地址也不一样了。

  • 到这里,有同学会问到:既然 局部变量i 先创建内存空间,arr后创建
  • 那为什么不先创建arr的内存空间,后创建局部变量i的空间呢?
  • 这样i的内存空间不就在arr下面了吗,这样不就不会死循环了吗?
  • 来演示一下结果:
    在这里插入图片描述
    现在把 i 放在arr之后,也就是 i 的创建是在arr之后的,出现上面的结果
    在这里插入图片描述
    意思就是,数组越界了
    那为什么第一次死循环的时候 , 又没有报这个错误呢?
    因为程序在忙着打印hehe,没空给你报错
    就是这个道理
    程序在忙着做自己的事情打印hehe
    根本没空搭理你,给你报错

    下面来一道题目

- 公司校招笔试题:

  • 在Linux x86_64 gcc环境下,下面的程序会出现什么问题?运行的结果是什么?
int main(int argc,char*argv[])
{
	long i;
	long a[16];
	for (i = 0; i <= 17; i++)
	{
		a[i] = 0;
		printf("%d", i);
	}
	return 0;
}
  • 其实这道题的本质是与上面的例题相同的
  • 只要你写出上面的分析过程,就能拿下这道题
  • 该题运行的现象仍然是会出现死循环

2.如何写出好的代码?

2.1

代码运行正常

bug很少

效率高

可读性高

可维护性高

注释清晰

文档齐全

常见的coding技巧

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

下面以一个例子说明上面所有的要点:

模拟实现my_strcpy的功能

strcpy函数,简单来说就是把一个字符串拷贝到另一个字符串里面

//先观察strcpy的功能
#include<string.h>
int main()
{
	char arr[20] = "xxxxxxxxxxxxxxx";
	char str[] = "hello world";
	strcpy(arr, str);
	printf("%s\n", arr);
}

运行结果如下:
在这里插入图片描述

在c库中,可以搜索到strcpy函数的相关信息:
在这里插入图片描述

  • 现在来模拟my_strcpy函数
void mystrcpy(char* dst, char* src)
{
	while(*dst++=*src++)
	//等价于
	//*dst = *src;
	//dst++,src++;
	{
		;
	}
}

int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
}

  • 我们一般是这么写的
  • 但是这样写还有什么隐患呢?
  • 1.假如传过去的指针是空指针,就会出现问题,
  • 所以我们需要在while前面判断是否为空指针
  if(*dst ==NULL || *stc == NULL)
	 {
 		return ;
	 }

但是这样写,还是会有问题,为什么呢?
1.每次进入my_strcpy函数内部,都要执行if语句,不管它是不是空指针。
2.它不会暴露错误出来,就算是空指针,也会悄悄规避掉,程序员无法知道自己穿的是空指针。
所以,引用 “断言”-----assert
assert内部可以放一个表达式,表达式如果为假,就报错,为真,啥事没有。

仍然用上面的代码,来举例:

void my_strcpy(char* dst,  char* src)
{
	assert(dst !=NULL && src != NULL);//断言
	while (*dst++ = *src++)
	{
		;
	}
}

int main()
{
	char arr1[20] = { 0 };
	char* p = NULL;//p指向的常量字符串无法更改
	my_strcpy(arr1, p);
	//如果是反着来,是无法更改的

	printf("%s\n", arr1);
}

运行结果如下:
在这里插入图片描述

  • 它不仅能报错,还指出是哪个文件目录下的错误
  • 这就是(断言) assert 的好处

假设有一个程序员,将

void my_strcpy(char* dst, char* src)
{
	assert(dst && src);
	while(*dst++ = *src++ )
	{
		;
	}
}

int main()
{
	char arr1[20] = { 0 };
	char* p = "hello";//p指向的常量字符串无法更改
	my_strcpy(arr1, p);
	printf("%s\n", arr1);
}

写成了

while(*src++ =*dst++ )
{
	;
} 

也就是在while循环里面,将两个指针位置互换了

这样写一定会有问题,那么会是什么问题?
在这里插入图片描述

可以知道,arr里面只有\0,把\0 复制到src的第一个位置后
循环马上停下了,输出的也就是arr1里面的内容,即\0

如何避免呢?-----const
先来看一个例子,
在这里插入图片描述

  • 可以通过指针修改 n 内部的值。
  • 但是当我在指针变量前面加上了 const后 , 结果如下:

在这里插入图片描述
它说,左值是必须可修改的,这就意味着,加上了const 后,指针指向的内容不可更改

再来看一个例子:
在这里插入图片描述
看看有什么不同?
在这个例子中,我把const 放在了 * 的后面,这时候 p不能更改了。

总结:
const修饰指针变量的时候
1.const放在的左边,修饰的是指针指向的内容
表示指针指向的内容,不能通过指针来改变
*

2.也可以放在的右边,const修饰的是指针变量本身
表示指针变量本身的内容不能被修改,但是指针指向的内容,
可以通过指针来改变

回到上面的例子,所以当我写成
const char* src的时候,就算写反了位置,也没关系
因为这样写的时候

void my_strcpy(char* dst, const char* src)
{
	assert(dst && src);
while(*src++ = *dst++ )
{
	;
}
}

int main()
{
	char arr1[20] = { 0 };
	char* p = hello;
	my_strcpy(arr1, p);
	printf("%s\n", arr1);
}
  • 编译器都报错了,更别说运行起来了

在这里插入图片描述

看到这里,细心的朋友还会发现,
在这里插入图片描述

这里是char *类型的返回值呀,为什么上面写的都是void类型呢?
别着急,这就马上讲

char* my_strcpy(char* dst, const char* src)
{
	assert(dst && src);
	char* ret = dst;//存储dst的起始位置
	while(*src++ = *dst++ )
	{
		;
	}
	return ret ;
	//这里不能返回dst,因为dst指向的空间不再是起始位置的地址了
}

int main()
{
	char arr1[20] = { 0 };
	char* p = hello;
	my_strcpy(arr1, p);
	printf("%s\n", arr1);
}

大功告成!
总结:

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

3.编程常见的错误

3.1编译型错误

  • 直接看错误信息提示(双击错误行),或者根据经验直接判断,相对比较简单

下面这个例子就是把英文的小括号写成了中文的小括号 在这里插入图片描述

3.2链接型错误
下面是一个链接型错误,链接型错误中,双击错误行是没有什么反应的,看行数也没什么效果,解决办法就是 “搜索”
在这里插入图片描述
Ctrl +F5 打开搜索框进行搜索
在这里插入图片描述
下面是最后一个,也是最难的一个
3.3运行时错误

  • 运行时错误,也是最难解决的一个错误,

  • 就需要用到调试,上面讲的第一个例子

  • 就是运行时错误产生的,

  • 调试可以说是专门为了解决这个错误而产生的。

  • 今天的内容就到这里。

    总结:调试是重中之重,万事开头难,当你勇敢地迈出第一步,就成功了一半。
    下课在这里插入图片描述



————— END —————




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91535.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【大数据技术Hadoop+Spark】MapReduce概要、思想、编程模型组件、工作原理详解(超详细)

MapReduce是Hadoop系统核心组件之一&#xff0c;它是一种可用于大数据并行处理的计算模型、框架和平台&#xff0c;主要解决海量数据的计算&#xff0c;是目前分布式计算模型中应用较为广泛的一种。 一、MapReduce核心思想 MapReduce的核心思想是“分而治之”。所谓“分而治之…

猿人学APP第一题

抓包分析 copy CURL 转 requests代码 def app1():import requestsheaders {"Accept-Language": "zh-CN,zh;q0.8","User-Agent": "Mozilla/5.0 (Linux; U; Android 8.1.0; zh-cn; Nexus 6P Build/OPM1.171019.011) AppleWebKit/534.30 (K…

KT6368A蓝牙芯片的MTU的说明以及如何加快BLE传输速率

目录 一、蓝牙MTU的简介 二、详细的方法说明以及测试 三、KT6368A提升ble传输速率方法 BLE传输带宽主要跟两个要素有关&#xff1a; 通信周期和每个通信点可传输的数据量。 1.通信周期&#xff1a; 安卓手机一般可支持到10ms&#xff0c;苹果一般可支持到15ms 2.每个通信点…

[附源码]Node.js计算机毕业设计高校实习管理平台系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

菁染料CY3-聚乙二醇-二苯并环辛炔 CY3-PEG-DBCO

产品名称&#xff1a;菁染料CY3-聚乙二醇-二苯并环辛炔 英文名称&#xff1a; CY3-PEG-DBCO 分子量&#xff1a;1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k&#xff08;可按需定制&#xff09; 质量控制&#xff1a;95% 原料分散系…

SSM校园疫情防控

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 管理员功能&#xff1a; 教职工管理&#xff08;管理已注册的教职工&#xff09; 出入管理&#xff08;出入日期、人员姓…

若依缓存使用浅析

配置 这块主要涉及两个类 FastJson2JsonRedisSerializer : 继承 RedisSerializer 接口自定义使用 fastjson 进行序列化和反序列化RedisConfig&#xff1a;配置使用 StringRedisSerializer 来进行key的序列化与反序列&#xff0c;使用刚才我们 FastJson2JsonRedisSerializer 来…

通过WSL2运行GUI程序

上次我写过一篇VGPU和WSL2&#xff0c;这回我打算写一篇关于《通过WSL2运行GUI程序》的笔记。 起因 总所周知&#xff0c;KVM in Linux支持GPU passthrough(GPU直通)功能的&#xff0c;就是配置稍稍多了一点。最初想试试如何在Hyper-V中尝试安装VM&#xff0c;但是vgpu的文档…

2022-12-14 jedis

jedis 简介 编程语言与redis java语言连接redis服务 jedis SpringDataRedis Lettuce HelloWorld 客户端连接redis maven下载安装 配置maven环境变量 库的种类和彼此的关系 maven标准目录结构 核心代码部分配置文件部分测试代码部分测试配置文件 maven常用的命令 mvn…

nodejs+vue083新生报到服务管理系统

目 录 摘 要 I 目 录 III 第一章 概述 1 1.1研究背景 1 1.2 开发意义 1 1.3 研究现状 1 1.4 研究内容 2 1.5 论文结构 2 第二章 开发技术介绍 1 2.5 B/S架构 2 第三章 系统分析 1 3.1 可行性分析 1 3.1.1技术可行性 1 3.1.2操作可…

python绘制圣诞树、烟花、爱心及节日倒计时

说明&#xff1a; 该博客主要完成以下几个方面的功能: 1.元旦节日倒计时代码的实现 2.使用python源码“绘制圣诞树” 3.使用python绘制“跨年烟花” 4.使用python 绘制“爱心” 呈现方式&#xff1a;代码和截图 1.元旦倒计时代码&#xff1a; 输入相应的节日时间&#…

Seata - @GlobalTransactional源码解析

脑图 核心 Seata三大角色 TC &#xff1a;事务协调者&#xff0c;netty server(服务器)TM &#xff1a;事务管理器&#xff0c;netty client(客户端)RM&#xff1a; 资源管理器&#xff0c;netty client(客户端) GlobalTransactional(name "fsp-create-order" ro…

【静脉检测】手指静脉图像检测【含Matlab源码 1654期】

⛄一、简介 手指静脉识别系统的性能非常依赖于采集图像的质量,但是采集设备在成像和传输时产生的各类噪声,以及开放式使用场景下设备镜面上存在脏污、用户手指存在蜕皮情况等因素都会对图像质量造成极大的影响,增大后续特征提取的难度,最终影响整个系统的识别性能。针对目前现…

Zabbix 6.2 监控 PostgreSQL13 数据库

Zabbix 6.2 监控 PostgreSQL13 数据库 文章目录Zabbix 6.2 监控 PostgreSQL13 数据库官方模版地址1.pgsql新建监控用户2. 编辑 pg_hba.conf 文件并重启3.拷贝监控脚本到var/lib/zabbix下4.zabbix监控导入模版5.主机配置模版6.验证监控数据官方模版地址 https://git.zabbix.com…

一文带你读懂何为 macOS App 公证,以及如何自动化实现

前言 在上篇文章「macOS App 自动化分发 App Store 探索与实践」中讲解了如何通过 Shell 脚本实现 macOS App 自动化分发 App Store。相信&#xff0c;看过的同学都或多或少对 macOS App 构建、分发 App Store 相关的知识都具备了一定的认知。 而对于开发者来说&#xff0c;我…

RCE(远程代码/命令执行漏洞)原理及靶场练习

目录 PHP-RCE涉及函数 基础命令符 靶场练习 PHP-RCE涉及函数 代码注入 eval() 把字符串 code 作为PHP代码执行 assert() 检查一个断言是否为 false preg_replace() 执行一个正则表达式的搜索和替换 create_function() 创建一个匿名函数并且返回函数名创 call_user_func()/ca…

Android进程启动流程

一.Android 系统架构图 ​虽然 Android 系统非常庞大且错综复杂&#xff0c;但整体架构设计清晰。Android 底层内核空间以 Linux Kernel 作为基石&#xff0c;上层用户空间由 Native系统库、虚拟机运行环境、框架层组成&#xff0c;通过系统调用(Syscall)连通系统的内核空间 与…

浅谈hudi 的callback回调机制

浅谈hudi 的callback回调机制 关于hudi的write operations,hudi有4种类型,分别为upsert/insert/bulk_insert/delete[软删除/硬删除]。 了解hudi的都知道,hudi有一个核心的机制就是timeline,hudi的instantDTO包含action(动作),ts(时间),state(状态)。 action主要包括: commits…

Linux常用命令总结(建议收藏)

文章目录一、文件管理1、cat&#xff1a;查看文件内容案例1&#xff1a;输出内容行数2、chmod&#xff1a;是控制用户对文件的权限的命令案例1&#xff1a;&#xff1a;将user文件修改成用户、组、其他用户都可以读写可执行的权限3、diff&#xff1a;用于比较文件的差异4、find…

ELK (一)部署ELK+Filebeat日志收集分析系统

说明&#xff1a;此安装流程只适用于8.0.0以下的版本 1. ElasticSearch 部署 1.1 下载ElasticSearch的wget指令&#xff1a; wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.13.4-linux-x86_64.tar.gz1.2 解压安装包到指定目录 指定解压缩到 …