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

【C命名规范】遵循良好的命名规范,提高代码的可读性、可维护性和可复用性

/********************************************************************
* @brief @param @return @author @date @version是代码书写的一种规范
* @brief :简介,简单介绍函数作用
* @param :介绍函数参数
* @return:函数返回类型说明
* @exception NSException 可能抛出的异常.
* @author zhangsan: 作者
* @date 2011-07-27 22:30:00 :时间
* @version 1.0 :版本
* @property :属性介绍
* *********************************************************************/

【变量的命名习惯】
变量名是自定义的,严格遵循 标识符 命名规则。

  • 见名知意
  • 大驼峰:即每个单词首字母都大写,例如:MyName
  • 小驼峰:即第二个(含)以后的单词首字母大写,例如:myName
  • 小写+下划线:例如:my_name

个人风格分享

1.变量

  • 全局变量:小驼峰(小驼峰+下划线),例如 myNamemy_Name

  • 局部变量: 全小写(小写+下划线),例如 indexmy_name

2.常量: 全大写字母、下划线分隔单词和描述性名称

#define MAX_BUFFER_SIZE 1024
#define PI_VALUE 3.14159
#define DEFAULT_TIMEOUT 30

3.标志变量

  • 全局:g_ + 小驼峰 + 下划线,例如 g_has_Err
  • 局部: g_ + 全小写 + 下划线 , 例如 g_has_err

4.函数名 大驼峰(大驼峰+下划线), 例如 GetValue, Get_Value()

动词+名词结构 ,例如 GetValue, Get_Value()

5.结构体 大驼峰(大驼峰 + 下划线),例如

// 定义结构体
typedef struct {int id;   // 结构体成员局部的用小写char name[50];int age;
} StudentInfo;
// Student_Info

6.枚举 : 枚举成员名使用全大写字母, 前缀标识枚举成员

typedef enum {STATUS_SUCCESS, // 前缀标识枚举成员STATUS_ERROR,  // 全大写字母STATUS_PENDING
} StatusCode;
// Status_Code;

总结就是凡是有带大写的都跟全局有关,凡是标志位够加g_,常量用全大写字母表示。


在C语言编程中,良好的命名规则有助于提高代码的可读性可维护性可扩展性。以下是一些值得学习和遵守的命名规则:

  1. 变量命名规则:

    • 使用小写字母,单词之间用下划线分隔,例如:student_age, total_sumindex
    • 变量名应具有描述性,反映其用途或含义。
  2. 常量命名规则:

    • 使用全大写字母,单词之间用下划线分隔,例如:MAX_BUFFER_SIZE, PI_VALUE
    • 常量名通常使用 #defineconst 关键字定义。
  3. 函数命名规则:

    • 使用动词或动词短语开头,后接名词,单词之间用下划线分隔,例如:calculate_sum, print_student_info
    • 函数名应清晰描述其功能或操作。
  4. 结构体和联合体命名规则:

    • 结构体和联合体类型名使用大写字母开头,每个单词的首字母大写,例如:StudentInfo, EmployeeRecord
    • 结构体变量名使用小写字母,单词之间用下划线分隔,例如:student_info, employee_record
  5. 枚举命名规则:

    • 枚举类型名使用大写字母开头,每个单词的首字母大写,例如:Color, Status
    • 枚举成员名使用全大写字母,单词之间用下划线分隔,例如:RED, GREEN, SUCCESS, FAILURE
  6. 文件名规则:

    • 文件名应简洁且具有描述性,通常使用小写字母,单词之间用下划线分隔,例如:main.c, student_info.h
  7. 缩写和简写:

    • 尽量避免使用不常见的缩写和简写,如果使用缩写,确保其含义清晰并在代码中有解释。
  8. 命名长度:

    • 命名长度应适中,过长的名字会影响可读性,过短的名字则可能不够描述性。一般推荐变量名长度在3-20个字符之间。
  9. 前缀和后缀:

    • 使用前缀或后缀区分不同作用域或用途的变量,例如:全局变量使用 g_ 前缀,静态变量使用 s_ 前缀,指针变量使用 _ptr 后缀。

通过遵守这些命名规则,可以使代码更具可读性和可维护性,减少出错的概率。


全局变量的标志位变量命名时,应该遵循一些具体的规则,以确保代码的可读性和易维护性。以下是一些建议:

  1. 使用前缀标识全局变量:

    • g_ 前缀来标识全局变量。例如:g_flag_connected, g_flag_initialized
  2. 清晰描述变量含义:

    • 变量名应反映其用途或含义。使用描述性单词使其一目了然。例如:g_flag_error_occurred, g_flag_data_ready
  3. 使用布尔命名约定:

    • 对于布尔类型的标志位变量,使用 is, has, can 等前缀。例如:g_is_connected, g_has_error, g_can_read
  4. 统一命名风格:

    • 保持命名的一致性,确保团队中所有开发者遵循相同的命名规范。例如:所有标志位变量都以 g_flag_ 开头,然后描述具体含义。
  5. 避免使用缩写:

    • 除非是非常常见和容易理解的缩写,否则尽量避免使用缩写,以防止误解。例如:g_flag_buffer_overflow 而不是 g_flag_buf_ovf
  6. 示例命名:

    • g_flag_initialized - 表示系统或模块是否已初始化。
    • g_flag_error_occurred - 表示是否发生了错误。
    • g_flag_data_ready - 表示数据是否已经准备好。
    • g_is_connected - 表示是否已经连接。
    • g_has_data - 表示是否有数据可用。

通过遵守这些命名规则,可以使代码更具可读性和一致性,方便团队协作和代码维护。


在单片机开发中,良好的编程习惯和命名规则可以极大地提高代码的可读性、可维护性和可靠性。以下是一些值得学习和遵守的编程习惯和命名规则:

编程习惯

  1. 注释代码:

    • 在代码中添加必要的注释,解释代码的功能、逻辑和重要的变量或函数。确保注释清晰且有意义。
  2. 模块化编程:

    • 将代码划分为多个模块或文件,每个模块负责特定的功能。这样可以提高代码的可维护性和可复用性。
  3. 使用常量和宏定义:

    • 使用 #defineconst 定义常量,避免在代码中使用硬编码的数值。例如:#define MAX_BUFFER_SIZE 1024
  4. 初始化所有变量:

    • 在使用变量之前进行初始化,以防止未定义行为和潜在的错误。
  5. 防御性编程:

    • 编写健壮的代码,处理所有可能的错误情况和异常情况。使用断言(assert)来捕捉不应出现的条件。
  6. 合理使用中断:

    • 中断服务程序(ISR)应该尽量短小精悍,避免在ISR中执行耗时操作。将复杂的处理移到主程序中完成。
  7. 遵循代码规范:

    • 遵循团队或项目的编码规范,确保代码风格一致。常见的编码规范包括命名规则、缩进风格、括号位置等。

命名规则

  1. 变量命名:

    • 使用描述性名称,避免使用单个字符或无意义的名称。变量名应反映其用途或含义。
    • 使用小写字母,单词之间用下划线分隔。例如:sensor_value, motor_speed
  2. 常量命名:

    • 使用全大写字母,单词之间用下划线分隔。例如:MAX_BUFFER_SIZE, PI_VALUE
  3. 函数命名:

    • 使用动词或动词短语开头,后接名词,单词之间用下划线分隔。例如:init_motor, read_sensor_value
  4. 结构体和枚举命名:

    • 结构体和枚举类型名使用大写字母开头,每个单词的首字母大写,例如:SensorData, ErrorCode
    • 结构体变量名使用小写字母,单词之间用下划线分隔,例如:sensor_data, error_code
  5. 宏定义命名:

    • 使用全大写字母,单词之间用下划线分隔。例如:#define LED_PIN 13, #define TIMER_PERIOD 1000
  6. 文件命名:

    • 文件名应简洁且具有描述性,通常使用小写字母,单词之间用下划线分隔,例如:main.c, sensor_driver.h

代码示例

// 常量和宏定义
#define MAX_BUFFER_SIZE 1024
#define LED_PIN 13// 结构体定义
typedef struct {int sensor_value;float temperature;
} SensorData;// 函数声明
void init_motor(void);
int read_sensor_value(void);// 主函数
int main(void) {// 变量初始化SensorData sensor_data = {0, 0.0};int sensor_value = 0;// 初始化电机init_motor();// 读取传感器值sensor_value = read_sensor_value();// 处理传感器数据sensor_data.sensor_value = sensor_value;sensor_data.temperature = sensor_value * 0.1;return 0;
}// 函数定义
void init_motor(void) {// 初始化电机的代码
}int read_sensor_value(void) {// 读取传感器值的代码return 42;
}

这些编程习惯和命名规则可以帮助你编写更高质量、更易维护的代码,尤其是在团队协作和长期维护中显得尤为重要。


在编程中,断言(assert)是一种用于捕捉不应出现的条件的调试工具。断言可以在开发和调试过程中帮助检测程序中的逻辑错误和不一致性。当程序运行时,如果断言条件为假(false),程序会立即终止,并输出错误信息,方便开发者定位和修复问题。

使用断言的步骤

  1. 包含头文件:

    • 使用断言前,需要包含头文件 <assert.h>
  2. 使用 assert 宏:

    • 使用 assert 宏来检查条件。语法如下:
      #include <assert.h>void some_function(int value) {assert(value > 0); // 如果 value <= 0,程序将终止并输出错误信息
      }
      
  3. 编译时控制:

    • 断言在调试阶段非常有用,但在发布阶段可能不需要。可以通过定义 NDEBUG 来禁用断言。
      #define NDEBUG
      #include <assert.h>
      

示例代码

以下是一个使用断言的示例代码:

#include <assert.h>
#include <stdio.h>// 计算数组中元素的平均值
double calculate_average(int *array, int size) {// 断言数组指针不为空assert(array != NULL);// 断言数组大小大于0assert(size > 0);int sum = 0;for (int i = 0; i < size; i++) {sum += array[i];}return (double)sum / size;
}int main() {int values[] = {10, 20, 30, 40, 50};int size = sizeof(values) / sizeof(values[0]);// 调用函数前的断言assert(size > 0);double average = calculate_average(values, size);printf("Average: %.2f\n", average);return 0;
}

解释

  1. 包含头文件:

    • 代码中首先包含了 <assert.h> 头文件,这个头文件定义了 assert 宏。
  2. 使用 assert 宏:

    • calculate_average 函数中,使用了两个断言来确保输入的数组指针不为空且数组大小大于0。
    • main 函数中,使用了一个断言来确保数组大小大于0。
  3. 断言失败:

    • 如果 assert 条件为假,程序将终止并输出错误信息,包括断言失败的条件和源代码文件中的行号。

禁用断言

在发布阶段,可以通过定义 NDEBUG 来禁用断言。例如:

#define NDEBUG
#include <assert.h>

在定义 NDEBUG 后,所有 assert 语句将被预处理器移除,断言检查将不会在程序运行时执行。

总结

使用断言可以有效地捕捉程序中不应出现的条件,帮助开发者在调试阶段快速定位和修复逻辑错误。通过合理使用断言,可以提高代码的健壮性和可靠性。在发布阶段,可以通过定义 NDEBUG 来禁用断言,确保程序的性能和稳定性。


在项目中使用单片机的多个串口(如串口0、串口1、串口2)进行数据收发时,可以使用结构体来组织相关的变量,以便管理和操作这些串口。以下是一个示例,定义了一个结构体来表示三个串口的相关变量:

typedef struct {// 串口0相关变量volatile uint8_t uart0_rx_buffer[UART_RX_BUFFER_SIZE];volatile uint8_t uart0_tx_buffer[UART_TX_BUFFER_SIZE];volatile uint8_t uart0_rx_index;volatile uint8_t uart0_tx_index;volatile bool uart0_rx_complete;// 串口1相关变量volatile uint8_t uart1_rx_buffer[UART_RX_BUFFER_SIZE];volatile uint8_t uart1_tx_buffer[UART_TX_BUFFER_SIZE];volatile uint8_t uart1_rx_index;volatile uint8_t uart1_tx_index;volatile bool uart1_rx_complete;// 串口2相关变量volatile uint8_t uart2_rx_buffer[UART_RX_BUFFER_SIZE];volatile uint8_t uart2_tx_buffer[UART_TX_BUFFER_SIZE];volatile uint8_t uart2_rx_index;volatile uint8_t uart2_tx_index;volatile bool uart2_rx_complete;
} SerialPorts;// 定义一个实例化的结构体变量
SerialPorts serial_ports;

结构体成员说明:

  • uartX_rx_buffer: 串口X接收缓冲区,用于存储从串口X接收到的数据。
  • uartX_tx_buffer: 串口X发送缓冲区,用于存储待发送至串口X的数据。
  • uartX_rx_index: 串口X接收缓冲区的索引,指示下一个接收数据的位置。
  • uartX_tx_index: 串口X发送缓冲区的索引,指示下一个发送数据的位置。
  • uartX_rx_complete: 串口X接收完成标志,用于指示是否完成了一次数据接收。

注意事项:

  1. volatile 关键字

    • 在单片机编程中,通常使用 volatile 关键字来声明这些变量,以确保编译器不会对它们进行优化,保证每次访问都是直接从内存中读取或写入。
  2. 缓冲区大小定义

    • UART_RX_BUFFER_SIZEUART_TX_BUFFER_SIZE 是根据实际需求定义的宏或常量,表示串口接收和发送缓冲区的大小。
  3. 多个串口的区分

    • 结构体中通过命名方式区分不同的串口变量,如 uart0_...uart1_...uart2_...

通过这种方式,你可以方便地管理和操作多个串口的数据收发,使代码结构更清晰,易于维护和扩展。

相关文章:

【C命名规范】遵循良好的命名规范,提高代码的可读性、可维护性和可复用性

/******************************************************************** * brief param return author date version是代码书写的一种规范 * brief &#xff1a;简介&#xff0c;简单介绍函数作用 * param &#xff1a;介绍函数参数 * return&#xff1a;函数返回类型说明 * …...

Hbase面试题总结

一、介绍下HBase架构 --HMaster HBase集群的主节点&#xff0c;负责管理和协调整个集群的操作。它处理元数据和表的分区信息&#xff0c;控制RegionServer的负载均衡和故障恢复。--RegionServer HBase集群中的工作节点&#xff0c;负责存储和处理数据。每个RegionServer管理若…...

C语言部分复习笔记

1. 指针和数组 数组指针 和 指针数组 int* p1[10]; // 指针数组int (*p2)[10]; // 数组指针 因为 [] 的优先级比 * 高&#xff0c;p先和 [] 结合说明p是一个数组&#xff0c;p先和*结合说明p是一个指针 括号保证p先和*结合&#xff0c;说明p是一个指针变量&#xff0c;然后指…...

Rust学习笔记 (命令行命令) : 用override set 设置工具链

在cargo run某个项目时出现了如下错误&#xff1a;error: failed to run custom build command for ring v0.16.20&#xff08;无法运行“Ring v0.16.20”的自定义构建命令&#xff09;&#xff0c;在PowerShell命令行运行命令 rustup override set stable-msvc后成功运行。 o…...

cv::Mat类的矩阵内容输出的各种格式的例子

操作系统&#xff1a;ubuntu22.04OpenCV版本&#xff1a;OpenCV4.9IDE:Visual Studio Code编程语言&#xff1a;C11 功能描述 我们可以这样使用&#xff1a;cv::Mat M(…); cout << M;&#xff0c;直接将矩阵内容输出到控制台。 输出格式支持多种风格&#xff0c;包括O…...

Redis--注册中心集群 Cluster 集群-单服务器

与“多服务器集群”一致需要创建redis配置模板 参照以下链接 CSDN 创建redis容器 node01服务器上创建容器 docker run -d --name redis-6381 --net host --privilegedtrue \ -v /soft/redis-cluster/6381/conf/redis.conf:/etc/redis/redis.conf \ -v /soft/redis-cluster/6…...

CV01_相机成像原理与坐标系之间的转换

目录 0.引言&#xff1a;小孔成像->映射表达式 1. 相机自身的运动如何表征&#xff1f;->外参矩阵E 1.1 旋转 1.2 平移 2. 如何投影到“像平面”&#xff1f;->内参矩阵K 2.1 图像平面坐标转换为像素坐标系 3. 三维到二维的维度是如何丢失的&#xff1f;…...

Android Lint

文章目录 Android Lint概述工作流程Lint 问题问题种类警告严重性检查规则 用命令运行 LintAndroidStudio 使用 Lint忽略 Lint 警告gradle 配置 Lint查找无用资源文件 Android Lint 概述 Lint 是 Android 提供的 代码扫描分析工具&#xff0c;它可以帮助我们发现代码结构/质量…...

【算法刷题 | 动态规划14】6.28(最大子数组和、判断子序列、不同的子序列)

文章目录 35.最大子数组和35.1题目35.2解法&#xff1a;动规35.2.1动规思路35.2.2代码实现 36.判断子序列36.1题目36.2解法&#xff1a;动规36.2.1动规思路36.2.2代码实现 37.不同的子序列37.1题目37.2解法&#xff1a;动规37.2.1动规思路37.2.2代码实现 35.最大子数组和 35.1…...

vue3 vxe-grid列中绑定vxe-switch实现数据更新

1、先上一张图&#xff1a; <template #valueSlot"{ row }"><vxe-switch :value"getV(row.svalue)" change"changeSwitch(row)" /></template>function getV(value){return value 1;};function changeSwitch(row) {console.l…...

Hive SQL:实现炸列(列转行)以及逆操作(行转列)

目录 列转行行转列 列转行 函数&#xff1a; EXPLODE(ARRAY)&#xff1a;将ARRAY中的每一元素转换为每一行 EXPLODE(MAP)&#xff1a;将MAP中的每个键值对转换为两行&#xff0c;其中一行数据包含键&#xff0c;另一行数据包含值 数据样例&#xff1a; 1、将每天的课程&#…...

MD5算法详解

哈希函数 是一种将任意输入长度转变为固定输出长度的函数。 一些常见哈希函数有&#xff1a;MD5、SHA1、SHA256。 MD5算法 MD5算法是一种消息摘要算法&#xff0c;用于消息认证。 数据存储方式&#xff1a;小段存储。 数据填充 首先对我们明文数据进行处理&#xff0c;使其…...

ES6的代理模式-Proxy

语法 target 要使用 Proxy 包装的目标对象&#xff08;可以是任何类型的对象&#xff0c;包括原生数组&#xff0c;函数&#xff0c;甚至另一个代理handler 一个通常以函数作为属性的对象&#xff0c;用来定制拦截行为 const proxy new Proxy(target, handle)举个例子 <s…...

排序(堆排序、快速排序、归并排序)-->深度剖析(二)

前言 前面介绍了冒泡排序、选择排序、插入排序、希尔排序&#xff0c;作为排序中经常用到了算法&#xff0c;还有堆排序、快速排序、归并排序 堆排序&#xff08;HeaSort&#xff09; 堆排序的概念 堆排序是一种有效的排序算法&#xff0c;它利用了完全二叉树的特性。在C语言…...

七一建党节|热烈庆祝中国共产党成立103周年!

时光荏苒&#xff0c;岁月如梭。 在这热情似火的夏日&#xff0c; 我们迎来了中国共产党成立103周年的重要时刻。 这是一个值得全体中华儿女共同铭记和庆祝的日子&#xff0c; 也是激励我们不断前进的重要时刻。 103年&#xff0c; 风雨兼程&#xff0c;砥砺前行。 从嘉兴…...

Spring Boot应用知识梳理

一.简介 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的工具。它简化了基于 Spring 的应用程序的配置和部署过程&#xff0c;提供了一种快速、便捷的方式来构建独立的、生产级别的 Spring 应用程序。 Spring Boot 的一些主要优点包括&#xff1a; 1. 简化配置…...

Spring中利用重载与静态分派

Spring中利用重载与静态分派 在Java和Spring框架中&#xff0c;重载&#xff08;Overloading&#xff09;和静态分派&#xff08;Static Dispatch&#xff09;是两个非常重要的概念&#xff0c;它们在处理类方法选择和执行过程中扮演着关键角色。本文旨在深入探讨Spring环境下…...

文本三剑客之awk:

文本三剑客awk&#xff1a; grep 查 sed 增删改查 主要&#xff1a;增改 awk 按行取列 awk awk默认的分隔符&#xff1a;空格&#xff0c;tab键&#xff0c;多个空格自动压缩为一个。 awk的工作原理&#xff1a;根据指令信息&#xff0c;逐行的读取文本内容&#xff0c;然…...

SpringSecurity-授权示例

用户基于权限进行授权 定义用户与权限 authorities()。 package com.cms.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.userdetails.User; import…...

选哪个短剧系统源码好:全面评估与决策指南

在短剧内容创作和分享日益流行的今天&#xff0c;选择合适的短剧系统源码对于构建一个成功的短剧平台至关重要。短剧系统源码不仅关系到平台的稳定性和用户体验&#xff0c;还直接影响到内容创作者和观众的互动质量。本文将提供一份全面的评估指南&#xff0c;帮助您在众多短剧…...

Python训练营---DAY48

DAY 48 随机函数与广播机制 知识点回顾&#xff1a; 随机张量的生成&#xff1a;torch.randn函数卷积和池化的计算公式&#xff08;可以不掌握&#xff0c;会自动计算的&#xff09;pytorch的广播机制&#xff1a;加法和乘法的广播机制 ps&#xff1a;numpy运算也有类似的广播机…...

503 Service Unavailable:服务器暂时无法处理请求,可能是超载或维护中如何处理?

处理 "503 Service Unavailable" 错误是服务器管理者面临的常见挑战之一。这种错误通常表示服务器暂时无法处理请求&#xff0c;可能是由于服务器超载、维护中或其他临时性问题导致的。在本文中&#xff0c;我将介绍如何处理 "503 Service Unavailable" 错…...

论文阅读:HySCDG生成式数据处理流程

论文地址: The Change You Want To Detect: Semantic Change Detection In Earth Observation With Hybrid Data Generation Abstract 摘要内容介绍 &#x1f4cc; 问题背景 “Bi-temporal change detection at scale based on Very High Resolution (VHR) images is crucia…...

【评测】Qwen3-Embedding模型初体验

每一篇文章前后都增加返回目录 回到目录 【评测】Qwen3-Embedding模型初体验 模型的介绍页面 本机配置&#xff1a;八代i5-8265U&#xff0c;16G内存&#xff0c;无GPU核显运行&#xff0c;win10操作系统 ollama可以通过下面命令拉取模型&#xff1a; ollama pull modelscope…...

【MySQL系列】MySQL 执行 SQL 文件

博客目录 一、MySQL 执行 SQL 文件的常见场景二、MySQL 执行 SQL 文件的主要方法1. 使用 MySQL 命令行客户端2. 在 MySQL 交互界面中使用 source 命令3. 使用 MySQL Workbench 等图形化工具4. 使用编程语言接口 三、执行 SQL 文件时的注意事项1. 字符集问题2. 事务处理3. 错误处…...

深度学习习题3

1.训练神经网络过程中&#xff0c;损失函数在一些时期&#xff08;Epoch&#xff09;不再减小, 原因可能是&#xff1a; 1.学习率太低 2.正则参数太大 3.卡在了局部最小值 A1 and 2 B. 2 and 3 C. 1 and 3 D. 都是 2.对于分类任务&#xff0c;我们不是将神经网络中的随机权重…...

PyTorch——损失函数与反向传播(8)

Loss Functions 越小越好 L1loss MSELoss 损失函数 CrossEntyopyLoss 损失函数 import torch from torch.nn import L1Loss from torch import nn# 创建输入和目标张量&#xff0c;用于后续的损失计算 inputs torch.tensor([1,2,3],dtypetorch.float32) targets torch.tenso…...

Java-IO流之序列化与反序列化详解

Java-IO流之序列化与反序列化详解 一、序列化与反序列化概述1.1 基本概念1.2 核心接口与类1.3 应用场景 二、Java序列化的基本实现2.1 实现Serializable接口2.2 使用ObjectOutputStream进行序列化2.3 使用ObjectInputStream进行反序列化 三、序列化的高级特性3.1 serialVersion…...

PicSharp(图片压缩工具) v1.1.6

PicSharp 一个简单、高效、灵活的跨平台桌面图像压缩应用程序。软件基于Rust实现&#xff0c;高性能低资源&#xff0c;能快速扫描文件或目录&#xff0c;批处理图像。软件还具备组合压缩策略&#xff0c;TinyPNG提供最佳压缩比&#xff0c;但需要互联网连接&#xff0c;对大量…...

【p2p、分布式,区块链笔记 MESH】 论文阅读 Thread/OpenThread Low-Power Wireless Multihop Net

paperauthorThread/OpenThread: A Compromise in Low-Power Wireless Multihop Network Architecture for the Internet of ThingsHyung-Sin Kim, Sam Kumar, and David E. Culler 目录 引言RPL 标准设计目标与架构设计选择与特性shortcomIngs of RPL设计选择的反面影响sImulta…...