当前位置: 首页 > news >正文

51单片机教程(五)- LED灯闪烁

1 项目分析

  • 让输入/输出口的P1.0或P1.0~P1.7连接的LED灯闪烁。

2 技术准备

1、C语言知识点

1 运算符

1 算术运算符 在这里插入图片描述
#include <stdio.h>int main(){// 算术运算符int a = 13;int b = 6;printf("%d\n", a+b); printf("%d\n", a-b); printf("%d\n", a*b); printf("%d\n", a/b); printf("%d\n", a%b); // 后续应用:将数的十位、个位取出来,分别记录下来用于显示在不同的数码管printf("1: %d\n", a/10);printf("3: %d\n", a%10);return 0;
}

2 自增/自减运算符
  • 自增运算符为++,其功能是使变量的值自增1

  • 自减运算符为--,其功能是使变量值自减1。
    在这里插入图片描述

#include <stdio.h>int main(){// 自增、自减运算符 --》 计数int a = 10;printf("%d\n", a++);   // ++ 在后,表示先用后加 ++a;   // ++ 在前,表示用先加后用 printf("%d\n", a);return 0;
}

3 关系运算符

在这里插入图片描述

  • 关系表达式的值是,在C程序用整数10表示。
#include <stdio.h>int main(){// 关系运算符  --> if语句中使用 int a = 13;int b = 6;printf("%d\n", a>=b);  printf("%d\n", a==b);  printf("%d\n", a!=b);return 0;
}

5 逻辑运算符

在这里插入图片描述

#include <stdio.h>int main(){// 逻辑运算符  --> if语句中使用:条件关系的判断 int mathScore = 90;int englishScore = 60;int stu1_mathScore = 99;int stu1_englishScore  = 66;printf("%d\n", (stu1_mathScore > 60) && ();  printf("%d\n", a||b);  printf("%d\n", !b);return 0;
}

6 位运算符和位表达式

在这里插入图片描述

#include <stdio.h>int main()
{int a = 10;   // 二进制为:1010int b = 6;    // 二进制为:0110printf("&:%d\n", a & b);    // 2   --> 0010printf("|:%d\n", a | b);    // 14  --> 1110// int在计算机中是以补码形式存储printf("~:%d\n", ~a);       // -11  --> 0101 (32位:0101,最高位为1表示为负,补码:取反后 +1)printf("^:%d\n", a ^ b);     // 1100 --> 12printf("<<: %d\n", a << 2);   // 1010 00 --> 40   低位补0,高位丢失printf(">>: %d\n", b >> 3);   // 0000 00 --> 0    高位补0,低位丢失return 0; 
}

在这里插入图片描述


7 赋值运算符
  • 格式

    • 变量名 = 表达式;
#include <stdio.h>int main()
{int number = 10;   char letter = 'a';printf("int:%d\n", a); printf("char:%c\n", letter); return 0; 
}

8 复合赋值运算符
  • 将变量计算的结果重新赋值给该变量
    在这里插入图片描述
#include <stdio.h>int main()
{int a = 13;int b = 6;//a = a +b;a += b; printf("%d\n", a); return 0; 
}

9 三目运算符
表达式1 ? 表达式2 : 表达式3;
#include <stdio.h>int main()
{int a = 10;   int b = 6;  printf("结果为:%d", a > b ? a : b);    // a 大于 b,返回a,否则返回breturn 0; 
}

2 选择与循环语句

1 if 语句

if(表达式)
语句;if(表达式){语句列表1
}
else{语句列表2}//多分支
if(表达式1){语句列表1}
else if(表达式2){语句列表2}
else{语句列表3}
#include <stdio.h>int main()
{// if 语句 --> 存在多个范围判断 int a = 4;if(a > 0){printf("a 是正数");} else if(a < 0){printf("a 是负数");} else{printf("a 是零");}return 0; 
}

2 switch语句

  • 使用:用于多分支的情况,可以简化多重if语句嵌套的情况。
switch(表达式A){case  常量表达式1:语句1;break;case  常量表达式2:语句2;break;……case  常量表达式n:语句n;break;default:语句n+1; break;
} 
#include <stdio.h>int main()
{// switch-case --> 对变量的有限个取值判断 int a = 1;switch(a){case 1:// 无break:case穿透 printf("选项1");case 2:printf("选项2");break;case 3:printf("选项3");break;default:printf("无效选择");} return 0; 
}

3 while语句

while(表达式){循环语句;
}
#include <stdio.h>int main()
{// while循环 --> 已知或未知循环次数 int a = 1;while(a < 6){printf("%d\n", a);a++;} return 0; 
}

4 do-while语句

do{循环语句;
}while(表达式);

5 for语句

for(表达式1;表达式2;表达式3){循环语句;
}
#include <stdio.h>int main()
{// while循环 --> 已知或未知循环次数 int a = 1;while(a < 6){printf("%d\n", a);a++;} return 0; 
}

6 死循环

// 死循环 --> 主要用于未知循环次数
while(1){} for(;;){}

3 函数

1)函数的定义

void 函数名()
{语句块;
}

2)函数的返回

类型 函数名(形参1,形参2,形参3,……)
{……return 数据;
}

3)形式参数与实际参数。

void(类型形参1,类型形参2,……)
{
}函数名(实参1;实参2;实参3;……);

4)函数的调用方式

函数名();
#include <stdio.h>// 函数定义位置
// 1 在main函数之前
int max(int a, int b){if(a >b){return a;} else{return b;}
} int main()
{// 调用函数int c=max(4, 7); printf("最大值:%d\n", c);int d=max1(40, 7); printf("最大值:%d\n", d); return 0; 
}// 2 在main函数之后 
int max1(int a, int b){if(a >b){return a;} else{return b;}
} 

4 reg52.h

  • 介绍

    • 用于80C52和80C32微控制器的通用头文件。
    • 其中定义了各种特殊字节寄存器,如P0口、P1口、P2口、P3口、程序状态字寄存器、累加器、B特殊寄存器、堆栈指针寄存器和数字指针(低位/高位)等。
    • 该头文件还定义了一些与定时器/计数器和电源控制相关的寄存器。
  • 导入

    #include "reg52.h"
    
  • 区别:引入方式 “” 与 <>

    • “”
      • 使用双引号来包含头文件时,编译器会首先在当前文件所在的目录下搜索指定的头文件
      • 如果在这个目录下找不到文件,编译器会继续在编译器的标准库路径中查找。
      • 这种机制允许开发者包含项目中自定义的头文件。
    • <>
      • 当使用尖括号时,编译器只在编译器的标准库路径中查找指定的文件。
      • 意味着尖括号主要用于包含标准库头文件,如 <stdio.h><stdlib.h> 等。
  • 内容

    • 点击到 “reg52.h” 处,右击选择 Open Document <reg52.h>

    在这里插入图片描述

3 项目实现

实验1 点亮LED

  • 基本结构:main.c 文件

    #include "reg52.h"sbit LED = P1^4;    void main()
    {while (1){LED = 0;     // 点亮LED1}
    }
    
    • 注意:一个程序里有且只有一个main函数。
  • 释义

    • **sbit LED = P0^4; ** 定义P0.4引脚赋值给变量LED(sbit 是定义特殊功能寄存器的位变量)。

实验2 实现LED闪烁

  • 延迟函数

    void Delay(unsigned int t)
    {while(--t);
    }
    
  • 说明

    • 通过一个空 while 循环来延迟执行,让CPU在循环中忙等待,直到变量 t 的值从传入的值递减到0。
    • 大约是 0.1 毫秒(ms)或 0.0001 秒。
  • 缺点

    1. 效率低下:CPU在这段时间内无法执行其他任务,导致资源浪费。
    2. 不可预测性:延迟的时间长度高度依赖于执行代码的CPU的速度。在更快的CPU上,同样的循环次数会更快地完成,导致延迟时间更短。
    3. 不可移植性:由于延迟时间依赖于CPU速度,因此代码在不同硬件上的表现可能会有很大差异。
  • 代码

    #include <reg52.h>  //头文件包含特殊功能寄存器的定义sbit LED0=P1^0;  // 用sbit 关键字 定义 LED到P1.0端口,void Delay(unsigned int t); //函数声明/*------------------------------------------------主函数
    ------------------------------------------------*/
    void main (void)
    {while (1)         //主循环{LED0=0;            //将P1.0口赋值 0,对外输出低电平Delay(10000);      //调用延时程序;更改延时数字可以更改延时长度;LED0=1;            //将P1.0口赋值 1,对外输出高电平Delay(10000);}
    }/*------------------------------------------------延时函数,含有输入参数 unsigned int t,无返回值unsigned int 是定义无符号整形变量,其值的范围是0~65535
    ------------------------------------------------*/
    void Delay(unsigned int t)
    {while(--t);
    }
    

  • 方式2:for循环延迟

    #include "reg52.h"sbit LED = P1^4;    
    void delay();void main()
    {while (1){LED = 0;     // 点亮LED1delay();      // 延时1sLED = 1;     // 熄灭LED1delay();      // 延时1s        }
    }
    
  • 实现delay函数

    void delay()
    {unsigned int i, j;for(i=0; i<183; i++){for(j=0; j<1000;j++);   }
    }   
    

实验3 LED灯先快闪,再慢闪,循环闪烁

#include<reg52.h> //头文件包含特殊功能寄存器的定义sbit LED0 = P1 ^ 0; // 用sbit 关键字 定义 LED到P1.0端口,void Delay(unsigned int t); //函数声明/*------------------------------------------------主函数
------------------------------------------------*/
void main()
{unsigned char i;  //定义一个无符号字符型局部变量 i 取值范围 0~255while (1) {       //主循环for (i = 0; i < 10; i++) { //加入 for循环,表明for循环大括号中的程序循环执行10次LED0 = 0;          //将P1.0口赋值 0,对外输出低电平Delay(5000);       //调用延时程序;更改延时数字可以更改延时长度;LED0 = 1;          //将P1.0口赋值 1,对外输出高电平Delay(5000);}for (i = 0; i < 10; i++) {LED0 = 0;          //将P1.0口赋值 0,对外输出低电平Delay(60000);      //用于改变闪烁频率LED0 = 1;          //将P1.0口赋值 1,对外输出高电平Delay(60000);}}
}/*------------------------------------------------延时函数,含有输入参数 unsigned int t,无返回值unsigned int 是定义无符号整形变量,其值的范围是0~65535
------------------------------------------------*/
void Delay(unsigned int t)
{while (--t);
}

实验4 不同频率让多个LED灯闪烁

#include<reg52.h>sbit LED0 = P1 ^ 0;
sbit LED1 = P1 ^ 1;
sbit LED2 = P1 ^ 2;
sbit LED3 = P1 ^ 3;
sbit LED4 = P1 ^ 4;
sbit LED5 = P1 ^ 5;
sbit LED6 = P1 ^ 6;
sbit LED7 = P1 ^ 7;void Delay(unsigned int t); //函数声明/*------------------------------------------------主函数
------------------------------------------------*/
void main()
{unsigned char i;  //定义一个无符号字符型局部变量 i 取值范围 0~255while (1) {for (i = 0; i < 10; i++) { //程序循环执行10次LED0 = 0;          //将P1.0口赋值 0,对外输出低电平LED2 = 1;LED4 = 0;Delay(5000);LED0 = 1;         //将P1.0口赋值1,对外输出高电平LED2 = 0;LED4 = 1;Delay(5000);}for (i = 0; i < 10; i++) {LED0 = 0;          //将P1.0口赋值 0,对外输出低电平LED2 = 1;LED4 = 0;Delay(60000);LED0 = 1;          //将P1.0口赋值 1,对外输出高电平LED2 = 0;LED4 = 1;Delay(60000);}}
}/*------------------------------------------------延时函数,含有输入参数 unsigned int t,无返回值unsigned int 是定义无符号整形变量,其值的范围是 0~65535
------------------------------------------------*/
void Delay(unsigned int t)
{while (--t);
}

4 格式化代码

  1. 下载并安装AStyle

    • 访问AStyle的官方网站(https://astyle.sourceforge.net/)下载最新版本的AStyle。

      在这里插入图片描述

    • 解压下载的文件,并记住解压后的路径,特别是Astyle.exe文件的路径。

      在这里插入图片描述

  2. 在Keil 5中加载AStyle

    • 打开Keil 5软件,选择Tools -> Customize Tools Menu...,点击“新建”按钮,创建一个新的菜单项,命名为“formatCode”,

    • “Command” 栏中选择上述解压的绝对路径(选择到 astyle.exe)

    • “Arguments” 栏中输入:!E --style=1tbs --indent-col1-comments --break-blocks --pad-oper --pad-comma --pad-header --unpad-paren --delete-empty-lines --align-pointer=name --break-one-line-headers --add-braces --max-code-length=120

    • 点击“OK”保存设置。

      在这里插入图片描述

  3. 设置 代码格式化 快捷键

    • 点击菜单栏中的 【Edit】选择【 Configuration…】,切换到【Shortcut Keys】,在 Select a Command 中找到:【Tools: formatCode】,点击 【Create Shortcut】,设置快捷键(按下对应的按键,如:Ctrl + Q),点击【OK】

    在这里插入图片描述

相关文章:

51单片机教程(五)- LED灯闪烁

1 项目分析 让输入/输出口的P1.0或P1.0~P1.7连接的LED灯闪烁。 2 技术准备 1、C语言知识点 1 运算符 1 算术运算符 #include <stdio.h>int main(){// 算术运算符int a 13;int b 6;printf("%d\n", ab); printf("%d\n", a-b); printf("%…...

VUE3中Element table表头动态展示合计信息(不是表尾合计)

一、背景 原型上需要对两个字段动态合计&#xff0c;输出摘要信息 原先想到是的Element的 :summary-method&#xff0c;发现不是动态&#xff0c;所以换监听来实现 二、vue代码 <el-table v-model"loading" :data"itemList"><el-table-column la…...

git重置的四种类型(Git Reset)

git区域概念 1.工作区:IDEA中红色显示文件为工作区中的文件 (还未使用git add命令加入暂存区) 2.暂存区:IDEA中绿色(本次还未提交的新增的文件显示为绿色)或者蓝色(本次修改的之前版本提交的文件但本次还未提交的文件显示为蓝色)显示的文件为暂存区中的文件&#xff08;使用了…...

【Java集合面试1】说说Java中的HashMap原理?

Java中的HashMap是一种基于哈希表的Map接口实现&#xff0c;它存储的内容是键值对&#xff08;key-value&#xff09;映射。HashMap允许空键&#xff08;null&#xff09;和空值&#xff08;null&#xff09;&#xff0c;并且它的键值对没有顺序。以下是HashMap的一些关键工作原…...

万字长文解读机器学习——决策树

&#x1f33a;历史文章列表&#x1f33a; 机器学习——损失函数、代价函数、KL散度机器学习——特征工程、正则化、强化学习机器学习——常见算法汇总机器学习——感知机、MLP、SVM机器学习——KNN机器学习——贝叶斯机器学习——决策树机器学习——随机森林、Bagging、Boostin…...

内网环境,基于k8s docer 自动发包

背景&#xff1a;生产环境是内网&#xff0c;无法连接外部git环境&#xff0c;需要上传tar包打成镜像&#xff0c;然后发布。 简单写了个脚本&#xff0c;记录下方便复用。 将tar包和脚本拷贝到同一个目录下。 使用方式&#xff1a; tar 包名称格式&#xff1a;服务名-版本号…...

【HCIP园区网综合拓扑实验】配置步骤与详解(已施工完毕)

一、实验要求 实验拓扑图如上图所示 1、按照图示的VLAN及IP地址需求&#xff0c;完成相关配置 2、要求SW1为VLAN 2/3的主根及主网关 SW2为vlan 20/30的主根及主网关 SW1和SW2互为备份 3、可以使用super vlan&#xff08;本实验未使用&#xff09; 4、上层…...

Qt 编写插件plugin,支持接口定义信号

https://blog.csdn.net/u014213012/article/details/122434193?spm1001.2014.3001.5506 本教程基于该链接的内容进行升级&#xff0c;在编写插件的基础上&#xff0c;支持接口类定义信号。 环境&#xff1a;Qt5.12.12 MSVC2017 一、创建项目 新建一个子项目便于程序管理【…...

Qt中 QWidget 和 QMainWindow 区别

QWidget 用来构建简单窗口 QMainWindow 用来构建更复杂的窗口&#xff0c;QMainWindow 继承自QWidget&#xff0c;在QWidget 的基础上提供了菜单栏、工具栏、状态栏等功能 菜单栏&#xff08;QMenuBar&#xff09;工具栏&#xff08;QToolBar&#xff09;状态栏&#xff08;Q…...

Kafka集群中数据的存储是按照什么方式存储的?

1&#xff09;Topic 数据的存储机制 Topic是逻辑上的概念&#xff0c;而partition是物理上的概念&#xff0c;每个partition对应于一个log文件&#xff0c;该log文件中存储的就是Producer生产的数据。Producer生产的数据会被不断追加到该log文件末端&#xff0c;为防止log文件…...

中断的硬件框架

往期内容 本专栏往期内容&#xff0c;interrtupr子系统&#xff1a; 深入解析Linux内核中断管理&#xff1a;从IRQ描述符到irq domain的设计与实现Linux内核中IRQ Domain的结构、操作及映射机制详解中断描述符irq_desc成员详解Linux 内核中断描述符 (irq_desc) 的初始化与动态分…...

数据备份策略:企业防御的关键

数据备份是保护数据免受网络攻击的重要步骤。在从恶意软件或勒索软件攻击中恢复时&#xff0c;公司可以使用保存的备份将其恢复到之前的状态。但是&#xff0c;为了确保数据的完全安全&#xff0c;任何公司的备份策略都应该在其总体策略中包含多种解决方案。 根据关于创建、消…...

Baget 私有化nuget

Baget下载 1、下载运行 方法一&#xff1a;cmd运行 dotnet BaGet.dll --urls http://*:8002 http://localhost:8002 方法二&#xff1a;bat脚本运行Baget 创建Start.bat dotnet BaGet.dll --urls http://*:8002 运行Start.bat 方法三&#xff1a;部署成Window服务 NSSM部…...

前端函数的参数都有哪些?

在前端开发中&#xff0c;函数的分类可以根据不同的标准进行。以下是一些常见的函数分类方式&#xff0c;并附有相应的例子&#xff1a; 按传递方式分类&#xff1a; 按值传递&#xff1a;JavaScript 中的基本类型&#xff08;如数字、字符串、布尔值&#xff09;都是按值传递的…...

【CSS】什么是BFC?

块级格式化上下文&#xff08;Block Formatting Context&#xff0c;简称BFC&#xff09;是CSS布局中的一种重要概念&#xff0c;它决定了块级盒子如何在其容器内排列&#xff0c;以及浮动元素对其周围元素的影响。理解BFC可以帮助解决许多常见的网页布局问题&#xff0c;比如清…...

HCIP小型园区网拓扑实验

1.拓扑以及需求 2.需求分析 需要的核心技术 1、虚拟局域网&#xff08;VLAN&#xff09; 2、链路聚合&#xff08;E-trunk&#xff09; 3、多生成树协议&#xff08;MSTP&#xff09; 4、VLANIF三层逻辑接口 5、虚拟路由冗余协议&#xff08;VRRP&#xff09; 6、动态主…...

GRR测量系统的重复性和再现性

GRR&#xff08;GaugeRepeatabilityandReproducibility&#xff09;即测量系统的重复性和再现性&#xff0c;是用于评估测量系统性能的一个重要指标。以下是对GRR的详细解释&#xff1a; 一、定义 • 重复性&#xff08;Repeatability&#xff09;&#xff1a;在相同条件下&…...

133.鸿蒙基础01

鸿蒙基础 1.自定义构建函数1. 构建函数-[Builder ](/Builder )2. 构建函数-传参传递(单向)3. 构建函数-传递参数(双向)4. 构建函数-传递参数练习5. 构建函数-[BuilderParam ](/BuilderParam ) 传递UI 2.组件状态共享1. 状态共享-父子单向2. 状态共享-父子双向3. 状态共享-后代组…...

科技查新小知识

首先科技查新是什么&#xff1f; 科技查新是文献检索和情报调研相结合的情报研究工作&#xff0c;它以文献为基础&#xff0c;以文献检索和情报调研为手段&#xff0c;以检出结果为依据&#xff0c;通过综合分析&#xff0c;对查新项目的新颖性进行情报学审查&#xff0c;写出有…...

docker安装portainer

1、拉取镜像 docker pull portainer/portainer-ce:latest2、执行 docker run -d --restartalways --name portainer -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v /data/portainer/data:/data -v /data/portainer/public:/public portainer/portain…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...