diff --git a/api/api-LinkIoT/IOT.md b/api/api-LinkIoT/IOT.md old mode 100755 new mode 100644 diff --git a/bundles/Readme-CN.md b/bundles/Readme-CN.md index 5867ea43d0b211b117dd116fa47b2f31deb4f363..9dd83ddce47aa0b788bc86d0def6d988e0cdbe46 100755 --- a/bundles/Readme-CN.md +++ b/bundles/Readme-CN.md @@ -1,15 +1,15 @@ # 组件开发 -- [组件开发指南](组件开发指南.md) - - [概述](概述.md) - - [准备工作](准备工作.md) - - [组件开发](组件开发.md) - - [组件开发规范](组件开发规范.md) - - [概述](概述-0.md) + - [概述](概述.md) - [组件构成](组件构成.md) - [组件管理](组件管理.md) - [组件版本](组件版本.md) - [发行版](发行版.md) - [环境变量说明](环境变量说明.md) +- [组件开发指南](组件开发指南.md) + - [概述](概述-0.md) + - [准备工作](准备工作.md) + - [组件开发](组件开发.md) + diff --git "a/bundles/\345\207\206\345\244\207\345\267\245\344\275\234.md" "b/bundles/\345\207\206\345\244\207\345\267\245\344\275\234.md" index 5102e5ed15e410be2dda84044c2e996373abf5e7..b129bdcb0a1f857d25147c1609a542114d9cd2d4 100755 --- "a/bundles/\345\207\206\345\244\207\345\267\245\344\275\234.md" +++ "b/bundles/\345\207\206\345\244\207\345\267\245\344\275\234.md" @@ -54,15 +54,15 @@ login = https://hpm.harmonyos.com/hpm/auth/pk # hpm处理 loginUser = {your-account} # 配置hpm登录账号,发布组件必须 shellPath = C:\WINDOWS\System32\cmd.exe # hpm命令执行使用的shell globalRepo = C:\Users\yourname\.global # 配置全局安装的组件存放路径 -http_proxy = http://your-proxy-server:port # 配置HTTP代理 -https_proxy = http://your-proxy-server:port # 配置HTTPS代理 +http_proxy = http://your-proxy-server:port # 配置HTTP代理 +https_proxy = http://your-proxy-server:port # 配置HTTPS代理 ``` hpm-cli的命令介绍可以参考:[hpm操作命令](组件管理.md#table10510164515371) ## 下载OpenHarmony代码 -参考[《源码获取》](zh-cn_topic_0000001050769927.md) +参考[《源码获取》](../get-code/源码获取.md) ## 安装开发依赖的组件 diff --git "a/bundles/\346\246\202\350\277\260-0.md" "b/bundles/\346\246\202\350\277\260-0.md" index 65546c20d4f20861d7b1c162dd81e161e297d0ee..f0068759076b5dcc58c354f336dcd6f244c9bd52 100755 --- "a/bundles/\346\246\202\350\277\260-0.md" +++ "b/bundles/\346\246\202\350\277\260-0.md" @@ -1,34 +1,54 @@ -# 概述 - -本文档将介绍组件的基本概念以及如何按照规范定义组件。 - -## 定义 - -OpenHarmony软件以组件\(bundle\)作为基本单元,从系统角度看,凡是运行在OpenHarmony上的软件都可以定义为组件;一般来讲,根据组件的应用范围,可以分为: - -- 板级组件:如board、arch、mcu这些与设备硬件相关的组件。 -- 系统组件:一组独立功能的集合,如内核、文件系统、框架等。 -- 应用组件:直接面向用户提供服务的应用\(如wifi\_iot,ip\_camera\)。 - -从形式上看,组件是为复用而生,一切可以复用的模块都可以定义为组件,可以分为: - -- 源代码 -- 二进制 -- 代码片段 -- 发行版 - -## 组件划分原则 - -原则上应尽可能划分为细颗粒度的组件,以满足最大限度的复用。主要考虑以下几点: - -- 独立性:组件的功能应该相对独立,支持独立编译,可以单独对外提供接口和服务; -- 耦合性:如果组件必须依赖其他的组件,才能对外提供服务,应考虑和被依赖的组件合并为一个组件。 -- 相关性:如果一组组件共同完成一项功能,且没有被其他组件依赖,未来也没有被依赖的可能,则可以考虑合并为一个组件。 - -## 组件依赖 - -组件的依赖关系分为两种:必选依赖和可选依赖。 - -- 必选依赖:是指组件A在完成某个功能时,必须引入组件B,调用B的接口或服务配合才能完成。称B为A的必选依赖。 -- 可选依赖:是在组件A在完成某个功能时,可以引入组件C,也可以引入组件D。C和D可以相互替换,称C和D为A的可选依赖。 +# 概述 + +本章节将简要介绍如何开发OpenHarmony组件和发行版,并通过命令行工具方式完成组件创建、开发、编译、烧录、调试等开发过程。 + +- 一个组件(bundle)通常和一个代码仓库对应,在代码的基础上增加bundle.json、README文件、LICENSE描述文件。 +- 一个发行版(distribution)是由多个组件构成的。发行版中集合了一个完整系统的各种组件(如驱动、内核、框架、应用),可以用于设备的烧录。 + +**表 1** 组件和发行版的差异对比 + + + + + + + + + + + + + + + + + + + + + + + + +

异同点

+

组件

+

发行版

+

应用场景

+

面向功能特性开发

+

面向系统开发

+

内容

+

功能或特性的实现代码或二进制库

+

依赖的组件清单及编译构建脚本

+

完整程度

+

操作系统的一部分

+

一个完整操作系统版本

+

编译后结果

+

组件包

+

系统镜像

+
+ +**图 1** 组件和发行版的构成 + + +![](figures/zh-cn_image_0000001054663940.png) diff --git "a/bundles/\346\246\202\350\277\260.md" "b/bundles/\346\246\202\350\277\260.md" index f0068759076b5dcc58c354f336dcd6f244c9bd52..65546c20d4f20861d7b1c162dd81e161e297d0ee 100755 --- "a/bundles/\346\246\202\350\277\260.md" +++ "b/bundles/\346\246\202\350\277\260.md" @@ -1,54 +1,34 @@ -# 概述 - -本章节将简要介绍如何开发OpenHarmony组件和发行版,并通过命令行工具方式完成组件创建、开发、编译、烧录、调试等开发过程。 - -- 一个组件(bundle)通常和一个代码仓库对应,在代码的基础上增加bundle.json、README文件、LICENSE描述文件。 -- 一个发行版(distribution)是由多个组件构成的。发行版中集合了一个完整系统的各种组件(如驱动、内核、框架、应用),可以用于设备的烧录。 - -**表 1** 组件和发行版的差异对比 - - - - - - - - - - - - - - - - - - - - - - - - -

异同点

-

组件

-

发行版

-

应用场景

-

面向功能特性开发

-

面向系统开发

-

内容

-

功能或特性的实现代码或二进制库

-

依赖的组件清单及编译构建脚本

-

完整程度

-

操作系统的一部分

-

一个完整操作系统版本

-

编译后结果

-

组件包

-

系统镜像

-
- -**图 1** 组件和发行版的构成 - - -![](figures/zh-cn_image_0000001054663940.png) +# 概述 + +本文档将介绍组件的基本概念以及如何按照规范定义组件。 + +## 定义 + +OpenHarmony软件以组件\(bundle\)作为基本单元,从系统角度看,凡是运行在OpenHarmony上的软件都可以定义为组件;一般来讲,根据组件的应用范围,可以分为: + +- 板级组件:如board、arch、mcu这些与设备硬件相关的组件。 +- 系统组件:一组独立功能的集合,如内核、文件系统、框架等。 +- 应用组件:直接面向用户提供服务的应用\(如wifi\_iot,ip\_camera\)。 + +从形式上看,组件是为复用而生,一切可以复用的模块都可以定义为组件,可以分为: + +- 源代码 +- 二进制 +- 代码片段 +- 发行版 + +## 组件划分原则 + +原则上应尽可能划分为细颗粒度的组件,以满足最大限度的复用。主要考虑以下几点: + +- 独立性:组件的功能应该相对独立,支持独立编译,可以单独对外提供接口和服务; +- 耦合性:如果组件必须依赖其他的组件,才能对外提供服务,应考虑和被依赖的组件合并为一个组件。 +- 相关性:如果一组组件共同完成一项功能,且没有被其他组件依赖,未来也没有被依赖的可能,则可以考虑合并为一个组件。 + +## 组件依赖 + +组件的依赖关系分为两种:必选依赖和可选依赖。 + +- 必选依赖:是指组件A在完成某个功能时,必须引入组件B,调用B的接口或服务配合才能完成。称B为A的必选依赖。 +- 可选依赖:是在组件A在完成某个功能时,可以引入组件C,也可以引入组件D。C和D可以相互替换,称C和D为A的可选依赖。 diff --git "a/bundles/\347\273\204\344\273\266\345\274\200\345\217\221\346\214\207\345\215\227.md" "b/bundles/\347\273\204\344\273\266\345\274\200\345\217\221\346\214\207\345\215\227.md" index 8a67a35235a6b9789469ac4344a76fcbb3f0ddd8..87e8f9fb34140fcaf107bdc2f75bd9363313d826 100755 --- "a/bundles/\347\273\204\344\273\266\345\274\200\345\217\221\346\214\207\345\215\227.md" +++ "b/bundles/\347\273\204\344\273\266\345\274\200\345\217\221\346\214\207\345\215\227.md" @@ -1,6 +1,6 @@ # 组件开发指南 -- **[概述](概述.md)** +- **[概述](概述-0.md)** - **[准备工作](准备工作.md)** diff --git "a/bundles/\347\273\204\344\273\266\345\274\200\345\217\221\350\247\204\350\214\203.md" "b/bundles/\347\273\204\344\273\266\345\274\200\345\217\221\350\247\204\350\214\203.md" index 964a644bd66ecfbab4260a168f0881bf91fa2945..f5a3ffb754e067a761da0f9bf5e2b82c18c73514 100755 --- "a/bundles/\347\273\204\344\273\266\345\274\200\345\217\221\350\247\204\350\214\203.md" +++ "b/bundles/\347\273\204\344\273\266\345\274\200\345\217\221\350\247\204\350\214\203.md" @@ -1,6 +1,6 @@ # 组件开发规范 -- **[概述](概述-0.md)** +- **[概述](概述.md)** - **[组件构成](组件构成.md)** diff --git "a/bundles/\347\273\204\344\273\266\347\256\241\347\220\206.md" "b/bundles/\347\273\204\344\273\266\347\256\241\347\220\206.md" index d619fff09924efe721fe037860d37dbb9d0db7f1..894381c827c6cdcb4b11acbc42e29eae3c8aaef6 100755 --- "a/bundles/\347\273\204\344\273\266\347\256\241\347\220\206.md" +++ "b/bundles/\347\273\204\344\273\266\347\256\241\347\220\206.md" @@ -214,14 +214,14 @@ hpm dependencies

hpm gen-keys

-

生成公钥/私钥对,将公钥配置到HPM服务端,可以实现hpm-cli

+

生成公钥/私钥对,将公钥配置到HPM服务端,可以实现hpm-cli 免密登录,发布组件。

生成第三方开源说明

hpm gen-notice

-

根据每个组件的说明,生成一份合并后的第三方开源说明的合并文件

+

根据每个组件的说明,生成一份合并后的第三方开源说明的合并文件。

diff --git a/contribute/OpenHarmony-c-coding-style-guide.md b/contribute/OpenHarmony-c-coding-style-guide.md old mode 100755 new mode 100644 index 453ffaa068147f395aeff32a9e71aac94d4c895f..73012d71dde11cb7a11f129a7aa5c297ebd8f8a6 --- a/contribute/OpenHarmony-c-coding-style-guide.md +++ b/contribute/OpenHarmony-c-coding-style-guide.md @@ -1,1976 +1,1976 @@ -# C语言编程规范 - -## 目的 -规则并不是完美的,通过禁止在特定情况下有用的特性,可能会对代码实现造成影响。但是我们制定规则的目的“为了大多数程序员可以得到更多的好处”, 如果在团队运作中认为某个规则无法遵循,希望可以共同改进该规则。 -参考该规范之前,希望您具有相应的C语言基础能力,而不是通过该文档来学习C语言。 -1. 了解C语言的ISO标准; -2. 熟知C语言的基本语言特性; -3. 了解C语言的标准库; - -## 总体原则 -代码需要在保证功能正确的前提下,满足**可读、可维护、安全、可靠、可测试、高效、可移植**的特征要求。 - -## 约定 - -**规则**:编程时必须遵守的约定 -**建议**:编程时必须加以考虑的约定 - -无论是“规则”还是“建议”,都必须理解该条目这么规定的原因,并努力遵守。 - -## 例外 - -在不违背总体原则,经过充分考虑,有充足的理由的前提下,可以适当违背规范中约定。 -例外破坏了代码的一致性,请尽量避免。“规则”的例外应该是极少的。 - -下列情况,应风格一致性原则优先: -**修改外部开源代码、第三方代码时,应该遵守开源代码、第三方代码已有规范,保持风格统一。** - - -# 1 命名 - -命名包括文件、函数、变量、类型、宏等命名。 - -命名被认为是软件开发过程中最困难,也是最重要的事情。 -标识符的命名要清晰、明了,有明确含义,符合阅读习惯,容易理解。 - -统一的命名风格是一致性原则最直接的体现。 - - -## 总体风格 - -**驼峰风格(CamelCase)** -大小写字母混用,单词连在一起,不同单词间通过单词首字母大写来分开。 -按连接后的首字母是否大写,又分: **大驼峰(UpperCamelCase)**和**小驼峰(lowerCamelCase)** - - -### 规则1.1 标识符命名使用驼峰风格 - -| 类型 | 命名风格 | -|-|-| -| 函数,结构体类型,枚举类型,联合体类型 | 大驼峰 | -| 变量,函数参数,宏参数,结构体中字段,联合体中成员 | 小驼峰 | -| 宏,常量,枚举值,goto 标签 | 全大写,下划线分割 | - -注意: -上表中`常量`是指,全局作用域下,const 修饰的基本数据类型、枚举、字符串类型的变量,不包括数组、结构体和联合体。 -上表中`变量`是指除常量定义以外的其他变量,均使用小驼峰风格。 - -### 建议1.1 作用域越大,命名应越精确 - -C 与 C++ 不同,没有名字空间,没有类,所以全局作用域下的标识符命名要考虑不要冲突。 -对于全局函数、全局变量、宏、类型名、枚举名的命名,应当精确描述并全局唯一。 - -例: -```c -int GetCount(void); // Bad: 描述不精确 -int GetActiveConnectCount(void); // Good -``` - -为了命名更精确,必要时可以增加模块前缀。 -模块前缀与命名主体之间,按驼峰方式连接。 -示例: -```c -int PrefixFuncName(void); // OK: 驼峰方式,形式上无前缀,内容上有前缀 - -enum XxxMyEnum { // OK. - ... -}; -``` - -## 文件命名 - -### 建议1.2 文件命名统一采用小写字符 - -文件名命名只允许使用小写字母、数字以及下划线(\_)。 -文件名应尽量简短、准确、无二义性。 -不大小写混用的原因是,不同系统对文件名大小写处理会不同(如 MicroSoft 的 DOS, Windows 系统不区分大小写,但是 Unix / Linux, Mac 系统则默认区分)。 - -好的命名举例: -`dhcp_user_log.c` - -坏的命名举例: -`dhcp_user-log.c`: 不推荐用'\-'分隔 -`dhcpuserlog.c`: 未分割单词,可读性差 - -## 函数命名 - -函数命名统一使用大驼峰风格。 - -### 建议1.3 函数的命名遵循阅读习惯 - -动作类函数名,可以使用动宾结构。如: -```c -AddTableEntry() // OK -DeleteUser() // OK -GetUserInfo() // OK -``` -判断型函数,可以用形容词,或加 is: -```c -DataReady() // OK -IsRunning() // OK -JobDone() // OK -``` -数据型函数: -```c -TotalCount() // OK -GetTotalCount() // OK -``` - -## 变量命名 - -变量命名使用小驼峰风格,包括全局变量,局部变量,函数声明或定义中的参数,带括号宏中的参数。 - -### 规则1.2 全局变量应增加 'g_' 前缀,函数内静态变量命名不需要加特殊前缀 - -全局变量应当尽量少使用,使用时应特别注意,所以加上前缀用于视觉上的突出,促使开发人员对这些变量的使用更加小心。 -全局静态变量命名与全局变量相同,函数内的静态变量命名与普通局部变量相同。 - -```c -int g_activeConnectCount; - -void Func(void) -{ - static int pktCount = 0; - ... -} -``` - -注意: -常量本质也是全局变量,但如果命名风格是全大写,下划线连接的格式,则不适用当前规则。 - -### 建议1.4 局部变量应该简短,且能够表达相关含义 - -函数局部变量的命名,在能够表达相关含义的前提下,应该简短。 - -如下: -```c -int Func(...) -{ - enum PowerBoardStatus powerBoardStatusOfSlot; // Not good: 局部变量有点长 - powerBoardStatusOfSlot = GetPowerBoardStatus(slot); - if (powerBoardStatusOfSlot == POWER_OFF) { - ... - } - ... -} -``` -更好的写法: -```c -int Func(...) -{ - enum PowerBoardStatus status; // Good: 结合上下文,status 已经能明确表达意思 - status = GetPowerBoardStatus(slot); - if (status == POWER_OFF) { - ... - } - ... -} -``` -类似的, tmp 可以用来称呼任意类型的临时变量。 -过短的变量命名应慎用,但有时候,单字符变量也是允许的,如用于循环语句中的计数器变量: -```c -int i; -... -for (i = 0; i < COUNTER_RANGE; i++) { - ... -} -``` -或一些简单的数学计算函数中的变量: -```c -int Mul(int a, int b) -{ - return a * b; -} -``` - -## 类型命名 - -类型命名采用大驼峰命名风格。 -类型包括结构体、联合体、枚举类型名。 - -例: -```c -struct MsgHead { - enum MsgType type; - int msgLen; - char *msgBuf; -}; - -union Packet { - struct SendPacket send; - struct RecvPacket recv; -}; - -enum BaseColor { - RED, // 注意,枚举类型是大驼峰,枚举值应使用宏风格 - GREEN, - BLUE -}; - -typedef int (*NodeCmpFunc)(struct Node *a, struct Node *b); -``` - -通过 typedef 对结构体、联合体、枚举起别名时,尽量使用匿名类型。 -若需要指针自嵌套,可以增加 'tag' 前缀或下划线后缀。 -```c -typedef struct { // Good: 无须自嵌套,使用匿名结构体 - int a; - int b; -} MyType; // 结构体别名用大驼峰风格 -``` -```c -typedef struct tagNode { // Good: 使用 tag 前缀。这里也可以使用 'Node_'代替也可以。 - struct tagNode *prev; - struct tagNode *next; -} Node; // 类型主体用大驼峰风格 -``` - -## 宏、常量、枚举命名 - -宏、枚举值采用全大写,下划线连接的格式。 -常量推荐采用全大写,下划线连接风格。作为全局变量,也可以保持与普通全局变量命名风格相同。 -这里常量如前文定义,是指基本数据类型、枚举、字符串类型的全局 const 变量。 - -函数式宏,如果功能上可以替代函数,也可以与函数的命名方式相同,使用大驼峰命名风格。 -这种做法会让宏与函数看起来一样,容易混淆,需要特别注意。 - -宏举例: -```c -#define PI 3.14 -#define MAX(a, b) (((a) < (b)) ? (b) : (a)) -``` -```c -#ifdef SOME_DEFINE -void Bar(int); -#define Foo(a) Bar(a) // 特殊场景,用大驼峰风格命名函数式宏 -#else -void Foo(int); -#endif -``` - -常量举例: -```c -const int VERSION = 200; // OK. - -const enum Color DEFAULT_COLOR = BLUE; // OK. - -const char PATH_SEP = '/'; // OK. - -const char * const GREETINGS = "Hello, World!"; // OK. -``` - -非常量举例: -```c -// 结构体类型,不符合常量定义 -const struct MyType g_myData = { ... }; // OK: 用小驼峰 - -// 数组类型,不符合常量定义 -const int g_xxxBaseValue[4] = { 1, 2, 4, 8 }; // OK: 用小驼峰 - -int Foo(...) -{ - // 局部作用域,不符合常量定义 - const int bufSize = 100; // OK: 用小驼峰 - ... -} -``` - -枚举举例: -```c -// 注意,枚举类型名用大驼峰,其下面的取值是全大写,下划线相连 -enum BaseColor { - RED, - GREEN, - BLUE -}; -``` - -### 建议1.5 避免函数式宏中的临时变量命名污染外部作用域 - -首先,**尽量少的使用函数式宏。** - -当函数式宏需要定义局部变量时,为了防止跟外部函数中的局部变量有命名冲突。 - -后置下划线,是一种解决方案。 例: -```c -#define SWAP_INT(a, b) do { \ - int tmp_ = a; \ - a = b; \ - b = tmp_; \ -} while (0) -``` - -# 2 排版格式 - -## 行宽 - -### 建议2.1 行宽不超过 120 个字符 - -代码行宽不宜过长,否则不利于阅读。 -控制行宽长度可以间接的引导开发去缩短函数、变量的命名,减少嵌套的层数,提升代码可读性。 -强烈建议和要求每行字符数不要超过 **120** 个;除非超过 **120** 能显著增加可读性,并且不会隐藏信息。 -虽然现代显示器分辨率已经很高,但是行宽过长,反而提高了阅读理解的难度;跟本规范提倡的“清晰”、“简洁”原则相背。 - -如下场景不宜换行,可以例外: -- 换行会导致内容截断,无法被方便查找(grep)的字符串,如命令行或 URL 等等。包含这些内容的代码或注释,可以适当例外。 -- \#include / \#error 语句可以超出行宽要求,但是也需要尽量避免。 - -例: -```c -#ifndef XXX_YYY_ZZZ -#error Header aaaa/bbbb/cccc/abc.h must only be included after xxxx/yyyy/zzzz/xyz.h -#endif -``` -## 缩进 - -### 规则2.1 使用空格进行缩进,每次缩进4个空格 - -只允许使用空格(space)进行缩进,每次缩进为 **4** 个空格。不允许使用Tab键进行缩进。 -当前几乎所有的集成开发环境(IDE)和代码编辑器都支持配置将Tab键自动扩展为**4**空格输入,请配置你的代码编辑器支持使用空格进行缩进。 - -## 大括号 - -### 规则2.2 使用 K&R 缩进风格 - -**K&R风格** -换行时,函数左大括号另起一行放行首,并独占一行;其他左大括号跟随语句放行末。 -右大括号独占一行,除非后面跟着同一语句的剩余部分,如 do 语句中的 while,或者 if 语句的 else/else if,或者逗号、分号。 - -如: -```c -struct MyType { // Good: 跟随语句放行末,前置1空格 - ... -}; // Good: 右大括号后面紧跟分号 - -int Foo(int a) -{ // Good: 函数左大括号独占一行,放行首 - if (...) { - ... - } else { // Good: 右大括号与 else 语句在同一行 - ... - } // Good: 右大括号独占一行 -} -``` - -## 函数声明和定义 - -### 规则2.3 函数声明、定义的返回类型和函数名在同一行;函数参数列表换行时应合理对齐 - -在声明和定义函数的时候,函数的返回值类型应该和函数名在同一行。 - -函数参数列表换行时,应合理对齐。 -参数列表的左圆括号总是和函数名在同一行,不要单独一行;右圆括号总是跟随最后一个参数。 - -换行举例: -```c -ReturnType FunctionName(ArgType paramName1, ArgType paramName2) // Good:全在同一行 -{ - ... -} - -ReturnType VeryVeryVeryLongFunctionName(ArgType paramName1, // 行宽不满足所有参数,进行换行 - ArgType paramName2, // Good:和上一行参数对齐 - ArgType paramName3) -{ - ... -} - -ReturnType LongFunctionName(ArgType paramName1, ArgType paramName2, // 行宽限制,进行换行 - ArgType paramName3, ArgType paramName4, ArgType paramName5) // Good: 换行后 4 空格缩进 -{ - ... -} - -ReturnType ReallyReallyReallyReallyLongFunctionName( // 行宽不满足第1个参数,直接换行 - ArgType paramName1, ArgType paramName2, ArgType paramName3) // Good: 换行后 4 空格缩进 -{ - ... -} -``` - -## 函数调用 - -### 规则2.4 函数调用参数列表换行时保持参数进行合理对齐 - -函数调用时,函数参数列表如果换行,应该进行合理的参数对齐。 -左圆括号总是跟函数名,右圆括号总是跟最后一个参数。 - -换行举例: -```c -ReturnType result = FunctionName(paramName1, paramName2); // Good:函数参数放在一行 - -ReturnType result = FunctionName(paramName1, - paramName2, // Good:保持与上方参数对齐 - paramName3); - -ReturnType result = FunctionName(paramName1, paramName2, - paramName3, paramName4, paramName5); // Good:参数换行,4 空格缩进 - -ReturnType result = VeryVeryVeryLongFunctionName( // 行宽不满足第1个参数,直接换行 - paramName1, paramName2, paramName3); // 换行后,4 空格缩进 -``` - -如果函数调用的参数存在内在关联性,按照可理解性优先于格式排版要求,对参数进行合理分组换行。 -```c -// Good:每行的参数代表一组相关性较强的数据结构,放在一行便于理解 -int result = DealWithStructureLikeParams(left.x, left.y, // 表示一组相关参数 - right.x, right.y); // 表示另外一组相关参数 -``` - -## 条件语句 - -### 规则2.5 条件语句必须要使用大括号 - -我们要求条件语句都需要使用大括号,即便只有一条语句。 -理由: -- 代码逻辑直观,易读; -- 在已有条件语句代码上增加新代码时不容易出错; -- 对于在条件语句中使用函数式宏时,没有大括号保护容易出错(如果宏定义时遗漏了大括号)。 - -```c -if (objectIsNotExist) { // Good:单行条件语句也加大括号 - return CreateNewObject(); -} -``` - -### 规则2.6 禁止 if/else/else if 写在同一行 - -条件语句中,若有多个分支,应该写在不同行。 - -如下是正确的写法: -```c -if (someConditions) { - ... -} else { // Good: else 与 if 在不同行 - ... -} -``` - -下面是不符合规范的案例: -```c -if (someConditions) { ... } else { ... } // Bad: else 与 if 在同一行 -``` - -## 循环 - -### 规则2.7 循环语句必须使用大括号 - -和条件表达式类似,我们要求for/while循环语句必须加上大括号,即便循环体是空的,或循环语句只有一条。 -```c -for (int i = 0; i < someRange; i++) { // Good: 使用了大括号 - DoSomething(); -} -``` -```c -while (condition) { } // Good:循环体是空,使用大括号 -``` -```c -while (condition) { - continue; // Good:continue 表示空逻辑,使用大括号 -} -``` - -坏的例子: -```c -for (int i = 0; i < someRange; i++) - DoSomething(); // Bad: 应该加上括号 -``` -```c -while (condition); // Bad:使用分号容易让人误解是while语句中的一部分 -``` - -## switch语句 - -### 规则2.8 switch 语句的 case/default 要缩进一层 - -switch 语句的缩进风格如下: -```c -switch (var) { - case 0: // Good: 缩进 - DoSomething1(); // Good: 缩进 - break; - case 1: { // Good: 带大括号格式 - DoSomething2(); - break; - } - default: - break; -} -``` - -```c -switch (var) { -case 0: // Bad: case 未缩进 - DoSomething(); - break; -default: // Bad: default 未缩进 - break; -} -``` - -## 表达式 - -### 建议2.2 表达式换行要保持换行的一致性,操作符放行末 - -较长的表达式,不满足行宽要求的时候,需要在适当的地方换行。一般在较低优先级操作符或连接符后面截断,操作符或连接符放在行末。 -操作符、连接符放在行末,表示“未结束,后续还有”。 - -例: -```c -// 假设下面第一行已经不满足行宽要求 -if ((currentValue > MIN) && // Good:换行后,布尔操作符放在行末 - (currentValue < MAX)) { - DoSomething(); - ... -} - -int result = reallyReallyLongVariableName1 + // Good: 加号留在行末 - reallyReallyLongVariableName2; -``` - -表达式换行后,注意保持合理对齐,或者4空格缩进。参考下面例子 -```c -int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + - longVaribleName4 + longVaribleName5 + longVaribleName6; // OK: 4空格缩进 - -int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + - longVaribleName4 + longVaribleName5 + longVaribleName6; // OK: 保持对齐 -``` - -## 变量赋值 - -### 规则2.9 多个变量定义和赋值语句不允许写在一行 - -每行最好只有一个变量初始化的语句,更容易阅读和理解。 - -```c -int maxCount = 10; -bool isCompleted = false; -``` - -下面是不符合规范的示例: -```c -int maxCount = 10; bool isCompleted = false; // Bad:多个初始化放在了同一行 -int x, y = 0; // Bad:多个变量定义需要分行,每行一个 - -int pointX; -int pointY; -... -pointX = 1; pointY = 2; // Bad:多个变量赋值语句放同一行 -``` - -例外情况: -对于多个相关性强的变量定义,且无需初始化时,可以定义在一行,减少重复信息,以便代码更加紧凑。 -```c -int i, j; // Good:多变量定义,未初始化,可以写在一行 -for (i = 0; i < row; i++) { - for (j = 0; j < col; j++) { - ... - } -} -``` - -## 初始化 - -初始化包括结构体、联合体及数组的初始化 - -### 规则2.10 初始化换行时要有缩进,或进行合理对齐 - -结构体或数组初始化时,如果换行应保持4空格缩进。 -从可读性角度出发,选择换行点和对齐位置。 -```c -// Good: 满足行宽要求时不换行 -int arr[4] = { 1, 2, 3, 4 }; - -// Good: 行宽较长时,换行让可读性更好 -const int rank[] = { - 16, 16, 16, 16, 32, 32, 32, 32, - 64, 64, 64, 64, 32, 32, 32, 32 -}; -``` - -对于复杂结构数据的初始化,尽量清晰、紧凑。 -参考如下格式: -```c -int a[][4] = { - { 1, 2, 3, 4 }, { 2, 2, 3, 4 }, // OK. - { 3, 2, 3, 4 }, { 4, 2, 3, 4 } -}; - -int b[][8] = { - { 1, 2, 3, 4, 5, 6, 7, 8 }, // OK. - { 2, 2, 3, 4, 5, 6, 7, 8 } -}; -``` -```c -int c[][8] = { - { - 1, 2, 3, 4, 5, 6, 7, 8 // OK. - }, { - 2, 2, 3, 4, 5, 6, 7, 8 - } -}; -``` - -注意: -- 左大括号放行末时,对应的右大括号需另起一行 -- 左大括号被内容跟随时,对应的右大括号也应跟随内容 - -### 规则2.11 结构体和联合体在按成员初始化时,每个成员初始化单独一行 - -C99标准支持结构体和联合体按照成员进行初始化,标准中叫"指定初始化"(designated initializer)。 如果按照这种方式进行初始化,每个成员的初始化单独一行。 -```c -struct Date { - int year; - int month; - int day; -}; - -struct Date date = { // Good:使用指定初始化方式时,每行初始化一个 - .year = 2000, - .month = 1, - .day = 1 -}; -``` - -## 指针 - -### 建议2.3 指针类型"\*"跟随变量名或者类型,不要两边都留有空格或都没有空格 - -声明或定义指针变量或者返回指针类型函数时,"\*" 靠左靠右都可以,但是不要两边都有或者都没有空格。 -```c -int *p1; // OK. -int* p2; // OK. - -int*p3; // Bad: 两边都没空格 -int * p4; // Bad: 两边都有空格 -``` - -选择一种风格,并保持一致性。 - -选择"\*"跟随类型风格时,避免一行同时声明带指针的多个变量。 -```c -int* a, b; // Bad: 很容易将 b 误理解成指针 -``` - -选择"\*"跟随变量风格时,可能会存在无法紧跟的情况。 -无法跟随时就不跟随,不要破坏风格一致性。 -```c -char * const VERSION = "V100"; // OK. -int Foo(const char * restrict p); // OK. -``` -注意,任何时候 "\*" 不要紧跟 const 或 restrict 关键字。 - -## 编译预处理 - -### 规则2.12 编译预处理的"#"默认放在行首,嵌套编译预处理语句时,"#"可以进行缩进 - -编译预处理的"#"统一放在行首;即便编译预处理的代码是嵌入在函数体中的,"#"也应该放在行首。 - -## 空格和空行 - -### 规则2.13 水平空格应该突出关键字和重要信息,避免不必要的留白 - -水平空格应该突出关键字和重要信息,每行代码尾部不要加空格。 总体规则如下: -- if, switch, case, do, while, for 等关键字之后加空格; -- 小括号内部的两侧,不要加空格 -- 二元操作符(= + ‐ < > * / % | & ^ <= >= == !=)左右两侧加空格 -- 一元操作符(& * + ‐ ~ !)之后不要加空格 -- 三目操作符(? :)符号两侧均需要空格 -- 结构体中表示位域的冒号,两侧均需要空格 -- 前置和后置的自增、自减(++ --)和变量之间不加空格 -- 结构体成员操作符(. ->)前后不加空格 -- 大括号内部两侧有无空格,左右必须保持一致 -- 逗号、分号、冒号(不含三目操作符和表示位域的冒号)紧跟前面内容无空格,其后需要空格 -- 函数参数列表的小括号与函数名之间无空格 -- 类型强制转换的小括号与被转换对象之间无空格 -- 数组的中括号与数组名之间无空格 -- 涉及到换行时,行末的空格可以省去 - -对于大括号内部两侧的空格,**建议**如下: -- 一般的,大括号内部两侧建议加空格 -- 对于空的,或单个标识符,或单个字面常量,空格不是必须 -如:'{}', '{0}', '{NULL}', '{"hi"}' 等 -- 连续嵌套的多重括号之间,空格不是必须 -如:'{{0}}', '{{ 1, 2 }}' 等 -错误示例:'{ 0, {1}}',不属于连续嵌套场景,而且最外侧大括号左右不一致 - -常规情况: -```c -int i = 0; // Good:变量初始化时,= 前后应该有空格,分号前面不要留空格 -int buf[BUF_SIZE] = {0}; // Good:数组初始化时,大括号内空格可选 -int arr[] = { 10, 20 }; // Good: 正常大括号内部两侧建议加空格 -``` - -函数定义和函数调用: -```c -int result = Foo(arg1,arg2); - ^ // Bad: 逗号后面应该有空格 - -int result = Foo( arg1, arg2 ); - ^ ^ // Bad: 小括号内部两侧不应该有空格 -``` - -指针和取地址 -```c -x = *p; // Good:*操作符和指针p之间不加空格 -p = &x; // Good:&操作符和变量x之间不加空格 -x = r.y; // Good:通过.访问成员变量时不加空格 -x = r->y; // Good:通过->访问成员变量时不加空格 -``` - -操作符: -```c -x = 0; // Good:赋值操作的=前后都要加空格 -x = -5; // Good:负数的符号和数值之前不要加空格 -++x; // Good:前置和后置的++/--和变量之间不要加空格 -x--; - -if (x && !y) // Good:布尔操作符前后要加上空格,!操作和变量之间不要空格 -v = w * x + y / z; // Good:二元操作符前后要加空格 -v = w * (x + z); // Good:括号内的表达式前后不需要加空格 -``` - -循环和条件语句: -```c -if (condition) { // Good:if关键字和括号之间加空格,括号内条件语句前后不加空格 - ... -} else { // Good:else关键字和大括号之间加空格 - ... -} - -while (condition) {} // Good:while关键字和括号之间加空格,括号内条件语句前后不加空格 - -for (int i = 0; i < someRange; ++i) { // Good:for关键字和括号之间加空格,分号之后加空格 - ... -} - -switch (var) { // Good: switch 关键字后面有1空格 - case 0: // Good:case语句条件和冒号之间不加空格 - ... - break; - ... - default: - ... - break; -} -``` - -注意:当前的集成开发环境(IDE)和代码编辑器都可以设置删除行尾的空格,请正确配置你的编辑器。 - -### 建议2.4 合理安排空行,保持代码紧凑 - -减少不必要的空行,可以显示更多的代码,方便代码阅读。下面有一些建议遵守的规则: -- 根据上下内容的相关程度,合理安排空行; -- 函数内部、类型定义内部、宏内部、初始化表达式内部,不使用连续空行 -- 不使用连续 **3** 个空行,或更多 -- 大括号内的代码块首行之前和末行之后不要加空行。 - -```c -ret = DoSomething(); - -if (ret != OK) { // Bad: 返回值判断应该紧跟函数调用 - return -1; -} -``` -```c -int Foo(void) -{ - ... -} - - - -int Bar(void) // Bad:最多使用连续2个空行。 -{ - ... -} -``` -```c -int Foo(void) -{ - - DoSomething(); // Bad:大括号内部首尾,不需要空行 - ... - -} -``` - -# 3 注释 - -一般的,尽量通过清晰的架构逻辑,好的符号命名来提高代码可读性;需要的时候,才辅以注释说明。 -注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,**按需注释**。 - -注释内容要简洁、明了、无二义性,信息全面且不冗余。 - -**注释跟代码一样重要。** -写注释时要换位思考,用注释去表达此时读者真正需要的信息。在代码的功能、意图层次上进行注释,即注释解释代码难以表达的意图,不要重复代码信息。 -修改代码时,也要保证其相关注释的一致性。只改代码,不改注释是一种不文明行为,破坏了代码与注释的一致性,让阅读者迷惑、费解,甚至误解。 - -使用英文进行注释。 - -## 注释风格 - -在 C 代码中,使用 `/*` `*/`和 `//` 都是可以的。 -按注释的目的和位置,注释可分为不同的类型,如文件头注释、函数头注释、代码注释等等; -同一类型的注释应该保持统一的风格。 - -注意:本文示例代码中,大量使用 '//' 后置注释只是为了更精确的描述问题,并不代表这种注释风格更好。 - -## 文件头注释 - -### 规则3.1 文件头注释必须包含版权许可 - -/* - * Copyright (c) 2020 XXX - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -## 函数头注释 - -### 规则3.2 禁止空有格式的函数头注释 - -并不是所有的函数都需要函数头注释; -函数原型无法表达的信息,加函数头注释辅助说明; - -函数头注释统一放在函数声明或定义上方。 -选择使用如下风格之一: -**使用'//'写函数头** -```c -// 单行函数头 -int Func1(void); - -// 多行函数头 -// 第二行 -int Func2(void); -``` - -**使用'/\*' '\*/' 写函数头** -```c -/* 单行函数头 */ -int Func1(void); - -/* - * 单行或多行函数头 - * 第二行 - */ -int Func2(void); -``` - -函数尽量通过函数名自注释,**按需**写函数头注释。 -不要写无用、信息冗余的函数头;不要写空有格式的函数头。 - -函数头注释内容**可选**,但不限于:功能说明、返回值,性能约束、用法、内存约定、算法实现、可重入的要求等等。 -模块对外头文件中的函数接口声明,其函数头注释,应当将重要、有用的信息表达清楚。 - -例: -```c -/* - * 返回实际写入的字节数,-1表示写入失败 - * 注意,内存 buf 由调用者负责释放 - */ -int WriteString(char *buf, int len); -``` - -坏的例子: -```c -/* - * 函数名:WriteString - * 功能:写入字符串 - * 参数: - * 返回值: - */ -int WriteString(char *buf, int len); -``` -上面例子中的问题: -- 参数、返回值,空有格式没内容 -- 函数名信息冗余 -- 关键的 buf 由谁释放没有说清楚 - -## 代码注释 - -### 规则3.3 代码注释放于对应代码的上方或右边 -### 规则3.4 注释符与注释内容间要有1空格;右置注释与前面代码至少1空格 - -代码上方的注释,应该保持对应代码一样的缩进。 -选择并统一使用如下风格之一: -**使用'//'** -```c -// 这是单行注释 -DoSomething(); - -// 这是多行注释 -// 第二行 -DoSomething(); -``` -**使用'/\*' '\*/'** -```c -/* 这是单行注释 */ -DoSomething(); - -/* - * 这是单/多行注释 - * 第二行 - */ -DoSomething(); -``` - -代码右边的注释,与代码之间,至少留1空格,建议不超过4空格。 -通常使用扩展后的 TAB 键即可实现 1-4 空格的缩进。 - -选择并统一使用如下风格之一: -```c -int foo = 100; // 放右边的注释 -int bar = 200; /* 放右边的注释 */ -``` - -右置格式在适当的时候,上下对齐会更美观。 -对齐后的注释,离左边代码最近的那一行,保证1-4空格的间隔。 -例: -```c -#define A_CONST 100 /* 相关的同类注释,可以考虑上下对齐 */ -#define ANOTHER_CONST 200 /* 上下对齐时,与左侧代码保持间隔 */ -``` - -当右置的注释超过行宽时,请考虑将注释置于代码上方。 - -### 规则3.5 不用的代码段直接删除,不要注释掉 - -被注释掉的代码,无法被正常维护;当企图恢复使用这段代码时,极有可能引入易被忽略的缺陷。 -正确的做法是,不需要的代码直接删除掉。若再需要时,考虑移植或重写这段代码。 - -这里说的注释掉代码,包括用 /\* \*/ 和 //,还包括 #if 0, #ifdef NEVER_DEFINED 等等。 - -### 建议3.1 case语句块结束时如果不加break/return,需要有注释说明(fall-through) - -有时候需要对多个case标签做相同的事情,case语句在结束不加break或return,直接执行下一个case标签中的语句,这在C语法中称之为"fall-through"。 -这种情况下,需要在"fall-through"的地方加上注释,清晰明确的表达出这样做的意图;或者至少显式指明是 "fall-through"。 - -例,显式指明 fall-through: -```c -switch (var) { - case 0: - DoSomething(); - /* fall-through */ - case 1: - DoSomeOtherThing(); - ... - break; - default: - DoNothing(); - break; -} -``` - -如果 case 语句是空语句,则可以不用加注释特别说明: -```c -switch (var) { - case 0: - case 1: - DoSomething(); - break; - default: - DoNothing(); - break; -} -``` - -# 4 头文件 - -**对于C语言来说,头文件的设计体现了大部分的系统设计**。 -正确使用头文件可使代码在可读性、文件大小和编译构建性能上大为改观。 - -本章从编程规范的角度总结了一些方法,可用于帮助合理规划头文件。 - -## 头文件职责 - -头文件是模块或文件的对外接口。 -头文件中适合放置接口的声明,不适合放置实现(内联函数除外)。 -头文件应当职责单一。头文件过于复杂,依赖过于复杂还是导致编译时间过长的主要原因。 - -### 建议4.1 每一个.c文件都应该有相应的.h文件,用于声明需要对外公开的接口 - -通常情况下,每个.c文件都有一个相应的.h(并不一定同名),用于放置对外提供的函数声明、宏定义、类型定义等。 -如果一个.c文件不需要对外公布任何接口,则其就不应当存在。 - -例外:程序的入口(如main函数所在的文件),单元测试代码,动态库代码。 - -示例: -foo.h 内容 -```c -#ifndef FOO_H -#define FOO_H - -int Foo(void); // Good:头文件中声明对外接口 - -#endif -``` - -foo.c 内容 -```c -static void Bar(void); // Good: 对内函数的声明放在.c文件的头部,并声明为static限制其作用域 - -void Foo(void) -{ - Bar(); -} - -static void Bar(void) -{ - // Do something; -} -``` - -内部使用的函数声明,宏、枚举、结构体等定义不应放在头文件中。 - -有些产品中,习惯一个.c文件对应两个.h文件,一个用于存放对外公开的接口,一个用于存放内部需要用到的定义、声明等,以控制.c文件的代码行数。 -不提倡这种风格,产生这种风格的根源在于.c过大,应当首先考虑拆分.c文件。 -另外,一旦把私有定义、声明放到独立的头文件中,就无法从技术上避免别人包含。 - -本规则反过来并不一定成立。比如: -有些特别简单的头文件,如命令 ID 定义头文件,不需要有对应的.c存在。 -同一套接口协议下,有多个实例,由于接口相同且稳定,所以允许出现一个.h对应多个.c文件。 - -### 建议4.2 头文件的扩展名只使用.h,不使用非习惯用法的扩展名,如.inc - -有些产品中使用了 .inc 作为头文件扩展名,这不符合C语言的习惯用法。 -在使用 .inc 作为头文件扩展名的产品,习惯上用于标识此头文件为私有头文件。 -但是从产品的实际代码来看,这一条并没有被遵守,一个 .inc 文件被多个 .c 包含。 -本规范不提倡将私有定义单独放在头文件中,具体见[建议4.1](#a4-1)。 - -## 头文件依赖 - -头文件包含是一种依赖关系,头文件应向稳定的方向包含。 -一般来说,应当让不稳定的模块依赖稳定的模块,从而当不稳定的模块发生变化时,不会影响(编译)稳定的模块。 - -依赖的方向应该是:产品依赖于平台,平台依赖于标准库。 - -除了不稳定的模块依赖于稳定的模块外,更好的方式是每个模块都依赖于接口,这样任何一个模块的内部实现更改都不需要重新编译另外一个模块。 -在这里,假设接口本身是最稳定的。 - -### 规则4.1 禁止头文件循环依赖 - -头文件循环依赖,指 a.h 包含 b.h,b.h 包含 c.h,c.h 包含 a.h, 导致任何一个头文件修改,都导致所有包含了a.h/b.h/c.h的代码全部重新编译一遍。 -而如果是单向依赖,如a.h包含b.h,b.h包含c.h,而c.h不包含任何头文件,则修改a.h不会导致包含了b.h/c.h的源代码重新编译。 - -头文件循环依赖直接体现了架构设计上的不合理,可通过架构优化来避免。 - - -### 规则4.2 头文件必须编写#define保护,防止重复包含 - -为防止头文件被多重包含,所有头文件都应当使用 #define 作为包含保护;不要使用 #pragma once - -定义包含保护符时,应该遵守如下规则: -- 保护符使用唯一名称;建议考虑项目源代码树顶层以下的文件路径 -- 不要在受保护部分的前后放置代码或者注释,文件头注释除外。 - -假定 timer 模块的 timer.h,其目录为 `timer/include/timer.h`。其保护符若使用 'TIME_H' 很容易不唯一,所以使用项目源代码树的全路径,如: -```c -#ifndef TIMER_INCLUDE_TIMER_H -#define TIMER_INCLUDE_TIMER_H - -... - -#endif -``` - -### 规则4.3 禁止通过声明的方式引用外部函数接口、变量 - -只能通过包含头文件的方式使用其他模块或文件提供的接口。 -通过 extern 声明的方式使用外部函数接口、变量,容易在外部接口改变时可能导致声明和定义不一致。 -同时这种隐式依赖,容易导致架构腐化。 - -不符合规范的案例: -a.c 内容 -```c -extern int Foo(void); // Bad: 通过 extern 的方式引用外部函数 - -void Bar(void) -{ - int i = Foo(); // 这里使用了外部接口 Foo - ... -} -``` - -应该改为: -a.c 内容 -```c -#include "b.h" // Good: 通过包含头文件的方式使用其他.c提供的接口 - -void Bar(void) -{ - int i = Foo(); - ... -} -``` - -b.h 内容 -```c -int Foo(void); -``` - -b.c内容 -```c -int Foo(void) -{ - // Do something -} -``` - -例外,有些场景需要引用其内部函数,但并不想侵入代码时,可以 extern 声明方式引用。 -如: -针对某一内部函数进行单元测试时,可以通过 extern 声明来引用被测函数; -当需要对某一函数进行打桩、打补丁处理时,允许 extern 声明该函数。 - -### 规则4.4 禁止在 extern "C" 中包含头文件 - -在 extern "C" 中包含头文件,有可能会导致 extern "C" 嵌套,部分编译器对 extern "C" 嵌套层次有限制,嵌套层次太多会编译错误。 - -extern "C" 通常出现在 C,C++ 混合编程的情况下,在 extern "C" 中包含头文件,可能会导致被包含头文件的原有意图遭到破坏,比如链接规范被不正确地更改。 - -示例,存在a.h和b.h两个头文件: -a.h 内容 -```c -... -#ifdef __cplusplus -void Foo(int); -#define A(value) Foo(value) -#else -void A(int) -#endif -``` - -b.h 内容 -```c -... -#ifdef __cplusplus -extern "C" { -#endif - -#include "a.h" -void B(void); - -#ifdef __cplusplus -} -#endif -``` - -使用C++预处理器展开b.h,将会得到 -```c -extern "C" { - void Foo(int); - void B(void); -} -``` - -按照 a.h 作者的本意,函数 Foo 是一个 C++ 自由函数,其链接规范为 "C++"。 -但在 b.h 中,由于 `#include "a.h"` 被放到了 `extern "C"` 的内部,函数 Foo 的链接规范被不正确地更改了。 - -例外: -如果在 C++ 编译环境中,想引用纯C的头文件,这些C头文件并没有 `extern "C"` 修饰。非侵入式的做法是,在 `extern "C"` 中去包含C头文件。 - -# 5 函数 - -函数的作用:避免重复代码、增加可重用性;分层,降低复杂度、隐藏实现细节,使程序更加模块化,从而更有利于程序的阅读,维护。 - -函数应该简洁、短小。 -一个函数只完成一件事情。 - -## 函数设计 - -函数设计的精髓:编写整洁函数,同时把代码有效组织起来。代码简单直接、不隐藏设计者的意图、用干净利落的抽象和直截了当的控制语句将函数有机组织起来。 - -### 规则5.1 避免函数过长,函数不超过50行(非空非注释) - -函数应该可以一屏显示完 (50行以内),只做一件事情,而且把它做好。 - -过长的函数往往意味着函数功能不单一,过于复杂,或过分呈现细节,未进行进一步抽象。 - -例外: -考虑代码的聚合性与功能的全面性,某些函数可能会超过50行,但前提是不影响代码的可读性与简洁。 -这些例外的函数应该是极少的,例如特定算法处理。 - -即使一个长函数现在工作的非常好, 一旦有人对其修改, 有可能出现新的问题, 甚至导致难以发现的bug。 -建议将其拆分为更加简短并易于管理的若干函数,以便于他人阅读和修改代码。 - -### 规则5.2 避免函数的代码块嵌套过深,不要超过4层 - -函数的代码块嵌套深度指的是函数中的代码控制块(例如:if、for、while、switch等)之间互相包含的深度。 -每级嵌套都会增加阅读代码时的脑力消耗,因为需要在脑子里维护一个“栈”(比如,进入条件语句、进入循环等等)。 -应该做进一步的功能分解,从而避免使代码的阅读者一次记住太多的上下文。 - -使用`卫语句`可以有效的减少 if 相关的嵌套层次。例: -原代码嵌套层数是 3: -```c -int Foo(...) -{ - if (received) { - type = GetMsgType(msg); - if (type != UNKNOWN) { - return DealMsg(...); - } - } - return -1; -} -``` - -使用`卫语句`重构,嵌套层数变成 2: -```c -int Foo(...) -{ - if (!received) { // Good: 使用'卫语句' - return -1; - } - - type = GetMsgType(msg); - if (type == UNKNOWN) { - return -1; - } - - return DealMsg(..); -} -``` -例外: -考虑代码的聚合性与功能的全面性,某些函数嵌套可能会超过4层,但前提是不影响代码的可读性与简洁。 -这些例外的函数应该是极少的。 - -### 建议5.1 对函数的错误返回码要全面处理 - -一个函数(标准库中的函数/第三方库函数/用户定义的函数)能够提供一些指示错误发生的方法。 -这可以通过使用错误标记、特殊的返回数据或者其他手段,不管什么时候函数提供了这样的机制,调用程序应该在函数返回时立刻检查错误指示。 - -示例: -```c -char fileHead[128]; -ReadFileHead(fileName, fileHead, sizeof(fileHead)); // Bad: 未检查返回值 - -DealWithFileHead(fileHead, sizeof(fileHead)); // fileHead 可能无效 -``` - -正确写法: -```c -char fileHead[128]; -ret = ReadFileHead(fileName, fileHead, sizeof(fileHead)); -if (ret != OK) { // Good: 确保 fileHead 被有效写入 - return ERROR; -} - -DealWithFileHead(fileHead, sizeof(fileHead)); // 处理文件头 -``` - - -注意,当函数返回值被大量的显式(void)忽略掉时,应当考虑函数返回值的设计是否合理。 -如果所有调用者都不关注函数返回值时,请将函数设计成`void`型。 - -## 函数参数 - -### 建议5.2 设计函数时,优先使用返回值而不是输出参数 - -使用返回值而不是输出参数,可以提高可读性,并且通常提供相同或更好的性能。 - -函数名为 GetXxx、FindXxx 或直接名词作函数名的函数,直接返回对应对象,可读性更好。 - -### 建议5.3 使用强类型参数,避免使用void\* - -尽管不同的语言对待强类型和弱类型有自己的观点,但是一般认为c/c++是强类型语言,既然我们使用的语言是强类型的,就应该保持这样的风格。 -好处是尽量让编译器在编译阶段就检查出类型不匹配的问题。 - -使用强类型便于编译器帮我们发现错误,如下代码中注意函数 `FooListAddNode` 的使用: -```c -struct FooNode { - struct List link; - int foo; -}; - -struct BarNode { - struct List link; - int bar; -} - -void FooListAddNode(void *node) // Bad: 这里用 void * 类型传递参数 -{ - FooNode *foo = (FooNode *)node; - ListAppend(&g_fooList, &foo->link); -} - -void MakeTheList(...) -{ - FooNode *foo; - BarNode *bar; - ... - - FooListAddNode(bar); // Wrong: 这里本意是想传递参数 foo,但错传了 bar,却没有报错 -} - -``` - -上述问题有可能很隐晦,不易轻易暴露,从而破坏性更大。 -如果明确 `FooListAddNode` 的参数类型,而不是 `void *`,则在编译阶段就能发现上述问题。 -```c -void FooListAddNode(FooNode *foo) -{ - ListAppend(&g_fooList, &foo->link); -} -``` - -例外:某些通用泛型接口,需要传入不同类型指针的,可以用 `void *` 入参。 - - -### 建议5.4 模块内部函数参数的合法性检查,由调用者负责 - -对于模块外部传入的参数,必须进行合法性检查,保护程序免遭非法输入数据的破坏。 -模块内部函数调用,缺省由调用者负责保证参数的合法性,如果都由被调用者来检查参数合法性,可能会出现同一个参数,被检查多次,产生冗余代码,很不简洁。 - -由调用者保证入参的合法性,这种契约式编程能让代码逻辑更简洁,可读性更好。 -示例: -```c -int SomeProc(...) -{ - int data; - - bool dataOK = GetData(&data); // 获取数据 - if (!dataOK) { // 检查上一步结果,其实也就保证了数据合法 - return -1; - } - - DealWithData(data); // 调用数据处理函数 - ... -} - -void DealWithData(int data) -{ - if (data < MIN || data > MAX) { // Bad: 调用者已经保证了数据合法性 - return; - } - - ... -} -``` - -### 建议5.5 函数的指针参数如果不是用于修改所指向的对象就应该声明为指向const的指针 - -const 指针参数,将限制函数通过该指针修改所指向对象,使代码更牢固、安全。 - -示例:C99标准 7.21.4.4 中strncmp 的例子,不变参数声明为const。 -```c -int strncmp(const char *s1, const char *s2, size_t n); // Good:不变参数声明为const -``` - -注意: -指针参数要不要加 const 取决于函数设计,而不是看函数实体内有没有发生“修改对象”的动作。 - -### 建议5.6 函数的参数个数不超过5个 - -函数的参数过多,会使得该函数易于受外部(其他部分的代码)变化的影响,从而影响维护工作。函数的参数过多同时也会增大测试的工作量。 - -函数的参数个数不要超过5个,如果超过可以考虑: -- 看能否拆分函数 -- 看能否将相关参数合在一起,定义结构体 - -## 内联函数 - -内联函数是**C99**引入的一种函数优化手段。函数内联能消除函数调用的开销;并得益于内联实现跟调用点代码的合并,编译器有更大的视角,从而完成更多的代码优化。内联函数跟函数式宏比较类似,两者的分析详见[建议6.1](#a6-1)。 - -### 建议5.7 内联函数不超过10行(非空非注释) - -将函数定义成内联一般希望提升性能,但是实际并不一定能提升性能。 -如果函数体短小,则函数内联可以有效的缩减目标代码的大小,并提升函数执行效率。 -反之,函数体比较大,内联展开会导致目标代码的膨胀,特别是当调用点很多时,膨胀得更厉害,反而会降低执行效率。 -内联函数规模建议控制在 **10** 行以内。 - -不要为了提高性能而滥用内联函数。不要过早优化。一般情况,当有实际测试数据证明内联性能更高时,再将函数定义为内联。 -对于类似 setter/getter 短小而且调用频繁的函数,可以定义为内联。 - -### 规则5.3 被多个源文件调用的内联函数要放在头文件中定义 - -内联函数是在编译时内联展开,因此要求内联函数定义必须在调用此函数的每个源文件内可见。 -如下所示代码,inline.h 只有`SomeInlineFunc`函数的声明而没有定义。other.c包含inline.h,调用`SomeInlineFunc`时无法内联。 - -inline.h -```c -inline int SomeInlineFunc(void); -``` - -inline.c -```c -inline int SomeInlineFunc(void) -{ - // 实现代码 -} -``` - -other.c -```c -#include "inline.h" -int OtherFunc(void) -{ - int ret = SomeInlineFunc(); -} -``` - -由于这个限制,多个源文件如果要调用同一个内联函数,需要将内联函数的定义放在头文件中。 -**gnu89** 在内联函数实现上跟**C99**标准有差异,兼容做法是将函数声明为 **static inline**。 - -# 6 宏 - -## 函数式宏(function-like macro) - -函数式宏是指形如函数的宏(示例代码如下所示),其包含若干条语句来实现某一特定功能。 -```c -#define ASSERT(x) do { \ - if (!(x)) { \ - printk(KERN_EMERG "assertion failed %s: %d: %s\n", \ - __FILE__, __LINE__, #x); \ - BUG(); \ - } \ -} while (0) -``` - -### 建议6.1 使用函数代替函数式宏 - -定义函数式宏前,应考虑能否用函数替代。对于可替代场景,建议用函数替代宏。 -函数式宏的缺点如下: - -- 函数式宏缺乏类型检查,不如函数调用检查严格。示例代码[见下](#macro_lack_of_type_check__example)。 -- 宏展开时宏参数不求值,可能会产生非预期结果,详见[规则6.1](#r6-1)和[规则6.3](#r6-3)。 -- 宏没有独立的作用域,跟控制流语句配合时,可能会产生如[规则6.2](#r6-2)描述的非预期结果。 -- 宏的技巧性太强(参见下面的规则),例如`#`的用法和无处不在的括号,影响可读性。 -- 在特定场景下必须用特定编译器对宏的扩展,如 `gcc` 的 `statement expression`,可移植性也不好。 -- 宏在预编译阶段展开后,在其后编译、链接和调试时都不可见;而且包含多行的宏会展开为一行。函数式宏难以调试、难以打断点,不利于定位问题。 -- 对于包含大量语句的宏,在每个调用点都要展开。如果调用点很多,会造成代码空间的膨胀。 - -函数式宏缺乏类型检查的示例代码: - -```c -#define MAX(a, b) (((a) < (b)) ? (b) : (a)) - -int Max(int a, int b) -{ - return (a < b) ? b : a; -} - -int TestMacro(void) -{ - unsigned int a = 1; - int b = -1; - - (void)printf("MACRO: max of a(%u) and b(%d) is %d\n", a, b, MAX(a, b)); - (void)printf("FUNC : max of a(%u) and b(%d) is %d\n", a, b, Max(a, b)); - return 0; -} -``` - -由于宏缺乏类型检查,`MAX`中的`a`和`b`的比较提升为无符号数的比较,结果是**a < b**。输出结果是: - -``` -MACRO: max of a(1) and b(-1) is -1 -FUNC : max of a(1) and b(-1) is 1 -``` - -函数没有宏的上述缺点。但是,函数相比宏,最大的劣势是执行效率不高(增加函数调用的开销和编译器优化的难度)。 -为此,C99标准引入了内联函数(gcc在标准之前就引入了内联函数)。 - -内联函数跟宏类似,也是在调用点展开。不同之处在于内联函数是在编译时展开。 -内联函数兼具函数和宏的优点: - -- 内联函数/函数执行严格的类型检查 -- 内联函数/函数的入参求值只会进行一次 -- 内联函数就地展开,没有函数调用的开销 -- 内联函数比函数优化得更好 - -对于性能敏感的代码,可以考虑用内联函数代替函数式宏。 -函数和内联函数不能完全替代函数式宏,函数式宏在某些场景更适合。 -比如,在日志记录场景下,使用带可变参和默认参数的函数式宏更方便: - -```c -int ErrLog(const char *file, unsigned long line, const char *fmt, ...); -#define ERR_LOG(fmt, ...) ErrLog(__FILE__, __LINE__, fmt, ##__VA_ARGS__) -``` - - -### 规则6.1 定义宏时,宏参数要使用完备的括号 - -宏参数在宏展开时只是文本替换,在编译时再求值。文本替换后,宏包含的语句跟调用点代码合并。 -合并后的表达式因为操作符的优先级和结合律,可能会导致计算结果跟期望的不同,尤其是当宏参数在一个表达式中时。 - -如下所示,是一种错误的写法: -```c -#define SUM(a, b) a + b // Bad. -``` - -下面这样调用宏,执行结果跟预期不符: -`100 / SUM(2, 8)` 将扩展成 `(100 / 2) + 8`,预期结果则是`100 / (2 + 8)`。 -这个问题可以通过将整个表示式加上括号来解决,如下所示: -```c -#define SUM(a, b) (a + b) // Bad. -``` - -但是这种改法在下面这种场景又有问题: -`SUM(1 << 2, 8)`扩展成`1 << (2 + 8)`(因为`<<`优先级低于`+`),跟预期结果`(1 << 2) + 8`不符。 - -这个问题可以通过将每个宏参数都加上括号来解决,如下所示: -```c -#define SUM(a, b) (a) + (b) // Bad. -``` - -再看看第三种问题场景:`SUM(2, 8) * 10` 。扩展后的结果为 `(2) + ((8) * 10)`,跟预期结果`(2 + 8) * 10`不符。 - -综上所述,正确的写法如下: -```c -#define SUM(a, b) ((a) + (b)) // Good. -``` - -但是要避免滥用括号。如下所示,单独的数字或标识符加括号毫无意义。 -```c -#define SOME_CONST 100 // Good: 单独的数字无需括号 -#define ANOTHER_CONST (-1) // Good: 负数需要使用括号 - -#define THE_CONST SOME_CONST // Good: 单独的标识符无需括号 -``` - -下列情况需要注意: -- 宏参数参与 '#', '##' 操作时,不要加括号 -- 宏参数参与字符串拼接时,不要加括号 -- 宏参数作为独立部分,在赋值(包括+=, -=等)操作的某一边时,无需括号 -- 宏参数作为独立部分,在逗号表达式,函数或宏调用列表中,无需括号 - -举例: -```c -#define MAKE_STR(x) #x // x 不要加括号 - -#define HELLO_STR(obj) "Hello, " obj // obj 不要加括号 - -#define ADD_3(sum, a, b, c) (sum = (a) + (b) + (c)) // a, b, c 需要括号;而 sum 无需括号 - -#define FOO(a, b) Bar((a) + 1, b) // a 需要括号;而 b 无需括号 -``` - - -### 规则6.2 包含多条语句的函数式宏的实现语句必须放在 do-while(0) 中 - -宏本身没有代码块的概念。当宏在调用点展开后,宏内定义的表达式和变量融合到调用代码中,可能会出现变量名冲突和宏内语句被分割等问题。 -通过 do-while(0) 显式为宏加上边界,让宏有独立的作用域,并且跟分号能更好的结合而形成单条语句,从而规避此类问题。 - -如下所示的宏是错误的用法(为了说明问题,下面示例代码稍不符规范): - -```c -// Not Good. -#define FOO(x) \ - (void)printf("arg is %d\n", (x)); \ - DoSomething((x)); -``` - -当像下面示例代码这样调用宏,for循环只执行了宏的第一条语句,宏的后一条语句只在循环结束后执行一次。 - -```c -for (i = 1; i < 10; i++) - FOO(i); -``` - -用大括号将`FOO`定义的语句括起来可以解决上面的问题: - -```c -#define FOO(x) { \ - (void)printf("arg is %d\n", (x)); \ - DoSomething((x)); \ -} -``` - -由于大括号跟分号没有关联。大括号后紧跟的分号,是另外一个语句。 -如下示例代码,会出现'悬挂else' 编译报错: - -```c -if (condition) - FOO(10); -else - FOO(20); -``` - -正确的写法是用 do-while(0) 把执行体括起来,如下所示: - -```c -// Good. -#define FOO(x) do { \ - (void)printf("arg is %d\n", (x)); \ - DoSomething((x)); \ -} while (0) -``` - -例外: -- 包含 break, continue 语句的宏可以例外。使用此类宏务必特别小心。 -- 宏中包含不完整语句时,可以例外。比如用宏封装 for 循环的条件部分。 -- 非多条语句,或单个 if/for/while/switch 语句,可以例外。 - -### 规则6.3 不允许把带副作用的表达式作为参数传递给函数式宏 - -由于宏只是文本替换,对于内部多次使用同一个宏参数的函数式宏,将带副作用的表达式作为宏参数传入会导致非预期的结果。 -如下所示,宏`SQUARE`本身没有问题,但是使用时将带副作用的`a++`传入导致`a`的值在`SQUARE`执行后跟预期不符: - -```c -#define SQUARE(a) ((a) * (a)) - -int a = 5; -int b; -b = SQUARE(a++); // Bad: 实际 a 自增加了 2 次 -``` - -`SQUARE(a++)`展开后为`((a++) * (a++))`,变量`a`自增了两次,其值为`7`,而不是预期的`6`。 - -正确的写法如下所示: - -```c -b = SQUARE(a); -a++; // 结果:a = 6,只自增了一次。 -``` - -此外,如果参数包含函数调用,宏展开后,函数可能会被重复调用。 -如果函数执行结果相同,则存在浪费;如果函数多次调用结果不一样,执行结果可能不符合预期。 - - -### 建议6.2 函数式宏定义中慎用 return、goto、continue、break 等改变程序流程的语句 - -宏中使用 return、goto、continue、break 等改变流程的语句,虽然能简化代码,但同时也隐藏了真实流程,不易于理解,容易导致资源泄漏等问题。 - -首先,宏封装 return 容易导致过度封装和使用。 -如下代码,`status`的判断是主干流程的一部分,用宏封装起来后,变得不直观了,阅读时习惯性把`RETURN_IF`宏忽略掉了,从而导致对主干流程的理解有偏差。 - -```c -#define LOG_AND_RETURN_IF_FAIL(ret, fmt, ...) do { \ - if ((ret) != OK) { \ - (void)ErrLog(fmt, ##__VA_ARGS__); \ - return (ret); \ - } \ -} while (0) - -#define RETURN_IF(cond, ret) do { \ - if (cond) { \ - return (ret); \ - } \ -} while (0) - -ret = InitModuleA(a, b, &status); -LOG_AND_RETURN_IF_FAIL(ret, "Init module A failed!"); // OK. - -RETURN_IF(status != READY, ERR_NOT_READY); // Bad: 重要逻辑不明显 - -ret = InitModuleB(c); -LOG_AND_RETURN_IF_FAIL(ret, "Init module B failed!"); // OK. -``` - -其次,宏封装 return 也容易引发内存泄漏。再看一个例子: -```c -#define CHECK_PTR(ptr, ret) do { \ - if ((ptr) == NULL) { \ - return (ret); \ - } \ -} while (0) - -... - -mem1 = MemAlloc(...); -CHECK_PTR(mem1, ERR_CODE_XXX); - -mem2 = MemAlloc(...); -CHECK_PTR(mem2, ERR_CODE_XXX); // Wrong: 内存泄漏 -``` - -如果 `mem2` 申请内存失败了,`CHECK_PTR` 会直接返回,而没有释放 `mem1`。 -除此之外,`CHECK_PTR` 宏命名也不好,宏名只反映了检查动作,没有指明结果。只有看了宏实现才知道指针为空时返回失败。 - -综上所述: -不推荐宏定义中封装 return、goto、continue、break 等改变程序流程的语句; -对于返回值判断等异常处理场景可以例外。 - -注意: -**包含 return、goto、continue、break 等改变流程语句的宏命名,务必要体现对应关键字。** - - -### 建议6.3 函数式宏不超过10行(非空非注释) - -函数式宏本身的一大问题是比函数更难以调试和定位,特别是宏过长,调试和定位的难度更大。 -而且宏扩展会导致目标代码的膨胀。建议函数式宏不要超过10行。 - -# 7 变量 - -在C语言编码中,除了函数,最重要的就是变量。 -变量在使用时,应始终遵循“职责单一”原则。 -按作用域区分,变量可分为全局变量和局部变量。 - -## 全局变量 - -尽量不用或少用全局变量。 -在程序设计中,全局变量是在所有作用域都可访问的变量。通常,使用不必要的全局变量被认为是坏习惯。 - -使用全局变量的缺点: -- 破坏函数的独立性和可移植性,使函数对全局变量产生依赖,存在耦合; -- 降低函数的代码可读性和可维护性。当多个函数读写全局变量时,某一时刻其取值可能不是确定的,对于代码的阅读和维护不利; -- 在并发编程环境中,使用全局变量会破坏函数的可重入性,需要增加额外的同步保护处理才能确保数据安全。 - -如不可避免,对全局变量的读写应集中封装。 - -### 规则7.1 模块间,禁止使用全局变量作接口 - -全局变量是模块内部的具体实现,不推荐但允许跨文件使用,但禁止作为模块接口暴露出去。 -对全局变量的使用应该尽量集中,如果本模块的数据需要对外部模块开放,应提供对应函数接口。 - -## 局部变量 - -### 规则7.2 严禁使用未经初始化的变量 - -这里的变量,指的是局部动态变量,并且还包括内存堆上申请的内存块。 -因为他们的初始值都是不可预料的,所以禁止未经有效初始化就直接读取其值。 - -```c -void Foo(...) -{ - int data; - Bar(data); // Bad: 未初始化就使用 - ... -} -``` - -如果有不同分支,要确保所有分支都得到初始化后才能使用: -```c -void Foo(...) -{ - int data; - if (...) { - data = 100; - } - Bar(data); // Bad: 部分分支该值未初始化 - ... -} -``` - -未经初始化就使用,一般静态检查工具是可以检查出来的。 -如 PCLint 工具,针对上述两个例子分别会报错: ->Warning 530: Symbol 'data' (line ...) not initialized ->Warning 644: Variable 'data' (line ...) may not have been initialized - -### 规则7.3 禁止无效、冗余的变量初始化 - -如果没有确定的初始值,而仍然进行初始化,不仅不简洁,反而不安全,可能会引入更难发现的问题。 - -常见的冗余初始化: -```c -int cnt = 0; // Bad: 冗余初始化,将会被后面直接覆盖 -... -cnt = GetXxxCnt(); -... -``` - -对于后续有条件赋值的变量,可以在定义时初始化成默认值 -```c -char *buf = NULL; // Good: 这里用 NULL 代表默认值 -if (condition) { - buf = malloc(MEM_SIZE); -} -... -if (buf != NULL) { // 判断是否申请过内存 - free(buf); -} -``` - -针对大数组的冗余清零,更是会影响到性能。 -```c -char buf[VERY_BIG_SIZE] = {0}; -memset(buf, 0, sizeof(buf)); // Bad: 冗余清零 -``` - -无效初始化,隐藏更大问题的反例: -```c -void Foo(...) -{ - int data = 0; // Bad: 习惯性的进行初始化 - - UseData(data); // 使用数据,本应该写在获取数据后面 - data = GetData(...); // 获取数据 - ... -} -``` -上例代码,如果没有赋 0 初始化,静态检查工具可以帮助发现“未经初始化就直接使用”的问题。 -但因为无效初始化,“使用数据”与“获取数据”写颠倒的缺陷,不能被轻易发现。 - -因此,应该写简洁的代码,对变量或内存块进行正确、必要的初始化。 - -C99不再限制局部变量定义必须在语句之前,可以按需定义,即在靠近变量使用的地方定义变量。 -这种简洁的做法,不仅将变量作用域限制更小,而且更方便阅读和维护,还能解决定义变量时不知该怎么初始化的问题。 -如果编译环境支持,建议按需定义。 - -**例外:** -**遵从“安全规范”要求,指针变量、表示资源描述符的变量、BOOL变量不作要求。** - -### 规则7.4 不允许使用魔鬼数字 - -所谓魔鬼数字即看不懂、难以理解的数字。 -魔鬼数字并非一个非黑即白的概念,看不懂也有程度,需要结合代码上下文和业务相关知识来判断 - -例如数字 12,在不同的上下文中情况是不一样的: -`type = 12;` 就看不懂,但 `month = year * 12;` 就能看懂。 -数字 0 有时候也是魔鬼数字,比如 `status = 0;` 并不能表达是什么状态。 - -解决途径: -对于单点使用的数字,可以增加注释说明 -对于多处使用的数字,必须定义宏或const 变量,并通过符号命名自注释。 - -禁止出现下列情况: -没有通过符号来解释数字含义,如 `#define ZERO 0` -符号命名限制了其取值,如 `#define XX_TIMER_INTERVAL_300MS 300` - -# 8 编程实践 - -## 表达式 - -### 建议8.1 表达式的比较,应当遵循左侧倾向于变化、右侧倾向于不变的原则 - -当变量与常量比较时,如果常量放左边,如 `if (MAX == v)` 不符合阅读习惯,而 `if (MAX > v)` 更是难于理解。 -应当按人的正常阅读、表达习惯,将常量放右边。写成如下方式: -```c -if (v == MAX) ... -if (v < MAX) ... -``` - -也有特殊情况,如:`if (MIN < v && v < MAX)` 用来描述区间时,前半段是常量在左的。 - -不用担心将 '==' 误写成 '=',因为 `if (v = MAX)` 会有编译告警,其他静态检查工具也会报错。让工具去解决笔误问题,代码要符合可读性第一。 - -### 规则8.1 含有变量自增或自减运算的表达式中禁止再次引用该变量 - -含有变量自增或自减运算的表达式中,如果再引用该变量,其结果在C标准中未明确定义。各个编译器或者同一个编译器不同版本实现可能会不一致。 -为了更好的可移植性,不应该对标准未定义的运算次序做任何假设。 - -注意,运算次序的问题不能使用括号来解决,因为这不是优先级的问题。 - -示例: -```c -x = b[i] + i++; // Bad: b[i]运算跟 i++,先后顺序并不明确。 -``` -正确的写法是将自增或自减运算单独放一行: -```c -x = b[i] + i; -i++; // Good: 单独一行 -``` - -函数参数: -```c -Func(i++, i); // Bad: 传递第2个参数时,不确定自增运算有没有发生 -``` -正确的写法: -```c -i++; // Good: 单独一行 -x = Func(i, i); -``` - - -### 建议8.2 用括号明确表达式的操作顺序,避免过分依赖默认优先级 - -可以使用括号强调表达式操作顺序,防止因默认的优先级与设计思想不符而导致程序出错。 -然而过多的括号会分散代码使其降低了可读性,应适度使用。 - -当表达式包含不常用,优先级易混淆的操作符时,推荐使用括号,比如位操作符: -```c -c = (a & 0xFF) + b; /* 涉及位操作符,需要括号 */ -``` - -## 语句 - -### 规则8.2 switch语句要有default分支 - -大部分情况下,switch语句中要有default分支,保证在遗漏case标签处理时能够有一个缺省的处理行为。 - -特例: -如果switch条件变量是枚举类型,并且 case 分支覆盖了所有取值,则加上default分支处理有些多余。 -现代编译器都具备检查是否在switch语句中遗漏了某些枚举值的case分支的能力,会有相应的warning提示。 -```c -enum Color { - RED, - BLUE -}; - -// 因为switch条件变量是枚举值,这里可以不用加default处理分支 -switch (color) { - case RED: - DoRedThing(); - break; - case BLUE: - DoBlueThing(); - ... - break; -} -``` - -### 建议8.3 慎用 goto 语句 - -goto语句会破坏程序的结构性,所以除非确实需要,最好不使用goto语句。使用时,也只允许跳转到本函数goto语句之后的语句。 - -goto语句通常用来实现函数单点返回。 -同一个函数体内部存在大量相同的逻辑但又不方便封装成函数的情况下,譬如反复执行文件操作, -对文件操作失败以后的处理部分代码(譬如关闭文件句柄,释放动态申请的内存等等), -一般会放在该函数体的最后部分,在需要的地方就goto到那里,这样代码反而变得清晰简洁。 -实际也可以封装成函数或者封装成宏,但是这么做会让代码变得没那么直接明了。 - -示例: -```c -// Good: 使用 goto 实现单点返回 -int SomeInitFunc(void) -{ - void *p1; - void *p2 = NULL; - void *p3 = NULL; - - p1 = malloc(MEM_LEN); - if (p1 == NULL) { - goto EXIT; - } - - p2 = malloc(MEM_LEN); - if (p2 == NULL) { - goto EXIT; - } - - p3 = malloc(MEM_LEN); - if (p3 == NULL) { - goto EXIT; - } - - DoSomething(p1, p2, p3); - return 0; // OK. - -EXIT: - if (p3 != NULL) { - free(p3); - } - if (p2 != NULL) { - free(p2); - } - if (p1 != NULL) { - free(p1); - } - return -1; // Failed! -} -``` - -## 类型转换 - -### 建议8.4 尽量减少没有必要的数据类型默认转换与强制转换 - -当进行数据类型强制转换时,其数据的意义、转换后的取值等都有可能发生变化,而这些细节若考虑不周,就很有可能留下隐患。 - -如下赋值,多数编译器不产生告警,但值的含义还是稍有变化。 -```c -char ch; -unsigned short int exam; - -ch = -1; -exam = ch; // Bad: 编译器不产生告警,此时exam为0xFFFF。 -``` +# C语言编程规范 + +## 目的 +规则并不是完美的,通过禁止在特定情况下有用的特性,可能会对代码实现造成影响。但是我们制定规则的目的“为了大多数程序员可以得到更多的好处”, 如果在团队运作中认为某个规则无法遵循,希望可以共同改进该规则。 +参考该规范之前,希望您具有相应的C语言基础能力,而不是通过该文档来学习C语言。 +1. 了解C语言的ISO标准; +2. 熟知C语言的基本语言特性; +3. 了解C语言的标准库; + +## 总体原则 +代码需要在保证功能正确的前提下,满足**可读、可维护、安全、可靠、可测试、高效、可移植**的特征要求。 + +## 约定 + +**规则**:编程时必须遵守的约定 +**建议**:编程时必须加以考虑的约定 + +无论是“规则”还是“建议”,都必须理解该条目这么规定的原因,并努力遵守。 + +## 例外 + +在不违背总体原则,经过充分考虑,有充足的理由的前提下,可以适当违背规范中约定。 +例外破坏了代码的一致性,请尽量避免。“规则”的例外应该是极少的。 + +下列情况,应风格一致性原则优先: +**修改外部开源代码、第三方代码时,应该遵守开源代码、第三方代码已有规范,保持风格统一。** + + +# 1 命名 + +命名包括文件、函数、变量、类型、宏等命名。 + +命名被认为是软件开发过程中最困难,也是最重要的事情。 +标识符的命名要清晰、明了,有明确含义,符合阅读习惯,容易理解。 + +统一的命名风格是一致性原则最直接的体现。 + + +## 总体风格 + +**驼峰风格(CamelCase)** +大小写字母混用,单词连在一起,不同单词间通过单词首字母大写来分开。 +按连接后的首字母是否大写,又分: **大驼峰(UpperCamelCase)**和**小驼峰(lowerCamelCase)** + + +### 规则1.1 标识符命名使用驼峰风格 + +| 类型 | 命名风格 | +|-|-| +| 函数,结构体类型,枚举类型,联合体类型 | 大驼峰 | +| 变量,函数参数,宏参数,结构体中字段,联合体中成员 | 小驼峰 | +| 宏,常量,枚举值,goto 标签 | 全大写,下划线分割 | + +注意: +上表中`常量`是指,全局作用域下,const 修饰的基本数据类型、枚举、字符串类型的变量,不包括数组、结构体和联合体。 +上表中`变量`是指除常量定义以外的其他变量,均使用小驼峰风格。 + +### 建议1.1 作用域越大,命名应越精确 + +C 与 C++ 不同,没有名字空间,没有类,所以全局作用域下的标识符命名要考虑不要冲突。 +对于全局函数、全局变量、宏、类型名、枚举名的命名,应当精确描述并全局唯一。 + +例: +```c +int GetCount(void); // Bad: 描述不精确 +int GetActiveConnectCount(void); // Good +``` + +为了命名更精确,必要时可以增加模块前缀。 +模块前缀与命名主体之间,按驼峰方式连接。 +示例: +```c +int PrefixFuncName(void); // OK: 驼峰方式,形式上无前缀,内容上有前缀 + +enum XxxMyEnum { // OK. + ... +}; +``` + +## 文件命名 + +### 建议1.2 文件命名统一采用小写字符 + +文件名命名只允许使用小写字母、数字以及下划线(\_)。 +文件名应尽量简短、准确、无二义性。 +不大小写混用的原因是,不同系统对文件名大小写处理会不同(如 MicroSoft 的 DOS, Windows 系统不区分大小写,但是 Unix / Linux, Mac 系统则默认区分)。 + +好的命名举例: +`dhcp_user_log.c` + +坏的命名举例: +`dhcp_user-log.c`: 不推荐用'\-'分隔 +`dhcpuserlog.c`: 未分割单词,可读性差 + +## 函数命名 + +函数命名统一使用大驼峰风格。 + +### 建议1.3 函数的命名遵循阅读习惯 + +动作类函数名,可以使用动宾结构。如: +```c +AddTableEntry() // OK +DeleteUser() // OK +GetUserInfo() // OK +``` +判断型函数,可以用形容词,或加 is: +```c +DataReady() // OK +IsRunning() // OK +JobDone() // OK +``` +数据型函数: +```c +TotalCount() // OK +GetTotalCount() // OK +``` + +## 变量命名 + +变量命名使用小驼峰风格,包括全局变量,局部变量,函数声明或定义中的参数,带括号宏中的参数。 + +### 规则1.2 全局变量应增加 'g_' 前缀,函数内静态变量命名不需要加特殊前缀 + +全局变量应当尽量少使用,使用时应特别注意,所以加上前缀用于视觉上的突出,促使开发人员对这些变量的使用更加小心。 +全局静态变量命名与全局变量相同,函数内的静态变量命名与普通局部变量相同。 + +```c +int g_activeConnectCount; + +void Func(void) +{ + static int pktCount = 0; + ... +} +``` + +注意: +常量本质也是全局变量,但如果命名风格是全大写,下划线连接的格式,则不适用当前规则。 + +### 建议1.4 局部变量应该简短,且能够表达相关含义 + +函数局部变量的命名,在能够表达相关含义的前提下,应该简短。 + +如下: +```c +int Func(...) +{ + enum PowerBoardStatus powerBoardStatusOfSlot; // Not good: 局部变量有点长 + powerBoardStatusOfSlot = GetPowerBoardStatus(slot); + if (powerBoardStatusOfSlot == POWER_OFF) { + ... + } + ... +} +``` +更好的写法: +```c +int Func(...) +{ + enum PowerBoardStatus status; // Good: 结合上下文,status 已经能明确表达意思 + status = GetPowerBoardStatus(slot); + if (status == POWER_OFF) { + ... + } + ... +} +``` +类似的, tmp 可以用来称呼任意类型的临时变量。 +过短的变量命名应慎用,但有时候,单字符变量也是允许的,如用于循环语句中的计数器变量: +```c +int i; +... +for (i = 0; i < COUNTER_RANGE; i++) { + ... +} +``` +或一些简单的数学计算函数中的变量: +```c +int Mul(int a, int b) +{ + return a * b; +} +``` + +## 类型命名 + +类型命名采用大驼峰命名风格。 +类型包括结构体、联合体、枚举类型名。 + +例: +```c +struct MsgHead { + enum MsgType type; + int msgLen; + char *msgBuf; +}; + +union Packet { + struct SendPacket send; + struct RecvPacket recv; +}; + +enum BaseColor { + RED, // 注意,枚举类型是大驼峰,枚举值应使用宏风格 + GREEN, + BLUE +}; + +typedef int (*NodeCmpFunc)(struct Node *a, struct Node *b); +``` + +通过 typedef 对结构体、联合体、枚举起别名时,尽量使用匿名类型。 +若需要指针自嵌套,可以增加 'tag' 前缀或下划线后缀。 +```c +typedef struct { // Good: 无须自嵌套,使用匿名结构体 + int a; + int b; +} MyType; // 结构体别名用大驼峰风格 +``` +```c +typedef struct tagNode { // Good: 使用 tag 前缀。这里也可以使用 'Node_'代替也可以。 + struct tagNode *prev; + struct tagNode *next; +} Node; // 类型主体用大驼峰风格 +``` + +## 宏、常量、枚举命名 + +宏、枚举值采用全大写,下划线连接的格式。 +常量推荐采用全大写,下划线连接风格。作为全局变量,也可以保持与普通全局变量命名风格相同。 +这里常量如前文定义,是指基本数据类型、枚举、字符串类型的全局 const 变量。 + +函数式宏,如果功能上可以替代函数,也可以与函数的命名方式相同,使用大驼峰命名风格。 +这种做法会让宏与函数看起来一样,容易混淆,需要特别注意。 + +宏举例: +```c +#define PI 3.14 +#define MAX(a, b) (((a) < (b)) ? (b) : (a)) +``` +```c +#ifdef SOME_DEFINE +void Bar(int); +#define Foo(a) Bar(a) // 特殊场景,用大驼峰风格命名函数式宏 +#else +void Foo(int); +#endif +``` + +常量举例: +```c +const int VERSION = 200; // OK. + +const enum Color DEFAULT_COLOR = BLUE; // OK. + +const char PATH_SEP = '/'; // OK. + +const char * const GREETINGS = "Hello, World!"; // OK. +``` + +非常量举例: +```c +// 结构体类型,不符合常量定义 +const struct MyType g_myData = { ... }; // OK: 用小驼峰 + +// 数组类型,不符合常量定义 +const int g_xxxBaseValue[4] = { 1, 2, 4, 8 }; // OK: 用小驼峰 + +int Foo(...) +{ + // 局部作用域,不符合常量定义 + const int bufSize = 100; // OK: 用小驼峰 + ... +} +``` + +枚举举例: +```c +// 注意,枚举类型名用大驼峰,其下面的取值是全大写,下划线相连 +enum BaseColor { + RED, + GREEN, + BLUE +}; +``` + +### 建议1.5 避免函数式宏中的临时变量命名污染外部作用域 + +首先,**尽量少的使用函数式宏。** + +当函数式宏需要定义局部变量时,为了防止跟外部函数中的局部变量有命名冲突。 + +后置下划线,是一种解决方案。 例: +```c +#define SWAP_INT(a, b) do { \ + int tmp_ = a; \ + a = b; \ + b = tmp_; \ +} while (0) +``` + +# 2 排版格式 + +## 行宽 + +### 建议2.1 行宽不超过 120 个字符 + +代码行宽不宜过长,否则不利于阅读。 +控制行宽长度可以间接的引导开发去缩短函数、变量的命名,减少嵌套的层数,提升代码可读性。 +强烈建议和要求每行字符数不要超过 **120** 个;除非超过 **120** 能显著增加可读性,并且不会隐藏信息。 +虽然现代显示器分辨率已经很高,但是行宽过长,反而提高了阅读理解的难度;跟本规范提倡的“清晰”、“简洁”原则相背。 + +如下场景不宜换行,可以例外: +- 换行会导致内容截断,无法被方便查找(grep)的字符串,如命令行或 URL 等等。包含这些内容的代码或注释,可以适当例外。 +- \#include / \#error 语句可以超出行宽要求,但是也需要尽量避免。 + +例: +```c +#ifndef XXX_YYY_ZZZ +#error Header aaaa/bbbb/cccc/abc.h must only be included after xxxx/yyyy/zzzz/xyz.h +#endif +``` +## 缩进 + +### 规则2.1 使用空格进行缩进,每次缩进4个空格 + +只允许使用空格(space)进行缩进,每次缩进为 **4** 个空格。不允许使用Tab键进行缩进。 +当前几乎所有的集成开发环境(IDE)和代码编辑器都支持配置将Tab键自动扩展为**4**空格输入,请配置你的代码编辑器支持使用空格进行缩进。 + +## 大括号 + +### 规则2.2 使用 K&R 缩进风格 + +**K&R风格** +换行时,函数左大括号另起一行放行首,并独占一行;其他左大括号跟随语句放行末。 +右大括号独占一行,除非后面跟着同一语句的剩余部分,如 do 语句中的 while,或者 if 语句的 else/else if,或者逗号、分号。 + +如: +```c +struct MyType { // Good: 跟随语句放行末,前置1空格 + ... +}; // Good: 右大括号后面紧跟分号 + +int Foo(int a) +{ // Good: 函数左大括号独占一行,放行首 + if (...) { + ... + } else { // Good: 右大括号与 else 语句在同一行 + ... + } // Good: 右大括号独占一行 +} +``` + +## 函数声明和定义 + +### 规则2.3 函数声明、定义的返回类型和函数名在同一行;函数参数列表换行时应合理对齐 + +在声明和定义函数的时候,函数的返回值类型应该和函数名在同一行。 + +函数参数列表换行时,应合理对齐。 +参数列表的左圆括号总是和函数名在同一行,不要单独一行;右圆括号总是跟随最后一个参数。 + +换行举例: +```c +ReturnType FunctionName(ArgType paramName1, ArgType paramName2) // Good:全在同一行 +{ + ... +} + +ReturnType VeryVeryVeryLongFunctionName(ArgType paramName1, // 行宽不满足所有参数,进行换行 + ArgType paramName2, // Good:和上一行参数对齐 + ArgType paramName3) +{ + ... +} + +ReturnType LongFunctionName(ArgType paramName1, ArgType paramName2, // 行宽限制,进行换行 + ArgType paramName3, ArgType paramName4, ArgType paramName5) // Good: 换行后 4 空格缩进 +{ + ... +} + +ReturnType ReallyReallyReallyReallyLongFunctionName( // 行宽不满足第1个参数,直接换行 + ArgType paramName1, ArgType paramName2, ArgType paramName3) // Good: 换行后 4 空格缩进 +{ + ... +} +``` + +## 函数调用 + +### 规则2.4 函数调用参数列表换行时保持参数进行合理对齐 + +函数调用时,函数参数列表如果换行,应该进行合理的参数对齐。 +左圆括号总是跟函数名,右圆括号总是跟最后一个参数。 + +换行举例: +```c +ReturnType result = FunctionName(paramName1, paramName2); // Good:函数参数放在一行 + +ReturnType result = FunctionName(paramName1, + paramName2, // Good:保持与上方参数对齐 + paramName3); + +ReturnType result = FunctionName(paramName1, paramName2, + paramName3, paramName4, paramName5); // Good:参数换行,4 空格缩进 + +ReturnType result = VeryVeryVeryLongFunctionName( // 行宽不满足第1个参数,直接换行 + paramName1, paramName2, paramName3); // 换行后,4 空格缩进 +``` + +如果函数调用的参数存在内在关联性,按照可理解性优先于格式排版要求,对参数进行合理分组换行。 +```c +// Good:每行的参数代表一组相关性较强的数据结构,放在一行便于理解 +int result = DealWithStructureLikeParams(left.x, left.y, // 表示一组相关参数 + right.x, right.y); // 表示另外一组相关参数 +``` + +## 条件语句 + +### 规则2.5 条件语句必须要使用大括号 + +我们要求条件语句都需要使用大括号,即便只有一条语句。 +理由: +- 代码逻辑直观,易读; +- 在已有条件语句代码上增加新代码时不容易出错; +- 对于在条件语句中使用函数式宏时,没有大括号保护容易出错(如果宏定义时遗漏了大括号)。 + +```c +if (objectIsNotExist) { // Good:单行条件语句也加大括号 + return CreateNewObject(); +} +``` + +### 规则2.6 禁止 if/else/else if 写在同一行 + +条件语句中,若有多个分支,应该写在不同行。 + +如下是正确的写法: +```c +if (someConditions) { + ... +} else { // Good: else 与 if 在不同行 + ... +} +``` + +下面是不符合规范的案例: +```c +if (someConditions) { ... } else { ... } // Bad: else 与 if 在同一行 +``` + +## 循环 + +### 规则2.7 循环语句必须使用大括号 + +和条件表达式类似,我们要求for/while循环语句必须加上大括号,即便循环体是空的,或循环语句只有一条。 +```c +for (int i = 0; i < someRange; i++) { // Good: 使用了大括号 + DoSomething(); +} +``` +```c +while (condition) { } // Good:循环体是空,使用大括号 +``` +```c +while (condition) { + continue; // Good:continue 表示空逻辑,使用大括号 +} +``` + +坏的例子: +```c +for (int i = 0; i < someRange; i++) + DoSomething(); // Bad: 应该加上括号 +``` +```c +while (condition); // Bad:使用分号容易让人误解是while语句中的一部分 +``` + +## switch语句 + +### 规则2.8 switch 语句的 case/default 要缩进一层 + +switch 语句的缩进风格如下: +```c +switch (var) { + case 0: // Good: 缩进 + DoSomething1(); // Good: 缩进 + break; + case 1: { // Good: 带大括号格式 + DoSomething2(); + break; + } + default: + break; +} +``` + +```c +switch (var) { +case 0: // Bad: case 未缩进 + DoSomething(); + break; +default: // Bad: default 未缩进 + break; +} +``` + +## 表达式 + +### 建议2.2 表达式换行要保持换行的一致性,操作符放行末 + +较长的表达式,不满足行宽要求的时候,需要在适当的地方换行。一般在较低优先级操作符或连接符后面截断,操作符或连接符放在行末。 +操作符、连接符放在行末,表示“未结束,后续还有”。 + +例: +```c +// 假设下面第一行已经不满足行宽要求 +if ((currentValue > MIN) && // Good:换行后,布尔操作符放在行末 + (currentValue < MAX)) { + DoSomething(); + ... +} + +int result = reallyReallyLongVariableName1 + // Good: 加号留在行末 + reallyReallyLongVariableName2; +``` + +表达式换行后,注意保持合理对齐,或者4空格缩进。参考下面例子 +```c +int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + + longVaribleName4 + longVaribleName5 + longVaribleName6; // OK: 4空格缩进 + +int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + + longVaribleName4 + longVaribleName5 + longVaribleName6; // OK: 保持对齐 +``` + +## 变量赋值 + +### 规则2.9 多个变量定义和赋值语句不允许写在一行 + +每行最好只有一个变量初始化的语句,更容易阅读和理解。 + +```c +int maxCount = 10; +bool isCompleted = false; +``` + +下面是不符合规范的示例: +```c +int maxCount = 10; bool isCompleted = false; // Bad:多个初始化放在了同一行 +int x, y = 0; // Bad:多个变量定义需要分行,每行一个 + +int pointX; +int pointY; +... +pointX = 1; pointY = 2; // Bad:多个变量赋值语句放同一行 +``` + +例外情况: +对于多个相关性强的变量定义,且无需初始化时,可以定义在一行,减少重复信息,以便代码更加紧凑。 +```c +int i, j; // Good:多变量定义,未初始化,可以写在一行 +for (i = 0; i < row; i++) { + for (j = 0; j < col; j++) { + ... + } +} +``` + +## 初始化 + +初始化包括结构体、联合体及数组的初始化 + +### 规则2.10 初始化换行时要有缩进,或进行合理对齐 + +结构体或数组初始化时,如果换行应保持4空格缩进。 +从可读性角度出发,选择换行点和对齐位置。 +```c +// Good: 满足行宽要求时不换行 +int arr[4] = { 1, 2, 3, 4 }; + +// Good: 行宽较长时,换行让可读性更好 +const int rank[] = { + 16, 16, 16, 16, 32, 32, 32, 32, + 64, 64, 64, 64, 32, 32, 32, 32 +}; +``` + +对于复杂结构数据的初始化,尽量清晰、紧凑。 +参考如下格式: +```c +int a[][4] = { + { 1, 2, 3, 4 }, { 2, 2, 3, 4 }, // OK. + { 3, 2, 3, 4 }, { 4, 2, 3, 4 } +}; + +int b[][8] = { + { 1, 2, 3, 4, 5, 6, 7, 8 }, // OK. + { 2, 2, 3, 4, 5, 6, 7, 8 } +}; +``` +```c +int c[][8] = { + { + 1, 2, 3, 4, 5, 6, 7, 8 // OK. + }, { + 2, 2, 3, 4, 5, 6, 7, 8 + } +}; +``` + +注意: +- 左大括号放行末时,对应的右大括号需另起一行 +- 左大括号被内容跟随时,对应的右大括号也应跟随内容 + +### 规则2.11 结构体和联合体在按成员初始化时,每个成员初始化单独一行 + +C99标准支持结构体和联合体按照成员进行初始化,标准中叫"指定初始化"(designated initializer)。 如果按照这种方式进行初始化,每个成员的初始化单独一行。 +```c +struct Date { + int year; + int month; + int day; +}; + +struct Date date = { // Good:使用指定初始化方式时,每行初始化一个 + .year = 2000, + .month = 1, + .day = 1 +}; +``` + +## 指针 + +### 建议2.3 指针类型"\*"跟随变量名或者类型,不要两边都留有空格或都没有空格 + +声明或定义指针变量或者返回指针类型函数时,"\*" 靠左靠右都可以,但是不要两边都有或者都没有空格。 +```c +int *p1; // OK. +int* p2; // OK. + +int*p3; // Bad: 两边都没空格 +int * p4; // Bad: 两边都有空格 +``` + +选择一种风格,并保持一致性。 + +选择"\*"跟随类型风格时,避免一行同时声明带指针的多个变量。 +```c +int* a, b; // Bad: 很容易将 b 误理解成指针 +``` + +选择"\*"跟随变量风格时,可能会存在无法紧跟的情况。 +无法跟随时就不跟随,不要破坏风格一致性。 +```c +char * const VERSION = "V100"; // OK. +int Foo(const char * restrict p); // OK. +``` +注意,任何时候 "\*" 不要紧跟 const 或 restrict 关键字。 + +## 编译预处理 + +### 规则2.12 编译预处理的"#"默认放在行首,嵌套编译预处理语句时,"#"可以进行缩进 + +编译预处理的"#"统一放在行首;即便编译预处理的代码是嵌入在函数体中的,"#"也应该放在行首。 + +## 空格和空行 + +### 规则2.13 水平空格应该突出关键字和重要信息,避免不必要的留白 + +水平空格应该突出关键字和重要信息,每行代码尾部不要加空格。 总体规则如下: +- if, switch, case, do, while, for 等关键字之后加空格; +- 小括号内部的两侧,不要加空格 +- 二元操作符(= + ‐ < > * / % | & ^ <= >= == !=)左右两侧加空格 +- 一元操作符(& * + ‐ ~ !)之后不要加空格 +- 三目操作符(? :)符号两侧均需要空格 +- 结构体中表示位域的冒号,两侧均需要空格 +- 前置和后置的自增、自减(++ --)和变量之间不加空格 +- 结构体成员操作符(. ->)前后不加空格 +- 大括号内部两侧有无空格,左右必须保持一致 +- 逗号、分号、冒号(不含三目操作符和表示位域的冒号)紧跟前面内容无空格,其后需要空格 +- 函数参数列表的小括号与函数名之间无空格 +- 类型强制转换的小括号与被转换对象之间无空格 +- 数组的中括号与数组名之间无空格 +- 涉及到换行时,行末的空格可以省去 + +对于大括号内部两侧的空格,**建议**如下: +- 一般的,大括号内部两侧建议加空格 +- 对于空的,或单个标识符,或单个字面常量,空格不是必须 +如:'{}', '{0}', '{NULL}', '{"hi"}' 等 +- 连续嵌套的多重括号之间,空格不是必须 +如:'{{0}}', '{{ 1, 2 }}' 等 +错误示例:'{ 0, {1}}',不属于连续嵌套场景,而且最外侧大括号左右不一致 + +常规情况: +```c +int i = 0; // Good:变量初始化时,= 前后应该有空格,分号前面不要留空格 +int buf[BUF_SIZE] = {0}; // Good:数组初始化时,大括号内空格可选 +int arr[] = { 10, 20 }; // Good: 正常大括号内部两侧建议加空格 +``` + +函数定义和函数调用: +```c +int result = Foo(arg1,arg2); + ^ // Bad: 逗号后面应该有空格 + +int result = Foo( arg1, arg2 ); + ^ ^ // Bad: 小括号内部两侧不应该有空格 +``` + +指针和取地址 +```c +x = *p; // Good:*操作符和指针p之间不加空格 +p = &x; // Good:&操作符和变量x之间不加空格 +x = r.y; // Good:通过.访问成员变量时不加空格 +x = r->y; // Good:通过->访问成员变量时不加空格 +``` + +操作符: +```c +x = 0; // Good:赋值操作的=前后都要加空格 +x = -5; // Good:负数的符号和数值之前不要加空格 +++x; // Good:前置和后置的++/--和变量之间不要加空格 +x--; + +if (x && !y) // Good:布尔操作符前后要加上空格,!操作和变量之间不要空格 +v = w * x + y / z; // Good:二元操作符前后要加空格 +v = w * (x + z); // Good:括号内的表达式前后不需要加空格 +``` + +循环和条件语句: +```c +if (condition) { // Good:if关键字和括号之间加空格,括号内条件语句前后不加空格 + ... +} else { // Good:else关键字和大括号之间加空格 + ... +} + +while (condition) {} // Good:while关键字和括号之间加空格,括号内条件语句前后不加空格 + +for (int i = 0; i < someRange; ++i) { // Good:for关键字和括号之间加空格,分号之后加空格 + ... +} + +switch (var) { // Good: switch 关键字后面有1空格 + case 0: // Good:case语句条件和冒号之间不加空格 + ... + break; + ... + default: + ... + break; +} +``` + +注意:当前的集成开发环境(IDE)和代码编辑器都可以设置删除行尾的空格,请正确配置你的编辑器。 + +### 建议2.4 合理安排空行,保持代码紧凑 + +减少不必要的空行,可以显示更多的代码,方便代码阅读。下面有一些建议遵守的规则: +- 根据上下内容的相关程度,合理安排空行; +- 函数内部、类型定义内部、宏内部、初始化表达式内部,不使用连续空行 +- 不使用连续 **3** 个空行,或更多 +- 大括号内的代码块首行之前和末行之后不要加空行。 + +```c +ret = DoSomething(); + +if (ret != OK) { // Bad: 返回值判断应该紧跟函数调用 + return -1; +} +``` +```c +int Foo(void) +{ + ... +} + + + +int Bar(void) // Bad:最多使用连续2个空行。 +{ + ... +} +``` +```c +int Foo(void) +{ + + DoSomething(); // Bad:大括号内部首尾,不需要空行 + ... + +} +``` + +# 3 注释 + +一般的,尽量通过清晰的架构逻辑,好的符号命名来提高代码可读性;需要的时候,才辅以注释说明。 +注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,**按需注释**。 + +注释内容要简洁、明了、无二义性,信息全面且不冗余。 + +**注释跟代码一样重要。** +写注释时要换位思考,用注释去表达此时读者真正需要的信息。在代码的功能、意图层次上进行注释,即注释解释代码难以表达的意图,不要重复代码信息。 +修改代码时,也要保证其相关注释的一致性。只改代码,不改注释是一种不文明行为,破坏了代码与注释的一致性,让阅读者迷惑、费解,甚至误解。 + +使用英文进行注释。 + +## 注释风格 + +在 C 代码中,使用 `/*` `*/`和 `//` 都是可以的。 +按注释的目的和位置,注释可分为不同的类型,如文件头注释、函数头注释、代码注释等等; +同一类型的注释应该保持统一的风格。 + +注意:本文示例代码中,大量使用 '//' 后置注释只是为了更精确的描述问题,并不代表这种注释风格更好。 + +## 文件头注释 + +### 规则3.1 文件头注释必须包含版权许可 + +/* + * Copyright (c) 2020 XXX + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +## 函数头注释 + +### 规则3.2 禁止空有格式的函数头注释 + +并不是所有的函数都需要函数头注释; +函数原型无法表达的信息,加函数头注释辅助说明; + +函数头注释统一放在函数声明或定义上方。 +选择使用如下风格之一: +**使用'//'写函数头** +```c +// 单行函数头 +int Func1(void); + +// 多行函数头 +// 第二行 +int Func2(void); +``` + +**使用'/\*' '\*/' 写函数头** +```c +/* 单行函数头 */ +int Func1(void); + +/* + * 单行或多行函数头 + * 第二行 + */ +int Func2(void); +``` + +函数尽量通过函数名自注释,**按需**写函数头注释。 +不要写无用、信息冗余的函数头;不要写空有格式的函数头。 + +函数头注释内容**可选**,但不限于:功能说明、返回值,性能约束、用法、内存约定、算法实现、可重入的要求等等。 +模块对外头文件中的函数接口声明,其函数头注释,应当将重要、有用的信息表达清楚。 + +例: +```c +/* + * 返回实际写入的字节数,-1表示写入失败 + * 注意,内存 buf 由调用者负责释放 + */ +int WriteString(char *buf, int len); +``` + +坏的例子: +```c +/* + * 函数名:WriteString + * 功能:写入字符串 + * 参数: + * 返回值: + */ +int WriteString(char *buf, int len); +``` +上面例子中的问题: +- 参数、返回值,空有格式没内容 +- 函数名信息冗余 +- 关键的 buf 由谁释放没有说清楚 + +## 代码注释 + +### 规则3.3 代码注释放于对应代码的上方或右边 +### 规则3.4 注释符与注释内容间要有1空格;右置注释与前面代码至少1空格 + +代码上方的注释,应该保持对应代码一样的缩进。 +选择并统一使用如下风格之一: +**使用'//'** +```c +// 这是单行注释 +DoSomething(); + +// 这是多行注释 +// 第二行 +DoSomething(); +``` +**使用'/\*' '\*/'** +```c +/* 这是单行注释 */ +DoSomething(); + +/* + * 这是单/多行注释 + * 第二行 + */ +DoSomething(); +``` + +代码右边的注释,与代码之间,至少留1空格,建议不超过4空格。 +通常使用扩展后的 TAB 键即可实现 1-4 空格的缩进。 + +选择并统一使用如下风格之一: +```c +int foo = 100; // 放右边的注释 +int bar = 200; /* 放右边的注释 */ +``` + +右置格式在适当的时候,上下对齐会更美观。 +对齐后的注释,离左边代码最近的那一行,保证1-4空格的间隔。 +例: +```c +#define A_CONST 100 /* 相关的同类注释,可以考虑上下对齐 */ +#define ANOTHER_CONST 200 /* 上下对齐时,与左侧代码保持间隔 */ +``` + +当右置的注释超过行宽时,请考虑将注释置于代码上方。 + +### 规则3.5 不用的代码段直接删除,不要注释掉 + +被注释掉的代码,无法被正常维护;当企图恢复使用这段代码时,极有可能引入易被忽略的缺陷。 +正确的做法是,不需要的代码直接删除掉。若再需要时,考虑移植或重写这段代码。 + +这里说的注释掉代码,包括用 /\* \*/ 和 //,还包括 #if 0, #ifdef NEVER_DEFINED 等等。 + +### 建议3.1 case语句块结束时如果不加break/return,需要有注释说明(fall-through) + +有时候需要对多个case标签做相同的事情,case语句在结束不加break或return,直接执行下一个case标签中的语句,这在C语法中称之为"fall-through"。 +这种情况下,需要在"fall-through"的地方加上注释,清晰明确的表达出这样做的意图;或者至少显式指明是 "fall-through"。 + +例,显式指明 fall-through: +```c +switch (var) { + case 0: + DoSomething(); + /* fall-through */ + case 1: + DoSomeOtherThing(); + ... + break; + default: + DoNothing(); + break; +} +``` + +如果 case 语句是空语句,则可以不用加注释特别说明: +```c +switch (var) { + case 0: + case 1: + DoSomething(); + break; + default: + DoNothing(); + break; +} +``` + +# 4 头文件 + +**对于C语言来说,头文件的设计体现了大部分的系统设计**。 +正确使用头文件可使代码在可读性、文件大小和编译构建性能上大为改观。 + +本章从编程规范的角度总结了一些方法,可用于帮助合理规划头文件。 + +## 头文件职责 + +头文件是模块或文件的对外接口。 +头文件中适合放置接口的声明,不适合放置实现(内联函数除外)。 +头文件应当职责单一。头文件过于复杂,依赖过于复杂还是导致编译时间过长的主要原因。 + +### 建议4.1 每一个.c文件都应该有相应的.h文件,用于声明需要对外公开的接口 + +通常情况下,每个.c文件都有一个相应的.h(并不一定同名),用于放置对外提供的函数声明、宏定义、类型定义等。 +如果一个.c文件不需要对外公布任何接口,则其就不应当存在。 + +例外:程序的入口(如main函数所在的文件),单元测试代码,动态库代码。 + +示例: +foo.h 内容 +```c +#ifndef FOO_H +#define FOO_H + +int Foo(void); // Good:头文件中声明对外接口 + +#endif +``` + +foo.c 内容 +```c +static void Bar(void); // Good: 对内函数的声明放在.c文件的头部,并声明为static限制其作用域 + +void Foo(void) +{ + Bar(); +} + +static void Bar(void) +{ + // Do something; +} +``` + +内部使用的函数声明,宏、枚举、结构体等定义不应放在头文件中。 + +有些产品中,习惯一个.c文件对应两个.h文件,一个用于存放对外公开的接口,一个用于存放内部需要用到的定义、声明等,以控制.c文件的代码行数。 +不提倡这种风格,产生这种风格的根源在于.c过大,应当首先考虑拆分.c文件。 +另外,一旦把私有定义、声明放到独立的头文件中,就无法从技术上避免别人包含。 + +本规则反过来并不一定成立。比如: +有些特别简单的头文件,如命令 ID 定义头文件,不需要有对应的.c存在。 +同一套接口协议下,有多个实例,由于接口相同且稳定,所以允许出现一个.h对应多个.c文件。 + +### 建议4.2 头文件的扩展名只使用.h,不使用非习惯用法的扩展名,如.inc + +有些产品中使用了 .inc 作为头文件扩展名,这不符合C语言的习惯用法。 +在使用 .inc 作为头文件扩展名的产品,习惯上用于标识此头文件为私有头文件。 +但是从产品的实际代码来看,这一条并没有被遵守,一个 .inc 文件被多个 .c 包含。 +本规范不提倡将私有定义单独放在头文件中,具体见[建议4.1](#a4-1)。 + +## 头文件依赖 + +头文件包含是一种依赖关系,头文件应向稳定的方向包含。 +一般来说,应当让不稳定的模块依赖稳定的模块,从而当不稳定的模块发生变化时,不会影响(编译)稳定的模块。 + +依赖的方向应该是:产品依赖于平台,平台依赖于标准库。 + +除了不稳定的模块依赖于稳定的模块外,更好的方式是每个模块都依赖于接口,这样任何一个模块的内部实现更改都不需要重新编译另外一个模块。 +在这里,假设接口本身是最稳定的。 + +### 规则4.1 禁止头文件循环依赖 + +头文件循环依赖,指 a.h 包含 b.h,b.h 包含 c.h,c.h 包含 a.h, 导致任何一个头文件修改,都导致所有包含了a.h/b.h/c.h的代码全部重新编译一遍。 +而如果是单向依赖,如a.h包含b.h,b.h包含c.h,而c.h不包含任何头文件,则修改a.h不会导致包含了b.h/c.h的源代码重新编译。 + +头文件循环依赖直接体现了架构设计上的不合理,可通过架构优化来避免。 + + +### 规则4.2 头文件必须编写#define保护,防止重复包含 + +为防止头文件被多重包含,所有头文件都应当使用 #define 作为包含保护;不要使用 #pragma once + +定义包含保护符时,应该遵守如下规则: +- 保护符使用唯一名称;建议考虑项目源代码树顶层以下的文件路径 +- 不要在受保护部分的前后放置代码或者注释,文件头注释除外。 + +假定 timer 模块的 timer.h,其目录为 `timer/include/timer.h`。其保护符若使用 'TIME_H' 很容易不唯一,所以使用项目源代码树的全路径,如: +```c +#ifndef TIMER_INCLUDE_TIMER_H +#define TIMER_INCLUDE_TIMER_H + +... + +#endif +``` + +### 规则4.3 禁止通过声明的方式引用外部函数接口、变量 + +只能通过包含头文件的方式使用其他模块或文件提供的接口。 +通过 extern 声明的方式使用外部函数接口、变量,容易在外部接口改变时可能导致声明和定义不一致。 +同时这种隐式依赖,容易导致架构腐化。 + +不符合规范的案例: +a.c 内容 +```c +extern int Foo(void); // Bad: 通过 extern 的方式引用外部函数 + +void Bar(void) +{ + int i = Foo(); // 这里使用了外部接口 Foo + ... +} +``` + +应该改为: +a.c 内容 +```c +#include "b.h" // Good: 通过包含头文件的方式使用其他.c提供的接口 + +void Bar(void) +{ + int i = Foo(); + ... +} +``` + +b.h 内容 +```c +int Foo(void); +``` + +b.c内容 +```c +int Foo(void) +{ + // Do something +} +``` + +例外,有些场景需要引用其内部函数,但并不想侵入代码时,可以 extern 声明方式引用。 +如: +针对某一内部函数进行单元测试时,可以通过 extern 声明来引用被测函数; +当需要对某一函数进行打桩、打补丁处理时,允许 extern 声明该函数。 + +### 规则4.4 禁止在 extern "C" 中包含头文件 + +在 extern "C" 中包含头文件,有可能会导致 extern "C" 嵌套,部分编译器对 extern "C" 嵌套层次有限制,嵌套层次太多会编译错误。 + +extern "C" 通常出现在 C,C++ 混合编程的情况下,在 extern "C" 中包含头文件,可能会导致被包含头文件的原有意图遭到破坏,比如链接规范被不正确地更改。 + +示例,存在a.h和b.h两个头文件: +a.h 内容 +```c +... +#ifdef __cplusplus +void Foo(int); +#define A(value) Foo(value) +#else +void A(int) +#endif +``` + +b.h 内容 +```c +... +#ifdef __cplusplus +extern "C" { +#endif + +#include "a.h" +void B(void); + +#ifdef __cplusplus +} +#endif +``` + +使用C++预处理器展开b.h,将会得到 +```c +extern "C" { + void Foo(int); + void B(void); +} +``` + +按照 a.h 作者的本意,函数 Foo 是一个 C++ 自由函数,其链接规范为 "C++"。 +但在 b.h 中,由于 `#include "a.h"` 被放到了 `extern "C"` 的内部,函数 Foo 的链接规范被不正确地更改了。 + +例外: +如果在 C++ 编译环境中,想引用纯C的头文件,这些C头文件并没有 `extern "C"` 修饰。非侵入式的做法是,在 `extern "C"` 中去包含C头文件。 + +# 5 函数 + +函数的作用:避免重复代码、增加可重用性;分层,降低复杂度、隐藏实现细节,使程序更加模块化,从而更有利于程序的阅读,维护。 + +函数应该简洁、短小。 +一个函数只完成一件事情。 + +## 函数设计 + +函数设计的精髓:编写整洁函数,同时把代码有效组织起来。代码简单直接、不隐藏设计者的意图、用干净利落的抽象和直截了当的控制语句将函数有机组织起来。 + +### 规则5.1 避免函数过长,函数不超过50行(非空非注释) + +函数应该可以一屏显示完 (50行以内),只做一件事情,而且把它做好。 + +过长的函数往往意味着函数功能不单一,过于复杂,或过分呈现细节,未进行进一步抽象。 + +例外: +考虑代码的聚合性与功能的全面性,某些函数可能会超过50行,但前提是不影响代码的可读性与简洁。 +这些例外的函数应该是极少的,例如特定算法处理。 + +即使一个长函数现在工作的非常好, 一旦有人对其修改, 有可能出现新的问题, 甚至导致难以发现的bug。 +建议将其拆分为更加简短并易于管理的若干函数,以便于他人阅读和修改代码。 + +### 规则5.2 避免函数的代码块嵌套过深,不要超过4层 + +函数的代码块嵌套深度指的是函数中的代码控制块(例如:if、for、while、switch等)之间互相包含的深度。 +每级嵌套都会增加阅读代码时的脑力消耗,因为需要在脑子里维护一个“栈”(比如,进入条件语句、进入循环等等)。 +应该做进一步的功能分解,从而避免使代码的阅读者一次记住太多的上下文。 + +使用`卫语句`可以有效的减少 if 相关的嵌套层次。例: +原代码嵌套层数是 3: +```c +int Foo(...) +{ + if (received) { + type = GetMsgType(msg); + if (type != UNKNOWN) { + return DealMsg(...); + } + } + return -1; +} +``` + +使用`卫语句`重构,嵌套层数变成 2: +```c +int Foo(...) +{ + if (!received) { // Good: 使用'卫语句' + return -1; + } + + type = GetMsgType(msg); + if (type == UNKNOWN) { + return -1; + } + + return DealMsg(..); +} +``` +例外: +考虑代码的聚合性与功能的全面性,某些函数嵌套可能会超过4层,但前提是不影响代码的可读性与简洁。 +这些例外的函数应该是极少的。 + +### 建议5.1 对函数的错误返回码要全面处理 + +一个函数(标准库中的函数/第三方库函数/用户定义的函数)能够提供一些指示错误发生的方法。 +这可以通过使用错误标记、特殊的返回数据或者其他手段,不管什么时候函数提供了这样的机制,调用程序应该在函数返回时立刻检查错误指示。 + +示例: +```c +char fileHead[128]; +ReadFileHead(fileName, fileHead, sizeof(fileHead)); // Bad: 未检查返回值 + +DealWithFileHead(fileHead, sizeof(fileHead)); // fileHead 可能无效 +``` + +正确写法: +```c +char fileHead[128]; +ret = ReadFileHead(fileName, fileHead, sizeof(fileHead)); +if (ret != OK) { // Good: 确保 fileHead 被有效写入 + return ERROR; +} + +DealWithFileHead(fileHead, sizeof(fileHead)); // 处理文件头 +``` + + +注意,当函数返回值被大量的显式(void)忽略掉时,应当考虑函数返回值的设计是否合理。 +如果所有调用者都不关注函数返回值时,请将函数设计成`void`型。 + +## 函数参数 + +### 建议5.2 设计函数时,优先使用返回值而不是输出参数 + +使用返回值而不是输出参数,可以提高可读性,并且通常提供相同或更好的性能。 + +函数名为 GetXxx、FindXxx 或直接名词作函数名的函数,直接返回对应对象,可读性更好。 + +### 建议5.3 使用强类型参数,避免使用void\* + +尽管不同的语言对待强类型和弱类型有自己的观点,但是一般认为c/c++是强类型语言,既然我们使用的语言是强类型的,就应该保持这样的风格。 +好处是尽量让编译器在编译阶段就检查出类型不匹配的问题。 + +使用强类型便于编译器帮我们发现错误,如下代码中注意函数 `FooListAddNode` 的使用: +```c +struct FooNode { + struct List link; + int foo; +}; + +struct BarNode { + struct List link; + int bar; +} + +void FooListAddNode(void *node) // Bad: 这里用 void * 类型传递参数 +{ + FooNode *foo = (FooNode *)node; + ListAppend(&g_fooList, &foo->link); +} + +void MakeTheList(...) +{ + FooNode *foo; + BarNode *bar; + ... + + FooListAddNode(bar); // Wrong: 这里本意是想传递参数 foo,但错传了 bar,却没有报错 +} + +``` + +上述问题有可能很隐晦,不易轻易暴露,从而破坏性更大。 +如果明确 `FooListAddNode` 的参数类型,而不是 `void *`,则在编译阶段就能发现上述问题。 +```c +void FooListAddNode(FooNode *foo) +{ + ListAppend(&g_fooList, &foo->link); +} +``` + +例外:某些通用泛型接口,需要传入不同类型指针的,可以用 `void *` 入参。 + + +### 建议5.4 模块内部函数参数的合法性检查,由调用者负责 + +对于模块外部传入的参数,必须进行合法性检查,保护程序免遭非法输入数据的破坏。 +模块内部函数调用,缺省由调用者负责保证参数的合法性,如果都由被调用者来检查参数合法性,可能会出现同一个参数,被检查多次,产生冗余代码,很不简洁。 + +由调用者保证入参的合法性,这种契约式编程能让代码逻辑更简洁,可读性更好。 +示例: +```c +int SomeProc(...) +{ + int data; + + bool dataOK = GetData(&data); // 获取数据 + if (!dataOK) { // 检查上一步结果,其实也就保证了数据合法 + return -1; + } + + DealWithData(data); // 调用数据处理函数 + ... +} + +void DealWithData(int data) +{ + if (data < MIN || data > MAX) { // Bad: 调用者已经保证了数据合法性 + return; + } + + ... +} +``` + +### 建议5.5 函数的指针参数如果不是用于修改所指向的对象就应该声明为指向const的指针 + +const 指针参数,将限制函数通过该指针修改所指向对象,使代码更牢固、安全。 + +示例:C99标准 7.21.4.4 中strncmp 的例子,不变参数声明为const。 +```c +int strncmp(const char *s1, const char *s2, size_t n); // Good:不变参数声明为const +``` + +注意: +指针参数要不要加 const 取决于函数设计,而不是看函数实体内有没有发生“修改对象”的动作。 + +### 建议5.6 函数的参数个数不超过5个 + +函数的参数过多,会使得该函数易于受外部(其他部分的代码)变化的影响,从而影响维护工作。函数的参数过多同时也会增大测试的工作量。 + +函数的参数个数不要超过5个,如果超过可以考虑: +- 看能否拆分函数 +- 看能否将相关参数合在一起,定义结构体 + +## 内联函数 + +内联函数是**C99**引入的一种函数优化手段。函数内联能消除函数调用的开销;并得益于内联实现跟调用点代码的合并,编译器有更大的视角,从而完成更多的代码优化。内联函数跟函数式宏比较类似,两者的分析详见[建议6.1](#a6-1)。 + +### 建议5.7 内联函数不超过10行(非空非注释) + +将函数定义成内联一般希望提升性能,但是实际并不一定能提升性能。 +如果函数体短小,则函数内联可以有效的缩减目标代码的大小,并提升函数执行效率。 +反之,函数体比较大,内联展开会导致目标代码的膨胀,特别是当调用点很多时,膨胀得更厉害,反而会降低执行效率。 +内联函数规模建议控制在 **10** 行以内。 + +不要为了提高性能而滥用内联函数。不要过早优化。一般情况,当有实际测试数据证明内联性能更高时,再将函数定义为内联。 +对于类似 setter/getter 短小而且调用频繁的函数,可以定义为内联。 + +### 规则5.3 被多个源文件调用的内联函数要放在头文件中定义 + +内联函数是在编译时内联展开,因此要求内联函数定义必须在调用此函数的每个源文件内可见。 +如下所示代码,inline.h 只有`SomeInlineFunc`函数的声明而没有定义。other.c包含inline.h,调用`SomeInlineFunc`时无法内联。 + +inline.h +```c +inline int SomeInlineFunc(void); +``` + +inline.c +```c +inline int SomeInlineFunc(void) +{ + // 实现代码 +} +``` + +other.c +```c +#include "inline.h" +int OtherFunc(void) +{ + int ret = SomeInlineFunc(); +} +``` + +由于这个限制,多个源文件如果要调用同一个内联函数,需要将内联函数的定义放在头文件中。 +**gnu89** 在内联函数实现上跟**C99**标准有差异,兼容做法是将函数声明为 **static inline**。 + +# 6 宏 + +## 函数式宏(function-like macro) + +函数式宏是指形如函数的宏(示例代码如下所示),其包含若干条语句来实现某一特定功能。 +```c +#define ASSERT(x) do { \ + if (!(x)) { \ + printk(KERN_EMERG "assertion failed %s: %d: %s\n", \ + __FILE__, __LINE__, #x); \ + BUG(); \ + } \ +} while (0) +``` + +### 建议6.1 使用函数代替函数式宏 + +定义函数式宏前,应考虑能否用函数替代。对于可替代场景,建议用函数替代宏。 +函数式宏的缺点如下: + +- 函数式宏缺乏类型检查,不如函数调用检查严格。示例代码[见下](#macro_lack_of_type_check__example)。 +- 宏展开时宏参数不求值,可能会产生非预期结果,详见[规则6.1](#r6-1)和[规则6.3](#r6-3)。 +- 宏没有独立的作用域,跟控制流语句配合时,可能会产生如[规则6.2](#r6-2)描述的非预期结果。 +- 宏的技巧性太强(参见下面的规则),例如`#`的用法和无处不在的括号,影响可读性。 +- 在特定场景下必须用特定编译器对宏的扩展,如 `gcc` 的 `statement expression`,可移植性也不好。 +- 宏在预编译阶段展开后,在其后编译、链接和调试时都不可见;而且包含多行的宏会展开为一行。函数式宏难以调试、难以打断点,不利于定位问题。 +- 对于包含大量语句的宏,在每个调用点都要展开。如果调用点很多,会造成代码空间的膨胀。 + +函数式宏缺乏类型检查的示例代码: + +```c +#define MAX(a, b) (((a) < (b)) ? (b) : (a)) + +int Max(int a, int b) +{ + return (a < b) ? b : a; +} + +int TestMacro(void) +{ + unsigned int a = 1; + int b = -1; + + (void)printf("MACRO: max of a(%u) and b(%d) is %d\n", a, b, MAX(a, b)); + (void)printf("FUNC : max of a(%u) and b(%d) is %d\n", a, b, Max(a, b)); + return 0; +} +``` + +由于宏缺乏类型检查,`MAX`中的`a`和`b`的比较提升为无符号数的比较,结果是**a < b**。输出结果是: + +``` +MACRO: max of a(1) and b(-1) is -1 +FUNC : max of a(1) and b(-1) is 1 +``` + +函数没有宏的上述缺点。但是,函数相比宏,最大的劣势是执行效率不高(增加函数调用的开销和编译器优化的难度)。 +为此,C99标准引入了内联函数(gcc在标准之前就引入了内联函数)。 + +内联函数跟宏类似,也是在调用点展开。不同之处在于内联函数是在编译时展开。 +内联函数兼具函数和宏的优点: + +- 内联函数/函数执行严格的类型检查 +- 内联函数/函数的入参求值只会进行一次 +- 内联函数就地展开,没有函数调用的开销 +- 内联函数比函数优化得更好 + +对于性能敏感的代码,可以考虑用内联函数代替函数式宏。 +函数和内联函数不能完全替代函数式宏,函数式宏在某些场景更适合。 +比如,在日志记录场景下,使用带可变参和默认参数的函数式宏更方便: + +```c +int ErrLog(const char *file, unsigned long line, const char *fmt, ...); +#define ERR_LOG(fmt, ...) ErrLog(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +``` + + +### 规则6.1 定义宏时,宏参数要使用完备的括号 + +宏参数在宏展开时只是文本替换,在编译时再求值。文本替换后,宏包含的语句跟调用点代码合并。 +合并后的表达式因为操作符的优先级和结合律,可能会导致计算结果跟期望的不同,尤其是当宏参数在一个表达式中时。 + +如下所示,是一种错误的写法: +```c +#define SUM(a, b) a + b // Bad. +``` + +下面这样调用宏,执行结果跟预期不符: +`100 / SUM(2, 8)` 将扩展成 `(100 / 2) + 8`,预期结果则是`100 / (2 + 8)`。 +这个问题可以通过将整个表示式加上括号来解决,如下所示: +```c +#define SUM(a, b) (a + b) // Bad. +``` + +但是这种改法在下面这种场景又有问题: +`SUM(1 << 2, 8)`扩展成`1 << (2 + 8)`(因为`<<`优先级低于`+`),跟预期结果`(1 << 2) + 8`不符。 + +这个问题可以通过将每个宏参数都加上括号来解决,如下所示: +```c +#define SUM(a, b) (a) + (b) // Bad. +``` + +再看看第三种问题场景:`SUM(2, 8) * 10` 。扩展后的结果为 `(2) + ((8) * 10)`,跟预期结果`(2 + 8) * 10`不符。 + +综上所述,正确的写法如下: +```c +#define SUM(a, b) ((a) + (b)) // Good. +``` + +但是要避免滥用括号。如下所示,单独的数字或标识符加括号毫无意义。 +```c +#define SOME_CONST 100 // Good: 单独的数字无需括号 +#define ANOTHER_CONST (-1) // Good: 负数需要使用括号 + +#define THE_CONST SOME_CONST // Good: 单独的标识符无需括号 +``` + +下列情况需要注意: +- 宏参数参与 '#', '##' 操作时,不要加括号 +- 宏参数参与字符串拼接时,不要加括号 +- 宏参数作为独立部分,在赋值(包括+=, -=等)操作的某一边时,无需括号 +- 宏参数作为独立部分,在逗号表达式,函数或宏调用列表中,无需括号 + +举例: +```c +#define MAKE_STR(x) #x // x 不要加括号 + +#define HELLO_STR(obj) "Hello, " obj // obj 不要加括号 + +#define ADD_3(sum, a, b, c) (sum = (a) + (b) + (c)) // a, b, c 需要括号;而 sum 无需括号 + +#define FOO(a, b) Bar((a) + 1, b) // a 需要括号;而 b 无需括号 +``` + + +### 规则6.2 包含多条语句的函数式宏的实现语句必须放在 do-while(0) 中 + +宏本身没有代码块的概念。当宏在调用点展开后,宏内定义的表达式和变量融合到调用代码中,可能会出现变量名冲突和宏内语句被分割等问题。 +通过 do-while(0) 显式为宏加上边界,让宏有独立的作用域,并且跟分号能更好的结合而形成单条语句,从而规避此类问题。 + +如下所示的宏是错误的用法(为了说明问题,下面示例代码稍不符规范): + +```c +// Not Good. +#define FOO(x) \ + (void)printf("arg is %d\n", (x)); \ + DoSomething((x)); +``` + +当像下面示例代码这样调用宏,for循环只执行了宏的第一条语句,宏的后一条语句只在循环结束后执行一次。 + +```c +for (i = 1; i < 10; i++) + FOO(i); +``` + +用大括号将`FOO`定义的语句括起来可以解决上面的问题: + +```c +#define FOO(x) { \ + (void)printf("arg is %d\n", (x)); \ + DoSomething((x)); \ +} +``` + +由于大括号跟分号没有关联。大括号后紧跟的分号,是另外一个语句。 +如下示例代码,会出现'悬挂else' 编译报错: + +```c +if (condition) + FOO(10); +else + FOO(20); +``` + +正确的写法是用 do-while(0) 把执行体括起来,如下所示: + +```c +// Good. +#define FOO(x) do { \ + (void)printf("arg is %d\n", (x)); \ + DoSomething((x)); \ +} while (0) +``` + +例外: +- 包含 break, continue 语句的宏可以例外。使用此类宏务必特别小心。 +- 宏中包含不完整语句时,可以例外。比如用宏封装 for 循环的条件部分。 +- 非多条语句,或单个 if/for/while/switch 语句,可以例外。 + +### 规则6.3 不允许把带副作用的表达式作为参数传递给函数式宏 + +由于宏只是文本替换,对于内部多次使用同一个宏参数的函数式宏,将带副作用的表达式作为宏参数传入会导致非预期的结果。 +如下所示,宏`SQUARE`本身没有问题,但是使用时将带副作用的`a++`传入导致`a`的值在`SQUARE`执行后跟预期不符: + +```c +#define SQUARE(a) ((a) * (a)) + +int a = 5; +int b; +b = SQUARE(a++); // Bad: 实际 a 自增加了 2 次 +``` + +`SQUARE(a++)`展开后为`((a++) * (a++))`,变量`a`自增了两次,其值为`7`,而不是预期的`6`。 + +正确的写法如下所示: + +```c +b = SQUARE(a); +a++; // 结果:a = 6,只自增了一次。 +``` + +此外,如果参数包含函数调用,宏展开后,函数可能会被重复调用。 +如果函数执行结果相同,则存在浪费;如果函数多次调用结果不一样,执行结果可能不符合预期。 + + +### 建议6.2 函数式宏定义中慎用 return、goto、continue、break 等改变程序流程的语句 + +宏中使用 return、goto、continue、break 等改变流程的语句,虽然能简化代码,但同时也隐藏了真实流程,不易于理解,容易导致资源泄漏等问题。 + +首先,宏封装 return 容易导致过度封装和使用。 +如下代码,`status`的判断是主干流程的一部分,用宏封装起来后,变得不直观了,阅读时习惯性把`RETURN_IF`宏忽略掉了,从而导致对主干流程的理解有偏差。 + +```c +#define LOG_AND_RETURN_IF_FAIL(ret, fmt, ...) do { \ + if ((ret) != OK) { \ + (void)ErrLog(fmt, ##__VA_ARGS__); \ + return (ret); \ + } \ +} while (0) + +#define RETURN_IF(cond, ret) do { \ + if (cond) { \ + return (ret); \ + } \ +} while (0) + +ret = InitModuleA(a, b, &status); +LOG_AND_RETURN_IF_FAIL(ret, "Init module A failed!"); // OK. + +RETURN_IF(status != READY, ERR_NOT_READY); // Bad: 重要逻辑不明显 + +ret = InitModuleB(c); +LOG_AND_RETURN_IF_FAIL(ret, "Init module B failed!"); // OK. +``` + +其次,宏封装 return 也容易引发内存泄漏。再看一个例子: +```c +#define CHECK_PTR(ptr, ret) do { \ + if ((ptr) == NULL) { \ + return (ret); \ + } \ +} while (0) + +... + +mem1 = MemAlloc(...); +CHECK_PTR(mem1, ERR_CODE_XXX); + +mem2 = MemAlloc(...); +CHECK_PTR(mem2, ERR_CODE_XXX); // Wrong: 内存泄漏 +``` + +如果 `mem2` 申请内存失败了,`CHECK_PTR` 会直接返回,而没有释放 `mem1`。 +除此之外,`CHECK_PTR` 宏命名也不好,宏名只反映了检查动作,没有指明结果。只有看了宏实现才知道指针为空时返回失败。 + +综上所述: +不推荐宏定义中封装 return、goto、continue、break 等改变程序流程的语句; +对于返回值判断等异常处理场景可以例外。 + +注意: +**包含 return、goto、continue、break 等改变流程语句的宏命名,务必要体现对应关键字。** + + +### 建议6.3 函数式宏不超过10行(非空非注释) + +函数式宏本身的一大问题是比函数更难以调试和定位,特别是宏过长,调试和定位的难度更大。 +而且宏扩展会导致目标代码的膨胀。建议函数式宏不要超过10行。 + +# 7 变量 + +在C语言编码中,除了函数,最重要的就是变量。 +变量在使用时,应始终遵循“职责单一”原则。 +按作用域区分,变量可分为全局变量和局部变量。 + +## 全局变量 + +尽量不用或少用全局变量。 +在程序设计中,全局变量是在所有作用域都可访问的变量。通常,使用不必要的全局变量被认为是坏习惯。 + +使用全局变量的缺点: +- 破坏函数的独立性和可移植性,使函数对全局变量产生依赖,存在耦合; +- 降低函数的代码可读性和可维护性。当多个函数读写全局变量时,某一时刻其取值可能不是确定的,对于代码的阅读和维护不利; +- 在并发编程环境中,使用全局变量会破坏函数的可重入性,需要增加额外的同步保护处理才能确保数据安全。 + +如不可避免,对全局变量的读写应集中封装。 + +### 规则7.1 模块间,禁止使用全局变量作接口 + +全局变量是模块内部的具体实现,不推荐但允许跨文件使用,但禁止作为模块接口暴露出去。 +对全局变量的使用应该尽量集中,如果本模块的数据需要对外部模块开放,应提供对应函数接口。 + +## 局部变量 + +### 规则7.2 严禁使用未经初始化的变量 + +这里的变量,指的是局部动态变量,并且还包括内存堆上申请的内存块。 +因为他们的初始值都是不可预料的,所以禁止未经有效初始化就直接读取其值。 + +```c +void Foo(...) +{ + int data; + Bar(data); // Bad: 未初始化就使用 + ... +} +``` + +如果有不同分支,要确保所有分支都得到初始化后才能使用: +```c +void Foo(...) +{ + int data; + if (...) { + data = 100; + } + Bar(data); // Bad: 部分分支该值未初始化 + ... +} +``` + +未经初始化就使用,一般静态检查工具是可以检查出来的。 +如 PCLint 工具,针对上述两个例子分别会报错: +>Warning 530: Symbol 'data' (line ...) not initialized +>Warning 644: Variable 'data' (line ...) may not have been initialized + +### 规则7.3 禁止无效、冗余的变量初始化 + +如果没有确定的初始值,而仍然进行初始化,不仅不简洁,反而不安全,可能会引入更难发现的问题。 + +常见的冗余初始化: +```c +int cnt = 0; // Bad: 冗余初始化,将会被后面直接覆盖 +... +cnt = GetXxxCnt(); +... +``` + +对于后续有条件赋值的变量,可以在定义时初始化成默认值 +```c +char *buf = NULL; // Good: 这里用 NULL 代表默认值 +if (condition) { + buf = malloc(MEM_SIZE); +} +... +if (buf != NULL) { // 判断是否申请过内存 + free(buf); +} +``` + +针对大数组的冗余清零,更是会影响到性能。 +```c +char buf[VERY_BIG_SIZE] = {0}; +memset(buf, 0, sizeof(buf)); // Bad: 冗余清零 +``` + +无效初始化,隐藏更大问题的反例: +```c +void Foo(...) +{ + int data = 0; // Bad: 习惯性的进行初始化 + + UseData(data); // 使用数据,本应该写在获取数据后面 + data = GetData(...); // 获取数据 + ... +} +``` +上例代码,如果没有赋 0 初始化,静态检查工具可以帮助发现“未经初始化就直接使用”的问题。 +但因为无效初始化,“使用数据”与“获取数据”写颠倒的缺陷,不能被轻易发现。 + +因此,应该写简洁的代码,对变量或内存块进行正确、必要的初始化。 + +C99不再限制局部变量定义必须在语句之前,可以按需定义,即在靠近变量使用的地方定义变量。 +这种简洁的做法,不仅将变量作用域限制更小,而且更方便阅读和维护,还能解决定义变量时不知该怎么初始化的问题。 +如果编译环境支持,建议按需定义。 + +**例外:** +**遵从“安全规范”要求,指针变量、表示资源描述符的变量、BOOL变量不作要求。** + +### 规则7.4 不允许使用魔鬼数字 + +所谓魔鬼数字即看不懂、难以理解的数字。 +魔鬼数字并非一个非黑即白的概念,看不懂也有程度,需要结合代码上下文和业务相关知识来判断 + +例如数字 12,在不同的上下文中情况是不一样的: +`type = 12;` 就看不懂,但 `month = year * 12;` 就能看懂。 +数字 0 有时候也是魔鬼数字,比如 `status = 0;` 并不能表达是什么状态。 + +解决途径: +对于单点使用的数字,可以增加注释说明 +对于多处使用的数字,必须定义宏或const 变量,并通过符号命名自注释。 + +禁止出现下列情况: +没有通过符号来解释数字含义,如 `#define ZERO 0` +符号命名限制了其取值,如 `#define XX_TIMER_INTERVAL_300MS 300` + +# 8 编程实践 + +## 表达式 + +### 建议8.1 表达式的比较,应当遵循左侧倾向于变化、右侧倾向于不变的原则 + +当变量与常量比较时,如果常量放左边,如 `if (MAX == v)` 不符合阅读习惯,而 `if (MAX > v)` 更是难于理解。 +应当按人的正常阅读、表达习惯,将常量放右边。写成如下方式: +```c +if (v == MAX) ... +if (v < MAX) ... +``` + +也有特殊情况,如:`if (MIN < v && v < MAX)` 用来描述区间时,前半段是常量在左的。 + +不用担心将 '==' 误写成 '=',因为 `if (v = MAX)` 会有编译告警,其他静态检查工具也会报错。让工具去解决笔误问题,代码要符合可读性第一。 + +### 规则8.1 含有变量自增或自减运算的表达式中禁止再次引用该变量 + +含有变量自增或自减运算的表达式中,如果再引用该变量,其结果在C标准中未明确定义。各个编译器或者同一个编译器不同版本实现可能会不一致。 +为了更好的可移植性,不应该对标准未定义的运算次序做任何假设。 + +注意,运算次序的问题不能使用括号来解决,因为这不是优先级的问题。 + +示例: +```c +x = b[i] + i++; // Bad: b[i]运算跟 i++,先后顺序并不明确。 +``` +正确的写法是将自增或自减运算单独放一行: +```c +x = b[i] + i; +i++; // Good: 单独一行 +``` + +函数参数: +```c +Func(i++, i); // Bad: 传递第2个参数时,不确定自增运算有没有发生 +``` +正确的写法: +```c +i++; // Good: 单独一行 +x = Func(i, i); +``` + + +### 建议8.2 用括号明确表达式的操作顺序,避免过分依赖默认优先级 + +可以使用括号强调表达式操作顺序,防止因默认的优先级与设计思想不符而导致程序出错。 +然而过多的括号会分散代码使其降低了可读性,应适度使用。 + +当表达式包含不常用,优先级易混淆的操作符时,推荐使用括号,比如位操作符: +```c +c = (a & 0xFF) + b; /* 涉及位操作符,需要括号 */ +``` + +## 语句 + +### 规则8.2 switch语句要有default分支 + +大部分情况下,switch语句中要有default分支,保证在遗漏case标签处理时能够有一个缺省的处理行为。 + +特例: +如果switch条件变量是枚举类型,并且 case 分支覆盖了所有取值,则加上default分支处理有些多余。 +现代编译器都具备检查是否在switch语句中遗漏了某些枚举值的case分支的能力,会有相应的warning提示。 +```c +enum Color { + RED, + BLUE +}; + +// 因为switch条件变量是枚举值,这里可以不用加default处理分支 +switch (color) { + case RED: + DoRedThing(); + break; + case BLUE: + DoBlueThing(); + ... + break; +} +``` + +### 建议8.3 慎用 goto 语句 + +goto语句会破坏程序的结构性,所以除非确实需要,最好不使用goto语句。使用时,也只允许跳转到本函数goto语句之后的语句。 + +goto语句通常用来实现函数单点返回。 +同一个函数体内部存在大量相同的逻辑但又不方便封装成函数的情况下,譬如反复执行文件操作, +对文件操作失败以后的处理部分代码(譬如关闭文件句柄,释放动态申请的内存等等), +一般会放在该函数体的最后部分,在需要的地方就goto到那里,这样代码反而变得清晰简洁。 +实际也可以封装成函数或者封装成宏,但是这么做会让代码变得没那么直接明了。 + +示例: +```c +// Good: 使用 goto 实现单点返回 +int SomeInitFunc(void) +{ + void *p1; + void *p2 = NULL; + void *p3 = NULL; + + p1 = malloc(MEM_LEN); + if (p1 == NULL) { + goto EXIT; + } + + p2 = malloc(MEM_LEN); + if (p2 == NULL) { + goto EXIT; + } + + p3 = malloc(MEM_LEN); + if (p3 == NULL) { + goto EXIT; + } + + DoSomething(p1, p2, p3); + return 0; // OK. + +EXIT: + if (p3 != NULL) { + free(p3); + } + if (p2 != NULL) { + free(p2); + } + if (p1 != NULL) { + free(p1); + } + return -1; // Failed! +} +``` + +## 类型转换 + +### 建议8.4 尽量减少没有必要的数据类型默认转换与强制转换 + +当进行数据类型强制转换时,其数据的意义、转换后的取值等都有可能发生变化,而这些细节若考虑不周,就很有可能留下隐患。 + +如下赋值,多数编译器不产生告警,但值的含义还是稍有变化。 +```c +char ch; +unsigned short int exam; + +ch = -1; +exam = ch; // Bad: 编译器不产生告警,此时exam为0xFFFF。 +``` diff --git a/contribute/OpenHarmony-cpp-coding-style-guide.md b/contribute/OpenHarmony-cpp-coding-style-guide.md old mode 100755 new mode 100644 index 9eaeb589e8aaa5b757d3672757685777ead3226b..75f746a9e51fe9951674f650fd11dea842797ab5 --- a/contribute/OpenHarmony-cpp-coding-style-guide.md +++ b/contribute/OpenHarmony-cpp-coding-style-guide.md @@ -1,2781 +1,2781 @@ -# C++语言编程规范 - -## 目的 -规则并不是完美的,通过禁止在特定情况下有用的特性,可能会对代码实现造成影响。但是我们制定规则的目的“为了大多数程序员可以得到更多的好处”, 如果在团队运作中认为某个规则无法遵循,希望可以共同改进该规则。 -参考该规范之前,希望您具有相应的C++语言基础能力,而不是通过该文档来学习C++语言。 -1. 了解C++语言的ISO标准; -2. 熟知C++语言的基本语言特性,包括C++ 03/11/14/17相关特性; -3. 了解C++语言的标准库; - -## 总体原则 -代码需要在保证功能正确的前提下,满足**可读、可维护、安全、可靠、可测试、高效、可移植**的特征要求。 - -## 重点关注 -1. 约定C++语言的编程风格,比如命名,排版等。 -2. C++语言的模块化设计,如何设计头文件,类,接口和函数。 -3. C++语言相关特性的优秀实践,比如常量,类型转换,资源管理,模板等。 -4. 现代C++语言的优秀实践,包括C++11/14/17中可以提高代码可维护性,提高代码可靠性的相关约定。 - - -## 约定 -**规则**:编程时必须遵守的约定(must) - -**建议**:编程时应该遵守的约定(should) - -本规范适用通用C++标准, 如果没有特定的标准版本,适用所有的版本(C++03/11/14/17)。 - -## 例外 -无论是'规则'还是'建议',都必须理解该条目这么规定的原因,并努力遵守。 -但是,有些规则和建议可能会有例外。 - -在不违背总体原则,经过充分考虑,有充足的理由的前提下,可以适当违背规范中约定。 -例外破坏了代码的一致性,请尽量避免。'规则'的例外应该是极少的。 - -下列情况,应风格一致性原则优先: -**修改外部开源代码、第三方代码时,应该遵守开源代码、第三方代码已有规范,保持风格统一。** - -# 2 命名 -## 通用命名 -__驼峰风格(CamelCase)__ -大小写字母混用,单词连在一起,不同单词间通过单词首字母大写来分开。 -按连接后的首字母是否大写,又分: 大驼峰(UpperCamelCase)和小驼峰(lowerCamelCase) - - -| 类型 | 命名风格 | -| ---------------------------------------- | --------- | -| 类类型,结构体类型,枚举类型,联合体类型等类型定义, 作用域名称 | 大驼峰 | -| 函数(包括全局函数,作用域函数,成员函数) | 大驼峰 | -| 全局变量(包括全局和命名空间域下的变量,类静态变量),局部变量,函数参数,类、结构体和联合体中的成员变量 | 小驼峰 | -| 宏,常量(const),枚举值,goto 标签 | 全大写,下划线分割 | - -注意: -上表中__常量__是指全局作用域、namespace域、类的静态成员域下,以 const或constexpr 修饰的基本数据类型、枚举、字符串类型的变量,不包括数组和其他类型变量。 -上表中__变量__是指除常量定义以外的其他变量,均使用小驼峰风格。 - -## 文件命名 -### 建议2.2.1 C++文件以.cpp结尾,头文件以.h结尾 -我们推荐使用.h作为头文件的后缀,这样头文件可以直接兼容C和C++。 -我们推荐使用.cpp作为实现文件的后缀,这样可以直接区分C++代码,而不是C代码。 - -目前业界还有一些其他的后缀的表示方法: - -- 头文件: .hh, .hpp, .hxx -- cpp文件:.cc, .cxx, .c - -如果当前项目组使用了某种特定的后缀,那么可以继续使用,但是请保持风格统一。 -但是对于本文档,我们默认使用.h和.cpp作为后缀。 - - -### 建议2.2.2 C++文件名和类名保持一致 -C++的头文件和cpp文件名和类名保持一致,使用下划线小写风格。 - -如果有一个类叫DatabaseConnection,那么对应的文件名: -- database_connection.h -- database_connection.cpp - -结构体,命名空间,枚举等定义的文件名类似。 - -## 函数命名 -函数命名统一使用大驼峰风格,一般采用动词或者动宾结构。 -```cpp -class List { -public: - void AddElement(const Element& element); - Element GetElement(const unsigned int index) const; - bool IsEmpty() const; -}; - -namespace Utils { - void DeleteUser(); -} -``` - -## 类型命名 - -类型命名采用大驼峰命名风格。 -所有类型命名——类、结构体、联合体、类型定义(typedef)、枚举——使用相同约定,例如: -```cpp -// classes, structs and unions -class UrlTable { ... -class UrlTableTester { ... -struct UrlTableProperties { ... -union Packet { ... - -// typedefs -typedef std::map PropertiesMap; - -// enums -enum UrlTableErrors { ... -``` - -对于命名空间的命名,建议使用大驼峰: -```cpp -// namespace -namespace OsUtils { - -namespace FileUtils { - -} - -} -``` - - -### 建议2.4.1 避免滥用 typedef或者#define 对基本类型起别名 -除有明确的必要性,否则不要用 typedef/#define 对基本数据类型进行重定义。 -优先使用``头文件中的基本类型: - -| 有符号类型 | 无符号类型 | 描述 | -| -------- | --------- | ---------------- | -| int8_t | uint8_t | 宽度恰为8的有/无符号整数类型 | -| int16_t | uint16_t | 宽度恰为16的有/无符号整数类型 | -| int32_t | uint32_t | 宽度恰为32的有/无符号整数类型 | -| int64_t | uint64_t | 宽度恰为64的有/无符号整数类型 | -| intptr_t | uintptr_t | 足以保存指针的有/无符号整数类型 | - - -## 变量命名 -通用变量命名采用小驼峰,包括全局变量,函数形参,局部变量,成员变量。 -```cpp -std::string tableName; // Good: 推荐此风格 -std::string tablename; // Bad: 禁止此风格 -std::string path; // Good: 只有一个单词时,小驼峰为全小写 -``` - -### 规则2.5.1 全局变量应增加 'g_' 前缀,静态变量命名不需要加特殊前缀 -全局变量是应当尽量少使用的,使用时应特别注意,所以加上前缀用于视觉上的突出,促使开发人员对这些变量的使用更加小心。 -- 全局静态变量命名与全局变量相同。 -- 函数内的静态变量命名与普通局部变量相同。 -- 类的静态成员变量和普通成员变量相同。 - -```cpp -int g_activeConnectCount; - -void Func() -{ - static int packetCount = 0; - ... -} -``` - -### 规则2.5.2 类的成员变量命名以小驼峰加后下划线组成 - -```cpp -class Foo { -private: - std::string fileName_; // 添加_后缀,类似于K&R命名风格 -}; -``` -对于struct/union的成员变量,仍采用小驼峰不加后缀的命名方式,与局部变量命名风格一致。 - -## 宏、常量、枚举命名 -宏、枚举值采用全大写,下划线连接的格式。 -全局作用域内,有名和匿名namespace内的 const 常量,类的静态成员常量,全大写,下划线连接;函数局部 const 常量和类的普通const成员变量,使用小驼峰命名风格。 - -```cpp -#define MAX(a, b) (((a) < (b)) ? (b) : (a)) // 仅对宏命名举例,并不推荐用宏实现此类功能 - -enum TintColor { // 注意,枚举类型名用大驼峰,其下面的取值是全大写,下划线相连 - RED, - DARK_RED, - GREEN, - LIGHT_GREEN -}; - -int Func(...) -{ - const unsigned int bufferSize = 100; // 函数局部常量 - char *p = new char[bufferSize]; - ... -} - -namespace Utils { - const unsigned int DEFAULT_FILE_SIZE_KB = 200; // 全局常量 -} - -``` - -# 3 格式 - -## 行宽 - -### 建议3.1.1 行宽不超过 120 个字符 -建议每行字符数不要超过 120 个。如果超过120个字符,请选择合理的方式进行换行。 - -例外: -- 如果一行注释包含了超过120 个字符的命令或URL,则可以保持一行,以方便复制、粘贴和通过grep查找; -- 包含长路径的 #include 语句可以超出120 个字符,但是也需要尽量避免; -- 编译预处理中的error信息可以超出一行。 -预处理的 error 信息在一行便于阅读和理解,即使超过 120 个字符。 -```cpp -#ifndef XXX_YYY_ZZZ -#error Header aaaa/bbbb/cccc/abc.h must only be included after xxxx/yyyy/zzzz/xyz.h, because xxxxxxxxxxxxxxxxxxxxxxxxxxxxx -#endif -``` - -## 缩进 - -### 规则3.2.1 使用空格进行缩进,每次缩进4个空格 -只允许使用空格(space)进行缩进,每次缩进为 4 个空格。不允许使用Tab符进行缩进。 -当前几乎所有的集成开发环境(IDE)都支持配置将Tab符自动扩展为4空格输入;请配置你的IDE支持使用空格进行缩进。 - -## 大括号 -### 规则3.3.1 使用 K&R 缩进风格 -__K&R风格__ -换行时,函数(不包括lambda表达式)左大括号另起一行放行首,并独占一行;其他左大括号跟随语句放行末。 -右大括号独占一行,除非后面跟着同一语句的剩余部分,如 do 语句中的 while,或者 if 语句的 else/else if,或者逗号、分号。 - -如: -```cpp -struct MyType { // 跟随语句放行末,前置1空格 - ... -}; - -int Foo(int a) -{ // 函数左大括号独占一行,放行首 - if (...) { - ... - } else { - ... - } -} -``` -推荐这种风格的理由: - -- 代码更紧凑; -- 相比另起一行,放行末使代码阅读节奏感上更连续; -- 符合后来语言的习惯,符合业界主流习惯; -- 现代集成开发环境(IDE)都具有代码缩进对齐显示的辅助功能,大括号放在行尾并不会对缩进和范围产生理解上的影响。 - - -对于空函数体,可以将大括号放在同一行: -```cpp -class MyClass { -public: - MyClass() : value_(0) {} - -private: - int value_; -}; -``` - -## 函数声明和定义 - -### 规则3.4.1 函数声明和定义的返回类型和函数名在同一行;函数参数列表超出行宽时要换行并合理对齐 -在声明和定义函数的时候,函数的返回值类型应该和函数名在同一行;如果行宽度允许,函数参数也应该放在一行;否则,函数参数应该换行,并进行合理对齐。 -参数列表的左圆括号总是和函数名在同一行,不要单独一行;右圆括号总是跟随最后一个参数。 - -换行举例: -```cpp -ReturnType FunctionName(ArgType paramName1, ArgType paramName2) // Good:全在同一行 -{ - ... -} - -ReturnType VeryVeryVeryLongFunctionName(ArgType paramName1, // 行宽不满足所有参数,进行换行 - ArgType paramName2, // Good:和上一行参数对齐 - ArgType paramName3) -{ - ... -} - -ReturnType LongFunctionName(ArgType paramName1, ArgType paramName2, // 行宽限制,进行换行 - ArgType paramName3, ArgType paramName4, ArgType paramName5) // Good: 换行后 4 空格缩进 -{ - ... -} - -ReturnType ReallyReallyReallyReallyLongFunctionName( // 行宽不满足第1个参数,直接换行 - ArgType paramName1, ArgType paramName2, ArgType paramName3) // Good: 换行后 4 空格缩进 -{ - ... -} -``` - -## 函数调用 -### 规则3.5.1 函数调用入参列表应放在一行,超出行宽换行时,保持参数进行合理对齐 -函数调用时,函数参数列表放在一行。参数列表如果超过行宽,需要换行并进行合理的参数对齐。 -左圆括号总是跟函数名,右圆括号总是跟最后一个参数。 - -换行举例: -```cpp -ReturnType result = FunctionName(paramName1, paramName2); // Good:函数参数放在一行 - -ReturnType result = FunctionName(paramName1, - paramName2, // Good:保持与上方参数对齐 - paramName3); - -ReturnType result = FunctionName(paramName1, paramName2, - paramName3, paramName4, paramName5); // Good:参数换行,4 空格缩进 - -ReturnType result = VeryVeryVeryLongFunctionName( // 行宽不满足第1个参数,直接换行 - paramName1, paramName2, paramName3); // 换行后,4 空格缩进 -``` - -如果函数调用的参数存在内在关联性,按照可理解性优先于格式排版要求,对参数进行合理分组换行。 -```cpp -// Good:每行的参数代表一组相关性较强的数据结构,放在一行便于理解 -int result = DealWithStructureLikeParams(left.x, left.y, // 表示一组相关参数 - right.x, right.y); // 表示另外一组相关参数 -``` - -## if语句 - -### 规则3.6.1 if语句必须要使用大括号 -我们要求if语句都需要使用大括号,即便只有一条语句。 - -理由: -- 代码逻辑直观,易读; -- 在已有条件语句代码上增加新代码时不容易出错; -- 对于在if语句中使用函数式宏时,有大括号保护不易出错(如果宏定义时遗漏了大括号)。 - -```cpp -if (objectIsNotExist) { // Good:单行条件语句也加大括号 - return CreateNewObject(); -} -``` -### 规则3.6.2 禁止 if/else/else if 写在同一行 -条件语句中,若有多个分支,应该写在不同行。 - -如下是正确的写法: - -```cpp -if (someConditions) { - DoSomething(); - ... -} else { // Good: else 与 if 在不同行 - ... -} -``` - -下面是不符合规范的案例: - -```cpp -if (someConditions) { ... } else { ... } // Bad: else 与 if 在同一行 -``` - -## 循环语句 -### 规则3.7.1 循环语句必须使用大括号 -和条件表达式类似,我们要求for/while循环语句必须加上大括号,即便循环体是空的,或循环语句只有一条。 -```cpp -for (int i = 0; i < someRange; i++) { // Good: 使用了大括号 - DoSomething(); -} -``` -```cpp -while (condition) { } // Good:循环体是空,使用大括号 -``` -```cpp -while (condition) { - continue; // Good:continue 表示空逻辑,使用大括号 -} -``` - -坏的例子: -```cpp -for (int i = 0; i < someRange; i++) - DoSomething(); // Bad: 应该加上括号 -``` -```cpp -while (condition); // Bad:使用分号容易让人误解是while语句中的一部分 -``` - -## switch语句 -### 规则3.8.1 switch 语句的 case/default 要缩进一层 -switch 语句的缩进风格如下: -```cpp -switch (var) { - case 0: // Good: 缩进 - DoSomething1(); // Good: 缩进 - break; - case 1: { // Good: 带大括号格式 - DoSomething2(); - break; - } - default: - break; -} -``` - -```cpp -switch (var) { -case 0: // Bad: case 未缩进 - DoSomething(); - break; -default: // Bad: default 未缩进 - break; -} -``` - -## 表达式 - -### 建议3.9.1 表达式换行要保持换行的一致性,运算符放行末 -较长的表达式,不满足行宽要求的时候,需要在适当的地方换行。一般在较低优先级运算符或连接符后面截断,运算符或连接符放在行末。 -运算符、连接符放在行末,表示“未结束,后续还有”。 -例: - -// 假设下面第一行已经不满足行宽要求 -```cpp -if ((currentValue > threshold) && // Good:换行后,逻辑操作符放在行尾 - someConditionsion) { - DoSomething(); - ... -} - -int result = reallyReallyLongVariableName1 + // Good - reallyReallyLongVariableName2; -``` -表达式换行后,注意保持合理对齐,或者4空格缩进。参考下面例子 - -```cpp -int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + - longVaribleName4 + longVaribleName5 + longVaribleName6; // Good: 4空格缩进 - -int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + - longVaribleName4 + longVaribleName5 + longVaribleName6; // Good: 保持对齐 -``` -## 变量赋值 - -### 规则3.10.1 多个变量定义和赋值语句不允许写在一行 -每行只有一个变量初始化的语句,更容易阅读和理解。 - -```cpp -int maxCount = 10; -bool isCompleted = false; -``` - -下面是不符合规范的示例: - -```cpp -int maxCount = 10; bool isCompleted = false; // Bad:多个变量初始化需要分开放在多行,每行一个变量初始化 -int x, y = 0; // Bad:多个变量定义需要分行,每行一个 - -int pointX; -int pointY; -... -pointX = 1; pointY = 2; // Bad:多个变量赋值语句放同一行 -``` -例外:for 循环头、if 初始化语句(C++17)、结构化绑定语句(C++17)中可以声明和初始化多个变量。这些语句中的多个变量声明有较强关联,如果强行分成多行会带来作用域不一致,声明和初始化割裂等问题。 - -## 初始化 -初始化包括结构体、联合体、及数组的初始化 - -### 规则3.11.1 初始化换行时要有缩进,并进行合理对齐 -结构体或数组初始化时,如果换行应保持4空格缩进。 -从可读性角度出发,选择换行点和对齐位置。 - -```cpp -const int rank[] = { - 16, 16, 16, 16, 32, 32, 32, 32, - 64, 64, 64, 64, 32, 32, 32, 32 -}; -``` - -## 指针与引用 -### 建议3.12.1 指针类型"`*`"跟随变量名或者类型,不要两边都留有或者都没有空格 -指针命名: `*`靠左靠右都可以,但是不要两边都有或者都没有空格。 -```cpp -int* p = nullptr; // Good -int *p = nullptr; // Good - -int*p = nullptr; // Bad -int * p = nullptr; // Bad -``` - -例外:当变量被 const 修饰时,"`*`" 无法跟随变量,此时也不要跟随类型。 -```cpp -const char * const VERSION = "V100"; -``` - -### 建议3.12.2 引用类型"`&`"跟随变量名或者类型,不要两边都留有或者都没有空格 -引用命名:`&`靠左靠右都可以,但是不要两边都有或者都没有空格。 -```cpp -int i = 8; - -int& p = i; // Good -int &p = i; // Good -int*& rp = pi; // Good,指针的引用,*& 一起跟随类型 -int *&rp = pi; // Good,指针的引用,*& 一起跟随变量名 -int* &rp = pi; // Good,指针的引用,* 跟随类型,& 跟随变量名 - -int & p = i; // Bad -int&p = i; // Bad -``` - -## 编译预处理 -### 规则3.13.1 编译预处理的"#"统一放在行首,嵌套编译预处理语句时,"#"可以进行缩进 -编译预处理的"#"统一放在行首,即使编译预处理的代码是嵌入在函数体中的,"#"也应该放在行首。 - - -## 空格和空行 -### 规则3.14.1 水平空格应该突出关键字和重要信息,避免不必要的留白 -水平空格应该突出关键字和重要信息,每行代码尾部不要加空格。总体规则如下: - -- if, switch, case, do, while, for等关键字之后加空格; -- 小括号内部的两侧,不要加空格; -- 大括号内部两侧有无空格,左右必须保持一致; -- 一元操作符(& * + ‐ ~ !)之后不要加空格; -- 二元操作符(= + ‐ < > * / % | & ^ <= >= == != )左右两侧加空格 -- 三目运算符(? :)符号两侧均需要空格 -- 前置和后置的自增、自减(++ --)和变量之间不加空格 -- 结构体成员操作符(. ->)前后不加空格 -- 逗号(,)前面不加空格,后面增加空格 -- 对于模板和类型转换(<>)和类型之间不要添加空格 -- 域操作符(::)前后不要添加空格 -- 冒号(:)前后根据情况来判断是否要添加空格 - -常规情况: -```cpp -void Foo(int b) { // Good:大括号前应该留空格 - -int i = 0; // Good:变量初始化时,=前后应该有空格,分号前面不要留空格 - -int buf[BUF_SIZE] = {0}; // Good:大括号内两侧都无空格 -``` - -函数定义和函数调用: -```cpp -int result = Foo(arg1,arg2); - ^ // Bad: 逗号后面需要增加空格 - -int result = Foo( arg1, arg2 ); - ^ ^ // Bad: 函数参数列表的左括号后面不应该有空格,右括号前面不应该有空格 -``` - -指针和取地址 -```cpp -x = *p; // Good:*操作符和指针p之间不加空格 -p = &x; // Good:&操作符和变量x之间不加空格 -x = r.y; // Good:通过.访问成员变量时不加空格 -x = r->y; // Good:通过->访问成员变量时不加空格 -``` - -操作符: -```cpp -x = 0; // Good:赋值操作的=前后都要加空格 -x = -5; // Good:负数的符号和数值之前不要加空格 -++x; // Good:前置和后置的++/--和变量之间不要加空格 -x--; - -if (x && !y) // Good:布尔操作符前后要加上空格,!操作和变量之间不要空格 -v = w * x + y / z; // Good:二元操作符前后要加空格 -v = w * (x + z); // Good:括号内的表达式前后不需要加空格 - -int a = (x < y) ? x : y; // Good: 三目运算符, ?和:前后需要添加空格 -``` - -循环和条件语句: -```cpp -if (condition) { // Good:if关键字和括号之间加空格,括号内条件语句前后不加空格 - ... -} else { // Good:else关键字和大括号之间加空格 - ... -} - -while (condition) {} // Good:while关键字和括号之间加空格,括号内条件语句前后不加空格 - -for (int i = 0; i < someRange; ++i) { // Good:for关键字和括号之间加空格,分号之后加空格 - ... -} - -switch (condition) { // Good: switch 关键字后面有1空格 - case 0: // Good:case语句条件和冒号之间不加空格 - ... - break; - ... - default: - ... - break; -} -``` - -模板和转换 -```cpp -// 尖括号(< and >) 不与空格紧邻, < 前没有空格, > 和 ( 之间也没有. -vector x; -y = static_cast(x); - -// 在类型与指针操作符之间留空格也可以, 但要保持一致. -vector x; -``` - -域操作符 -```cpp -std::cout; // Good: 命名空间访问,不要留空格 - -int MyClass::GetValue() const {} // Good: 对于成员函数定义,不要留空格 -``` - -冒号 -```cpp -// 添加空格的场景 - -// Good: 类的派生需要留有空格 -class Sub : public Base { - -}; - -// 构造函数初始化列表需要留有空格 -MyClass::MyClass(int var) : someVar_(var) -{ - DoSomething(); -} - -// 位域表示也留有空格 -struct XX { - char a : 4; - char b : 5; - char c : 4; -}; -``` - -```cpp -// 不添加空格的场景 - -// Good: 对于public:, private:这种类访问权限的冒号不用添加空格 -class MyClass { -public: - MyClass(int var); -private: - int someVar_; -}; - -// 对于switch-case的case和default后面的冒号不用添加空格 -switch (value) -{ - case 1: - DoSomething(); - break; - default: - break; -} -``` - -注意:当前的集成开发环境(IDE)可以设置删除行尾的空格,请正确配置。 - -### 建议3.14.1 合理安排空行,保持代码紧凑 - -减少不必要的空行,可以显示更多的代码,方便代码阅读。下面有一些建议遵守的规则: -- 根据上下内容的相关程度,合理安排空行; -- 函数内部、类型定义内部、宏内部、初始化表达式内部,不使用连续空行 -- 不使用连续 **3** 个空行,或更多 -- 大括号内的代码块行首之前和行尾之后不要加空行,但namespace的大括号内不作要求。 - -```cpp -int Foo() -{ - ... -} - - - -int Bar() // Bad:最多使用连续2个空行。 -{ - ... -} - - -if (...) { - // Bad:大括号内的代码块行首不要加入空行 - ... - // Bad:大括号内的代码块行尾不要加入空行 -} - -int Foo(...) -{ - // Bad:函数体内行首不要加空行 - ... -} -``` - -## 类 -### 规则3.15.1 类访问控制块的声明依次序是 public:, protected:, private:,缩进和 class 关键字对齐 -```cpp -class MyClass : public BaseClass { -public: // 注意没有缩进 - MyClass(); // 标准的4空格缩进 - explicit MyClass(int var); - ~MyClass() {} - - void SomeFunction(); - void SomeFunctionThatDoesNothing() - { - } - - void SetVar(int var) { someVar_ = var; } - int GetVar() const { return someVar_; } - -private: - bool SomeInternalFunction(); - - int someVar_; - int someOtherVar_; -}; -``` - -在各个部分中,建议将类似的声明放在一起, 并且建议以如下的顺序: 类型 (包括 typedef, using 和嵌套的结构体与类), 常量, 工厂函数, 构造函数, 赋值运算符, 析构函数, 其它成员函数, 数据成员。 - - -### 规则3.15.2 构造函数初始化列表放在同一行或按四格缩进并排多行 -```cpp -// 如果所有变量能放在同一行: -MyClass::MyClass(int var) : someVar_(var) -{ - DoSomething(); -} - -// 如果不能放在同一行, -// 必须置于冒号后, 并缩进4个空格 -MyClass::MyClass(int var) - : someVar_(var), someOtherVar_(var + 1) // Good: 逗号后面留有空格 -{ - DoSomething(); -} - -// 如果初始化列表需要置于多行, 需要逐行对齐 -MyClass::MyClass(int var) - : someVar_(var), // 缩进4个空格 - someOtherVar_(var + 1) -{ - DoSomething(); -} -``` - -# 4 注释 -一般的,尽量通过清晰的架构逻辑,好的符号命名来提高代码可读性;需要的时候,才辅以注释说明。 -注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,**按需注释**。 - -注释内容要简洁、明了、无二义性,信息全面且不冗余。 - -**注释跟代码一样重要。** -写注释时要换位思考,用注释去表达此时读者真正需要的信息。在代码的功能、意图层次上进行注释,即注释解释代码难以表达的意图,不要重复代码信息。 -修改代码时,也要保证其相关注释的一致性。只改代码,不改注释是一种不文明行为,破坏了代码与注释的一致性,让阅读者迷惑、费解,甚至误解。 - -使用英文进行注释。 - -## 注释风格 - -在 C++ 代码中,使用 `/*` `*/`和 `//` 都是可以的。 -按注释的目的和位置,注释可分为不同的类型,如文件头注释、函数头注释、代码注释等等; -同一类型的注释应该保持统一的风格。 - -注意:本文示例代码中,大量使用 '//' 后置注释只是为了更精确的描述问题,并不代表这种注释风格更好。 - -## 文件头注释 -### 规则3.1 文件头注释必须包含版权许可 - -/* - * Copyright (c) 2020 XXX - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -## 函数头注释 -### 规则4.3.1 禁止空有格式的函数头注释 -并不是所有的函数都需要函数头注释; -函数签名无法表达的信息,加函数头注释辅助说明; - -函数头注释统一放在函数声明或定义上方,使用如下风格之一: -使用`//`写函数头 - -```cpp -// 单行函数头 -int Func1(void); - -// 多行函数头 -// 第二行 -int Func2(void); -``` - -使用`/* */`写函数头 -```cpp -/* 单行函数头 */ -int Func1(void); - -/* - * 另一种单行函数头 - */ -int Func2(void); - -/* - * 多行函数头 - * 第二行 - */ -int Func3(void); -``` -函数尽量通过函数名自注释,按需写函数头注释。 -不要写无用、信息冗余的函数头;不要写空有格式的函数头。 - -函数头注释内容可选,但不限于:功能说明、返回值,性能约束、用法、内存约定、算法实现、可重入的要求等等。 -模块对外头文件中的函数接口声明,其函数头注释,应当将重要、有用的信息表达清楚。 - -例: - -```cpp -/* - * 返回实际写入的字节数,-1表示写入失败 - * 注意,内存 buf 由调用者负责释放 - */ -int WriteString(const char *buf, int len); -``` - -坏的例子: -```cpp -/* - * 函数名:WriteString - * 功能:写入字符串 - * 参数: - * 返回值: - */ -int WriteString(const char *buf, int len); -``` -上面例子中的问题: - -- 参数、返回值,空有格式没内容 -- 函数名信息冗余 -- 关键的 buf 由谁释放没有说清楚 - -## 代码注释 -### 规则4.4.1 代码注释放于对应代码的上方或右边 -### 规则4.4.2 注释符与注释内容间要有1空格;右置注释与前面代码至少1空格 -代码上方的注释,应该保持对应代码一样的缩进。 -选择并统一使用如下风格之一: -使用`//` -```cpp - -// 这是单行注释 -DoSomething(); - -// 这是多行注释 -// 第二行 -DoSomething(); -``` - -使用`/*' '*/` -```cpp -/* 这是单行注释 */ -DoSomething(); - -/* - * 另一种方式的多行注释 - * 第二行 - */ -DoSomething(); -``` -代码右边的注释,与代码之间,至少留1空格,建议不超过4空格。 -通常使用扩展后的 TAB 键即可实现 1-4 空格的缩进。 - -选择并统一使用如下风格之一: - -```cpp -int foo = 100; // 放右边的注释 -int bar = 200; /* 放右边的注释 */ -``` -右置格式在适当的时候,上下对齐会更美观。 -对齐后的注释,离左边代码最近的那一行,保证1-4空格的间隔。 -例: - -```cpp -const int A_CONST = 100; /* 相关的同类注释,可以考虑上下对齐 */ -const int ANOTHER_CONST = 200; /* 上下对齐时,与左侧代码保持间隔 */ -``` -当右置的注释超过行宽时,请考虑将注释置于代码上方。 - -### 规则4.4.3 不用的代码段直接删除,不要注释掉 -被注释掉的代码,无法被正常维护;当企图恢复使用这段代码时,极有可能引入易被忽略的缺陷。 -正确的做法是,不需要的代码直接删除掉。若再需要时,考虑移植或重写这段代码。 - -这里说的注释掉代码,包括用 /* */ 和 //,还包括 #if 0, #ifdef NEVER_DEFINED 等等。 - -# 5 头文件 -## 头文件职责 -头文件是模块或文件的对外接口,头文件的设计体现了大部分的系统设计。 -头文件中适合放置接口的声明,不适合放置实现(内联函数除外)。对于cpp文件中内部才需要使用的函数、宏、枚举、结构定义等不要放在头文件中。 -头文件应当职责单一。头文件过于复杂,依赖过于复杂还是导致编译时间过长的主要原因。 - -### 建议5.1.1 每一个.cpp文件应有一个对应的.h文件,用于声明需要对外公开的类与接口 -通常情况下,每个.cpp文件都有一个相应的.h,用于放置对外提供的函数声明、宏定义、类型定义等。 -如果一个.cpp文件不需要对外公布任何接口,则其就不应当存在。 -例外:__程序的入口(如main函数所在的文件),单元测试代码,动态库代码。__ - -示例: -```cpp -// Foo.h - -#ifndef FOO_H -#define FOO_H - -class Foo { -public: - Foo(); - void Fun(); - -private: - int value_; -}; - -#endif -``` - -```cpp -// Foo.cpp -#include "Foo.h" - -namespace { // Good: 对内函数的声明放在.cpp文件的头部,并声明为匿名namespace或者static限制其作用域 - void Bar() - { - } -} - -... - -void Foo::Fun() -{ - Bar(); -} -``` - -## 头文件依赖 -### 规则5.2.1 禁止头文件循环依赖 -头文件循环依赖,指 a.h 包含 b.h,b.h 包含 c.h,c.h 包含 a.h, 导致任何一个头文件修改,都导致所有包含了a.h/b.h/c.h的代码全部重新编译一遍。 -而如果是单向依赖,如a.h包含b.h,b.h包含c.h,而c.h不包含任何头文件,则修改a.h不会导致包含了b.h/c.h的源代码重新编译。 - -头文件循环依赖直接体现了架构设计上的不合理,可通过优化架构去避免。 - - -### 规则5.2.2 头文件必须编写`#define`保护,防止重复包含 -为防止头文件被重复包含,所有头文件都应当使用 #define 保护;不要使用 #pragma once - -定义包含保护符时,应该遵守如下规则: -1)保护符使用唯一名称; -2)不要在受保护部分的前后放置代码或者注释,文件头注释除外。 - -示例:假定timer模块的timer.h,其目录为timer/include/timer.h,应按如下方式保护: - -```cpp -#ifndef TIMER_INCLUDE_TIMER_H -#define TIMER_INCLUDE_TIMER_H -... -#endif -``` - -### 规则5.2.3 禁止通过声明的方式引用外部函数接口、变量 -只能通过包含头文件的方式使用其他模块或文件提供的接口。 -通过 extern 声明的方式使用外部函数接口、变量,容易在外部接口改变时可能导致声明和定义不一致。 -同时这种隐式依赖,容易导致架构腐化。 - -不符合规范的案例: - -// a.cpp内容 -```cpp -extern int Fun(); // Bad: 通过extern的方式使用外部函数 - -void Bar() -{ - int i = Fun(); - ... -} -``` - -// b.cpp内容 -```cpp -int Fun() -{ - // Do something -} -``` -应该改为: - -// a.cpp内容 -```cpp -#include "b.h" // Good: 通过包含头文件的方式使用其他.cpp提供的接口 - -void Bar() -{ - int i = Fun(); - ... -} -``` - -// b.h内容 -```cpp -int Fun(); -``` - -// b.cpp内容 -```cpp -int Fun() -{ - // Do something -} -``` -例外,有些场景需要引用其内部函数,但并不想侵入代码时,可以 extern 声明方式引用。 -如: -针对某一内部函数进行单元测试时,可以通过 extern 声明来引用被测函数; -当需要对某一函数进行打桩、打补丁处理时,允许 extern 声明该函数。 - -### 规则5.2.4 禁止在extern "C"中包含头文件 -在 extern "C" 中包含头文件,有可能会导致 extern "C" 嵌套,部分编译器对 extern "C" 嵌套层次有限制,嵌套层次太多会编译错误。 - -在C,C++混合编程的情况下,在extern "C"中包含头文件,可能会导致被包含头文件的原有意图遭到破坏,比如链接规范被不正确地更改。 - -示例,存在a.h和b.h两个头文件: - -// a.h内容 -```cpp -... -#ifdef __cplusplus -void Foo(int); -#define A(value) Foo(value) -#else -void A(int) -#endif -``` -// b.h内容 -```cpp -... -#ifdef __cplusplus -extern "C" { -#endif - -#include "a.h" -void B(); - -#ifdef __cplusplus -} -#endif -``` - -使用C++预处理器展开b.h,将会得到 -```cpp -extern "C" { - void Foo(int); - void B(); -} -``` - -按照 a.h 作者的本意,函数 Foo 是一个 C++ 自由函数,其链接规范为 "C++"。 -但在 b.h 中,由于 `#include "a.h"` 被放到了 `extern "C"` 的内部,函数 Foo 的链接规范被不正确地更改了。 - -例外: -如果在 C++ 编译环境中,想引用纯C的头文件,这些C头文件并没有` extern "C"` 修饰。非侵入式的做法是,在 `extern "C"` 中去包含C头文件。 - -### 建议5.2.1尽量避免使用前置声明,而是通过`#include`来包含头文件 -前置声明(forward declaration)通常指类、模板的纯粹声明,没伴随着其定义。 - -- 优点: - 1. 前置声明能够节省编译时间,多余的 #include 会迫使编译器展开更多的文件,处理更多的输入。 - 2. 前置声明能够节省不必要的重新编译的时间。 #include 使代码因为头文件中无关的改动而被重新编译多次。 -- 缺点: - 1. 前置声明隐藏了依赖关系,头文件改动时,用户的代码会跳过必要的重新编译过程。 - 2. 前置声明可能会被库的后续更改所破坏。前置声明模板有时会妨碍头文件开发者变动其 API. 例如扩大形参类型,加个自带默认参数的模板形参等等。 - 3. 前置声明来自命名空间` std::` 的 symbol 时,其行为未定义(在C++11标准规范中明确说明)。 - 4. 前置声明了不少来自头文件的 symbol 时,就会比单单一行的 include 冗长。 - 5. 仅仅为了能前置声明而重构代码(比如用指针成员代替对象成员)会使代码变得更慢更复杂。 - 6. 很难判断什么时候该用前置声明,什么时候该用`#include`,某些场景下面前置声明和`#include`互换以后会导致意想不到的结果。 - -所以我们尽可能避免使用前置声明,而是使用#include头文件来保证依赖关系。 - -# 6 作用域 - -## 命名空间 - -### 建议6.1.1 对于cpp文件中不需要导出的变量,常量或者函数,请使用匿名namespace封装或者用static修饰 -在C++ 2003标准规范中,使用static修饰文件作用域的变量,函数等被标记为deprecated特性,所以更推荐使用匿名namespace。 - -主要原因如下: -1. static在C++中已经赋予了太多的含义,静态函数成员变量,静态成员函数,静态全局变量,静态函数局部变量,每一种都有特殊的处理。 -2. static只能保证变量,常量和函数的文件作用域,但是namespace还可以封装类型等。 -3. 统一namespace来处理C++的作用域,而不需要同时使用static和namespace来管理。 -4. static修饰的函数不能用来实例化模板,而匿名namespace可以。 - -但是不要在 .h 中使用中使用匿名namespace或者static。 - -```cpp -// Foo.cpp - -namespace { - const int MAX_COUNT = 20; - void InternalFun() {}; -} - -void Foo::Fun() -{ - int i = MAX_COUNT; - - InternalFun(); -} - -``` - -### 规则6.1.1 不要在头文件中或者#include之前使用using导入命名空间 -说明:使用using导入命名空间会影响后续代码,易造成符号冲突,所以不要在头文件以及源文件中的#include之前使用using导入命名空间。 -示例: - -```cpp -// 头文件a.h -namespace NamespaceA { - int Fun(int); -} -``` - -```cpp -// 头文件b.h -namespace NamespaceB { - int Fun(int); -} - -using namespace NamespaceB; - -void G() -{ - Fun(1); -} -``` - -```cpp -// 源代码a.cpp -#include "a.h" -using namespace NamespaceA; -#include "b.h" - -void main() -{ - G(); // using namespace NamespaceA在#include “b.h”之前,引发歧义:NamespaceA::Fun,NamespaceB::Fun调用不明确 -} -``` - -对于在头文件中使用using导入单个符号或定义别名,允许在模块自定义名字空间中使用,但禁止在全局名字空间中使用。 -```cpp -// foo.h - -#include -using fancy::string; // Bad,禁止向全局名字空间导入符号 - -namespace Foo { - using fancy::string; // Good,可以在模块自定义名字空间中导入符号 - using MyVector = fancy::vector; // Good,C++11可在自定义名字空间中定义别名 -} -``` - - -## 全局函数和静态成员函数 - -### 建议6.2.1 优先使用命名空间来管理全局函数,如果和某个class有直接关系的,可以使用静态成员函数 -说明:非成员函数放在名字空间内可避免污染全局作用域, 也不要用类+静态成员方法来简单管理全局函数。 如果某个全局函数和某个类有紧密联系, 那么可以作为类的静态成员函数。 - -如果你需要定义一些全局函数,给某个cpp文件使用,那么请使用匿名namespace来管理。 -```cpp -namespace MyNamespace { - int Add(int a, int b); -} - -class File { -public: - static File CreateTempFile(const std::string& fileName); -}; -``` - -## 全局常量和静态成员常量 - -### 建议6.3.1 优先使用命名空间来管理全局常量,如果和某个class有直接关系的,可以使用静态成员常量 -说明:全局常量放在命名空间内可避免污染全局作用域, 也不要用类+静态成员常量来简单管理全局常量。 如果某个全局常量和某个类有紧密联系, 那么可以作为类的静态成员常量。 - -如果你需要定义一些全局常量,只给某个cpp文件使用,那么请使用匿名namespace来管理。 -```cpp -namespace MyNamespace { - const int MAX_SIZE = 100; -} - -class File { -public: - static const std::string SEPARATOR; -}; -``` - -## 全局变量 - -### 建议6.4.1 尽量避免使用全局变量,考虑使用单例模式 -说明:全局变量是可以修改和读取的,那么这样会导致业务代码和这个全局变量产生数据耦合。 -```cpp -int g_counter = 0; - -// a.cpp -g_counter++; - -// b.cpp -g_counter++; - -// c.cpp -cout << g_counter << endl; -``` - -使用单实例模式 -```cpp -class Counter { -public: - static Counter& GetInstance() - { - static Counter counter; - return counter; - } // 单实例实现简单举例 - - void Increase() - { - value_++; - } - - void Print() const - { - std::cout << value_ << std::endl; - } - -private: - Counter() : value_(0) {} - -private: - int value_; -}; - -// a.cpp -Counter::GetInstance().Increase(); - -// b.cpp -Counter::GetInstance().Increase(); - -// c.cpp -Counter::GetInstance().Print(); -``` - -实现单例模式以后,实现了全局唯一一个实例,和全局变量同样的效果,并且单实例提供了更好的封装性。 - -例外:有的时候全局变量的作用域仅仅是模块内部,这样进程空间里面就会有多个全局变量实例,每个模块持有一份,这种场景下是无法使用单例模式解决的。 - -# 7 类 - -## 构造,拷贝构造,赋值和析构函数 -构造,拷贝,移动和析构函数提供了对象的生命周期管理方法: -- 构造函数(constructor): `X()` -- 拷贝构造函数(copy constructor):`X(const X&)` -- 拷贝赋值操作符(copy assignment):`operator=(const X&)` -- 移动构造函数(move constructor):`X(X&&)` *C++11以后提供* -- 移动赋值操作符(move assignment):`operator=(X&&)` *C++11以后提供* -- 析构函数(destructor):`~X()` - -### 规则7.1.1 类的成员变量必须显式初始化 -说明:如果类有成员变量,没有定义构造函数,又没有定义默认构造函数,编译器将自动生成一个构造函数,但编译器生成的构造函数并不会对成员变量进行初始化,对象状态处于一种不确定性。 - -例外: -- 如果类的成员变量具有默认构造函数,那么可以不需要显式初始化。 - -示例:如下代码没有构造函数,私有数据成员无法初始化: -```cpp -class Message { -public: - void ProcessOutMsg() - { - //… - } - -private: - unsigned int msgID_; - unsigned int msgLength_; - unsigned char* msgBuffer_; - std::string someIdentifier_; -}; - -Message message; // message成员变量没有初始化 -message.ProcessOutMsg(); // 后续使用存在隐患 - -// 因此,有必要定义默认构造函数,如下: -class Message { -public: - Message() : msgID_(0), msgLength_(0), msgBuffer_(nullptr) - { - } - - void ProcessOutMsg() - { - // … - } - -private: - unsigned int msgID_; - unsigned int msgLength_; - unsigned char* msgBuffer_; - std::string someIdentifier_; // 具有默认构造函数,不需要显式初始化 -}; -``` - -### 建议7.1.1 成员变量优先使用声明时初始化(C++11)和构造函数初始化列表初始化 -说明:C++11的声明时初始化可以一目了然的看出成员初始值,应当优先使用。如果成员初始化值和构造函数相关,或者不支持C++11,则应当优先使用构造函数初始化列表来初始化成员。相比起在构造函数体中对成员赋值,初始化列表的代码更简洁,执行性能更好,而且可以对const成员和引用成员初始化。 - -```cpp -class Message { -public: - Message() : msgLength_(0) // Good,优先使用初始化列表 - { - msgBuffer_ = nullptr; // Bad,不推荐在构造函数中赋值 - } - -private: - unsigned int msgID_{0}; // Good,C++11中使用 - unsigned int msgLength_; - unsigned char* msgBuffer_; -}; -``` - -### 规则7.1.2 为避免隐式转换,将单参数构造函数声明为explicit -说明:单参数构造函数如果没有用explicit声明,则会成为隐式转换函数。 -示例: - -```cpp -class Foo { -public: - explicit Foo(const string& name): name_(name) - { - } -private: - string name_; -}; - - -void ProcessFoo(const Foo& foo){} - -int main(void) -{ - std::string test = "test"; - ProcessFoo(test); // 编译不通过 - return 0; -} -``` - -上面的代码编译不通过,因为`ProcessFoo`需要的参数是Foo类型,传入的string类型不匹配。 - -如果将Foo构造函数的explicit关键字移除,那么调用`ProcessFoo`传入的string就会触发隐式转换,生成一个临时的Foo对象。往往这种隐式转换是让人迷惑的,并且容易隐藏Bug,得到了一个不期望的类型转换。所以对于单参数的构造函数是要求explicit声明。 - -### 规则7.1.3 如果不需要拷贝构造函数、赋值操作符 / 移动构造函数、赋值操作符,请明确禁止 -说明:如果用户不定义,编译器默认会生成拷贝构造函数和拷贝赋值操作符, 移动构造和移动赋值操作符(移动语义的函数C++11以后才有)。 -如果我们不要使用拷贝构造函数,或者赋值操作符,请明确拒绝: - -1. 将拷贝构造函数或者赋值操作符设置为private,并且不实现: -```cpp -class Foo { -private: - Foo(const Foo&); - Foo& operator=(const Foo&); -}; -``` -2. 使用C++11提供的delete, 请参见后面现代C++的相关章节。 - -### 规则7.1.4 拷贝构造和拷贝赋值操作符应该是成对出现或者禁止 -拷贝构造函数和拷贝赋值操作符都是具有拷贝语义的,应该同时出现或者禁止。 - -```cpp -// 同时出现 -class Foo { -public: - ... - Foo(const Foo&); - Foo& operator=(const Foo&); - ... -}; - -// 同时default, C++11支持 -class Foo { -public: - Foo(const Foo&) = default; - Foo& operator=(const Foo&) = default; -}; - -// 同时禁止, C++11可以使用delete -class Foo { -private: - Foo(const Foo&); - Foo& operator=(const Foo&); -}; -``` - -### 规则7.1.5 移动构造和移动赋值操作符应该是成对出现或者禁止 -在C++11中增加了move操作,如果需要某个类支持移动操作,那么需要实现移动构造和移动赋值操作符。 - -移动构造函数和移动赋值操作符都是具有移动语义的,应该同时出现或者禁止。 -```cpp -// 同时出现 -class Foo { -public: - ... - Foo(Foo&&); - Foo& operator=(Foo&&); - ... -}; - -// 同时default, C++11支持 -class Foo { -public: - Foo(Foo&&) = default; - Foo& operator=(Foo&&) = default; -}; - -// 同时禁止, 使用C++11的delete -class Foo { -public: - Foo(Foo&&) = delete; - Foo& operator=(Foo&&) = delete; -}; -``` - -### 规则7.1.6 禁止在构造函数和析构函数中调用虚函数 -说明:在构造函数和析构函数中调用当前对象的虚函数,会导致未实现多态的行为。 -在C++中,一个基类一次只构造一个完整的对象。 - -示例:类Base是基类,Sub是派生类 -```cpp -class Base { -public: - Base(); - virtual void Log() = 0; // 不同的派生类调用不同的日志文件 -}; - -Base::Base() // 基类构造函数 -{ - Log(); // 调用虚函数Log -} - -class Sub : public Base { -public: - virtual void Log(); -}; -``` - -当执行如下语句: -`Sub sub;` -会先执行Sub的构造函数,但首先调用Base的构造函数,由于Base的构造函数调用虚函数Log,此时Log还是基类的版本,只有基类构造完成后,才会完成派生类的构造,从而导致未实现多态的行为。 -同样的道理也适用于析构函数。 - - -## 继承 - -### 规则7.2.1 基类的析构函数应该声明为virtual -说明:只有基类析构函数是virtual,通过多态调用的时候才能保证派生类的析构函数被调用。 - -示例:基类的析构函数没有声明为virtual导致了内存泄漏。 -```cpp -class Base { -public: - virtual std::string getVersion() = 0; - - ~Base() - { - std::cout << "~Base" << std::endl; - } -}; -``` - -```cpp -class Sub : public Base { -public: - Sub() : numbers_(nullptr) - { - } - - ~Sub() - { - delete[] numbers_; - std::cout << "~Sub" << std::endl; - } - - int Init() - { - const size_t numberCount = 100; - numbers_ = new (std::nothrow) int[numberCount]; - if (numbers_ == nullptr) { - return -1; - } - - ... - } - - std::string getVersion() - { - return std::string("hello!"); - } -private: - int* numbers_; -}; -``` - -```cpp -int main(int argc, char* args[]) -{ - Base* b = new Sub(); - - delete b; - return 0; -} -``` -由于基类Base的析构函数没有声明为virtual,当对象被销毁时,只会调用基类的析构函数,不会调用派生类Sub的析构函数,导致内存泄漏。 - - -### 规则7.2.2 禁止虚函数使用缺省参数值 -说明:在C++中,虚函数是动态绑定的,但函数的缺省参数却是在编译时就静态绑定的。这意味着你最终执行的函数是一个定义在派生类,但使用了基类中的缺省参数值的虚函数。为了避免虚函数重载时,因参数声明不一致给使用者带来的困惑和由此导致的问题,规定所有虚函数均不允许声明缺省参数值。 -示例:虚函数display缺省参数值text是由编译时刻决定的,而非运行时刻,没有达到多态的目的: -```cpp -class Base { -public: - virtual void Display(const std::string& text = "Base!") - { - std::cout << text << std::endl; - } - - virtual ~Base(){} -}; - -class Sub : public Base { -public: - virtual void Display(const std::string& text = "Sub!") - { - std::cout << text << std::endl; - } - - virtual ~Sub(){} -}; - -int main() -{ - Base* base = new Sub(); - Sub* sub = new Sub(); - - ... - - base->Display(); // 程序输出结果: Base! 而期望输出:Sub! - sub->Display(); // 程序输出结果: Sub! - - delete base; - delete sub; - return 0; -}; -``` - -### 规则7.2.3 禁止重新定义继承而来的非虚函数 -说明:因为非虚函数无法实现动态绑定,只有虚函数才能实现动态绑定:只要操作基类的指针,即可获得正确的结果。 - -示例: -```cpp -class Base { -public: - void Fun(); -}; - -class Sub : public Base { -public: - void Fun(); -}; - -Sub* sub = new Sub(); -Base* base = sub; - -sub->Fun(); // 调用子类的Fun -base->Fun(); // 调用父类的Fun -//... - -``` - -## 多重继承 -在实际开发过程中使用多重继承的场景是比较少的,因为多重继承使用过程中有下面的典型问题: -1. 菱形继承所带来的数据重复,以及名字二义性。因此,C++引入了virtual继承来解决这类问题; -2. 即便不是菱形继承,多个父类之间的名字也可能存在冲突,从而导致的二义性; -3. 如果子类需要扩展或改写多个父类的方法时,造成子类的职责不明,语义混乱; -4. 相对于委托,继承是一种白盒复用,即子类可以访问父类的protected成员, 这会导致更强的耦合。而多重继承,由于耦合了多个父类,相对于单根继承,这会产生更强的耦合关系。 - -多重继承具有下面的优点: -多重继承提供了一种更简单的组合来实现多种接口或者类的组装与复用。 - -所以,对于多重继承的只有下面几种情况下面才允许使用多重继承。 - -### 建议7.3.1 使用多重继承来实现接口分离与多角色组合 -如果某个类需要实现多重接口,可以通过多重继承把多个分离的接口组合起来,类似 scala 语言的 traits 混入。 - -```cpp -class Role1 {}; -class Role2 {}; -class Role3 {}; - -class Object1 : public Role1, public Role2 { - // ... -}; - -class Object2 : public Role2, public Role3 { - // ... -}; - -``` - -在C++标准库中也有类似的实现样例: -```cpp -class basic_istream {}; -class basic_ostream {}; - -class basic_iostream : public basic_istream, public basic_ostream { - -}; -``` - -## 重载 - -重载操作符要有充分理由,而且不要改变操作符原有语义,例如不要使用 ‘+’ 操作符来做减运算。 -操作符重载令代码更加直观,但也有一些不足: -- 混淆直觉,误以为该操作和内建类型一样是高性能的,忽略了性能降低的可能; -- 问题定位时不够直观,按函数名查找比按操作符显然更方便。 -- 重载操作符如果行为定义不直观(例如将‘+’ 操作符来做减运算),会让代码产生混淆。 -- 赋值操作符的重载引入的隐式转换会隐藏很深的bug。可以定义类似Equals()、CopyFrom()等函数来替代=,==操作符。 - - - -# 8 函数 -## 函数设计 -### 规则8.1.1 避免函数过长,函数不超过50行(非空非注释) -函数应该可以一屏显示完 (50行以内),只做一件事情,而且把它做好。 - -过长的函数往往意味着函数功能不单一,过于复杂,或过分呈现细节,未进行进一步抽象。 - -例外:某些实现算法的函数,由于算法的聚合性与功能的全面性,可能会超过50行。 - -即使一个长函数现在工作的非常好, 一旦有人对其修改, 有可能出现新的问题, 甚至导致难以发现的bug。 -建议将其拆分为更加简短并易于管理的若干函数,以便于他人阅读和修改代码。 - -## 内联函数 - -### 建议8.2.1 内联函数不超过10行(非空非注释) -**说明**:内联函数具有一般函数的特性,它与一般函数不同之处只在于函数调用的处理。一般函数进行调用时,要将程序执行权转到被调用函数中,然后再返回到调用它的函数中;而内联函数在调用时,是将调用表达式用内联函数体来替换。 - -内联函数只适合于只有 1~10 行的小函数。对一个含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,也没有必要用内联函数实现,一般的编译器会放弃内联方式,而采用普通的方式调用函数。 - -如果内联函数包含复杂的控制结构,如循环、分支(switch)、try-catch 等语句,一般编译器将该函数视同普通函数。 -**虚函数、递归函数不能被用来做内联函数**。 - -## 函数参数 - -### 建议8.3.1 函数参数使用引用取代指针 - -**说明**:引用比指针更安全,因为它一定非空,且一定不会再指向其他目标;引用不需要检查非法的NULL指针。 - -如果是基于老平台开发的产品,则优先顺从原有平台的处理方式。 -选择 const 避免参数被修改,让代码阅读者清晰地知道该参数不被修改,可大大增强代码可读性。 - -例外:当传入参数为编译期长度未知的数组时,可以使用指针而不是引用。 - -### 建议8.3.2 使用强类型参数,避免使用void* -尽管不同的语言对待强类型和弱类型有自己的观点,但是一般认为c/c++是强类型语言,既然我们使用的语言是强类型的,就应该保持这样的风格。 -好处是尽量让编译器在编译阶段就检查出类型不匹配的问题。 - -使用强类型便于编译器帮我们发现错误,如下代码中注意函数 FooListAddNode 的使用: -```cpp -struct FooNode { - struct List link; - int foo; -}; - -struct BarNode { - struct List link; - int bar; -} - -void FooListAddNode(void *node) // Bad: 这里用 void * 类型传递参数 -{ - FooNode *foo = (FooNode *)node; - ListAppend(&g_FooList, &foo->link); -} - -void MakeTheList() -{ - FooNode *foo = nullptr; - BarNode *bar = nullptr; - ... - - FooListAddNode(bar); // Wrong: 这里本意是想传递参数 foo,但错传了 bar,却没有报错 -} -``` - -1. 可以使用模板函数来实现参数类型的变化。 -2. 可以使用基类指针来实现多态。 - -### 建议8.3.3 函数的参数个数不超过5个 -函数的参数过多,会使得该函数易于受外部变化的影响,从而影响维护工作。函数的参数过多同时也会增大测试的工作量。 - -如果超过可以考虑: -- 看能否拆分函数 -- 看能否将相关参数合在一起,定义结构体 - -# 9 C++其他特性 - -## 常量与初始化 - -不变的值更易于理解、跟踪和分析,所以应该尽可能地使用常量代替变量,定义值的时候,应该把const作为默认的选项。 - -### 规则9.1.1 不允许使用宏来表示常量 - -**说明**:宏是简单的文本替换,在预处理阶段时完成,运行报错时直接报相应的值;跟踪调试时也是显示值,而不是宏名;宏没有类型检查,不安全;宏没有作用域。 - -```cpp -#define MAX_MSISDN_LEN 20 // 不好 - -// C++请使用const常量 -const int MAX_MSISDN_LEN = 20; // 好 - -// 对于C++11以上版本,可以使用constexpr -constexpr int MAX_MSISDN_LEN = 20; -``` - -### 建议9.1.1 一组相关的整型常量应定义为枚举 - -**说明**:枚举比`#define`或`const int`更安全。编译器会检查参数值是否位于枚举取值范围内,避免错误发生。 - -```cpp -// 好的例子: -enum Week { - SUNDAY, - MONDAY, - TUESDAY, - WEDNESDAY, - THURSDAY, - FRIDAY, - SATURDAY -}; - -enum Color { - RED, - BLACK, - BLUE -}; - -void ColorizeCalendar(Week today, Color color); - -ColorizeCalendar(BLUE, SUNDAY); // 编译报错,参数类型错误 - -// 不好的例子: -const int SUNDAY = 0; -const int MONDAY = 1; - -const int BLACK = 0; -const int BLUE = 1; - -bool ColorizeCalendar(int today, int color); -ColorizeCalendar(BLUE, SUNDAY); // 不会报错 -``` - -当枚举值需要对应到具体数值时,须在声明时显式赋值。否则不需要显式赋值,以避免重复赋值,降低维护(增加、删除成员)工作量。 - -```cpp -// 好的例子:S协议里定义的设备ID值,用于标识设备类型 -enum DeviceType { - DEV_UNKNOWN = -1, - DEV_DSMP = 0, - DEV_ISMG = 1, - DEV_WAPPORTAL = 2 -}; -``` - -程序内部使用,仅用于分类的情况,不应该进行显式的赋值。 - -```cpp -// 好的例子:程序中用来标识会话状态的枚举定义 -enum SessionState { - INIT, - CLOSED, - WAITING_FOR_RESPONSE -}; -``` - -应当尽量避免枚举值重复,如必须重复也要用已定义的枚举来修饰 - -```cpp -enum RTCPType { - RTCP_SR = 200, - RTCP_MIN_TYPE = RTCP_SR, - RTCP_RR = 201, - RTCP_SDES = 202, - RTCP_BYE = 203, - RTCP_APP = 204, - RTCP_RTPFB = 205, - RTCP_PSFB = 206, - RTCP_XR = 207, - RTCP_RSI = 208, - RTCP_PUBPORTS = 209, - RTCP_MAX_TYPE = RTCP_PUBPORTS -}; -``` - -### 规则9.1.2 不允许使用魔鬼数字 -所谓魔鬼数字即看不懂、难以理解的数字。 - -魔鬼数字并非一个非黑即白的概念,看不懂也有程度,需要自行判断。 -例如数字 12,在不同的上下文中情况是不一样的: -type = 12; 就看不懂,但 `monthsCount = yearsCount * 12`; 就能看懂。 -数字 0 有时候也是魔鬼数字,比如 `status = 0`; 并不能表达是什么状态。 - -解决途径: -对于局部使用的数字,可以增加注释说明 -对于多处使用的数字,必须定义 const 常量,并通过符号命名自注释。 - -禁止出现下列情况: -没有通过符号来解释数字含义,如` const int ZERO = 0` -符号命名限制了其取值,如 `const int XX_TIMER_INTERVAL_300MS = 300`,直接使用`XX_TIMER_INTERVAL_MS`来表示该常量是定时器的时间间隔。 - -### 规则9.1.3 常量应该保证单一职责 - -**说明**:一个常量只用来表示一个特定功能,即一个常量不能有多种用途。 - -```cpp -// 好的例子:协议A和协议B,手机号(MSISDN)的长度都是20。 -const unsigned int A_MAX_MSISDN_LEN = 20; -const unsigned int B_MAX_MSISDN_LEN = 20; - -// 或者使用不同的名字空间: -namespace Namespace1 { - const unsigned int MAX_MSISDN_LEN = 20; -} - -namespace Namespace2 { - const unsigned int MAX_MSISDN_LEN = 20; -} -``` - -### 规则9.1.4 禁止用memcpy_s、memset_s初始化非POD对象 - -**说明**:`POD`全称是`Plain Old Data`,是C++ 98标准(ISO/IEC 14882, first edition, 1998-09-01)中引入的一个概念,`POD`类型主要包括`int`, `char`, `float`,`double`,`enumeration`,`void`,指针等原始类型以及聚合类型,不能使用封装和面向对象特性(如用户定义的构造/赋值/析构函数、基类、虚函数等)。 - -由于非POD类型比如非聚合类型的class对象,可能存在虚函数,内存布局不确定,跟编译器有关,滥用内存拷贝可能会导致严重的问题。 - -即使对聚合类型的class,使用直接的内存拷贝和比较,破坏了信息隐蔽和数据保护的作用,也不提倡`memcpy_s`、`memset_s`操作。 - -对于POD类型的详细说明请参见附录。 - -### 建议9.1.2 变量使用时才声明并初始化 - -**说明**:变量在使用前未赋初值,是常见的低级编程错误。使用前才声明变量并同时初始化,非常方便地避免了此类低级错误。 - -在函数开始位置声明所有变量,后面才使用变量,作用域覆盖整个函数实现,容易导致如下问题: -* 程序难以理解和维护:变量的定义与使用分离。 -* 变量难以合理初始化:在函数开始时,经常没有足够的信息进行变量初始化,往往用某个默认的空值(比如零)来初始化,这通常是一种浪费,如果变量在被赋于有效值以前使用,还会导致错误。 - -遵循变量作用域最小化原则与就近声明原则, 使得代码更容易阅读,方便了解变量的类型和初始值。特别是,应使用初始化的方式替代声明再赋值。 - -```cpp -// 不好的例子:声明与初始化分离 -string name; // 声明时未初始化:调用缺省构造函数 -name = "zhangsan"; // 再次调用赋值操作符函数;声明与定义在不同的地方,理解相对困难 - -// 好的例子:声明与初始化一体,理解相对容易 -string name("zhangsan"); // 调用构造函数 -``` - - -## 表达式 -### 规则9.2.1 含有变量自增或自减运算的表达式中禁止再次引用该变量 -含有变量自增或自减运算的表达式中,如果再引用该变量,其结果在C++标准中未明确定义。各个编译器或者同一个编译器不同版本实现可能会不一致。 -为了更好的可移植性,不应该对标准未定义的运算次序做任何假设。 - -注意,运算次序的问题不能使用括号来解决,因为这不是优先级的问题。 - -示例: -```cpp -x = b[i] + i++; // Bad: b[i]运算跟 i++,先后顺序并不明确。 -``` -正确的写法是将自增或自减运算单独放一行: -```cpp -x = b[i] + i; -i++; // Good: 单独一行 -``` - -函数参数 -```cpp -Func(i++, i); // Bad: 传递第2个参数时,不确定自增运算有没有发生 -``` - -正确的写法 -```cpp -i++; // Good: 单独一行 -x = Func(i, i); -``` - -### 规则9.2.2 switch语句要有default分支 -大部分情况下,switch语句中要有default分支,保证在遗漏case标签处理时能够有一个缺省的处理行为。 - -特例: -如果switch条件变量是枚举类型,并且 case 分支覆盖了所有取值,则加上default分支处理有些多余。 -现代编译器都具备检查是否在switch语句中遗漏了某些枚举值的case分支的能力,会有相应的warning提示。 - -```cpp -enum Color { - RED = 0, - BLUE -}; - -// 因为switch条件变量是枚举值,这里可以不用加default处理分支 -switch (color) { - case RED: - DoRedThing(); - break; - case BLUE: - DoBlueThing(); - ... - break; -} -``` - -### 建议9.2.1 表达式的比较,应当遵循左侧倾向于变化、右侧倾向于不变的原则 -当变量与常量比较时,如果常量放左边,如 if (MAX == v) 不符合阅读习惯,而 if (MAX > v) 更是难于理解。 -应当按人的正常阅读、表达习惯,将常量放右边。写成如下方式: -```cpp -if (value == MAX) { - -} - -if (value < MAX) { - -} -``` -也有特殊情况,如:`if (MIN < value && value < MAX)` 用来描述区间时,前半段是常量在左的。 - -不用担心将 '==' 误写成 '=',因为` if (value = MAX)` 会有编译告警,其他静态检查工具也会报错。让工具去解决笔误问题,代码要符合可读性第一。 - -### 建议9.2.2 使用括号明确操作符的优先级 -使用括号明确操作符的优先级,防止因默认的优先级与设计思想不符而导致程序出错;同时使得代码更为清晰可读,然而过多的括号会分散代码使其降低了可读性。下面是如何使用括号的建议。 - -- 二元及以上操作符, 如果涉及多种操作符,则应该使用括号 -```cpp -x = a + b + c; /* 操作符相同,可以不加括号 */ -x = Foo(a + b, c); /* 逗号两边的表达式,不需要括号 */ -x = 1 << (2 + 3); /* 操作符不同,需要括号 */ -x = a + (b / 5); /* 操作符不同,需要括号 */ -x = (a == b) ? a : (a – b); /* 操作符不同,需要括号 */ -``` - - -## 类型转换 - -避免使用类型分支来定制行为:类型分支来定制行为容易出错,是企图用C++编写C代码的明显标志。这是一种很不灵活的技术,要添加新类型时,如果忘记修改所有分支,编译器也不会告知。使用模板和虚函数,让类型自己而不是调用它们的代码来决定行为。 - -建议避免类型转换,我们在代码的类型设计上应该考虑到每种数据的数据类型是什么,而不是应该过度使用类型转换来解决问题。在设计某个基本类型的时候,请考虑: -- 是无符号还是有符号的 -- 是适合float还是double -- 是使用int8,int16,int32还是int64,确定整形的长度 - -但是我们无法禁止使用类型转换,因为C++语言是一门面向机器编程的语言,涉及到指针地址,并且我们会与各种第三方或者底层API交互,他们的类型设计不一定是合理的,在这个适配的过程中很容易出现类型转换。 - -例外:在调用某个函数的时候,如果我们不想处理函数结果,首先要考虑这个是否是你的最好的选择。如果确实不想处理函数的返回值,那么可以使用(void)转换来解决。 - -### 规则9.3.1 如果确定要使用类型转换,请使用由C++提供的类型转换,而不是C风格的类型转换 - -**说明**: - -C++提供的类型转换操作比C风格更有针对性,更易读,也更加安全,C++提供的转换有: -- 类型转换: -1. `dynamic_cast`:主要用于继承体系下行转换,`dynamic_cast`具有类型检查的功能,请做好基类和派生类的设计,避免使用dynamic_cast来进行转换。 -2. `static_cast`:和C风格转换相似可做值的强制转换,或上行转换(把派生类的指针或引用转换成基类的指针或引用)。该转换经常用于消除多重继承带来的类型歧义,是相对安全的。如果是纯粹的算数转换,那么请使用后面的大括号转换方式。 -3. `reinterpret_cast`:用于转换不相关的类型。`reinterpret_cast`强制编译器将某个类型对象的内存重新解释成另一种类型,这是一种不安全的转换,建议尽可能少用`reinterpret_cast`。 -4. `const_cast`:用于移除对象的`const`属性,使对象变得可修改,这样会破坏数据的不变性,建议尽可能少用。 - -- 算数转换: (C++11开始支持) - 对于那种算数转换,并且类型信息没有丢失的,比如float到double, int32到int64的转换,推荐使用大括号的初始方式。 -```cpp - double d{ someFloat }; - int64_t i{ someInt32 }; -``` - -### 建议9.3.1 避免使用`dynamic_cast` -1. `dynamic_cast`依赖于C++的RTTI, 让程序员在运行时识别C++类对象的类型。 -2. `dynamic_cast`的出现一般说明我们的基类和派生类设计出现了问题,派生类破坏了基类的契约,不得不通过`dynamic_cast`转换到子类进行特殊处理,这个时候更希望来改善类的设计,而不是通过`dynamic_cast`来解决问题。 - -### 建议9.3.2 避免使用`reinterpret_cast` - -**说明**:`reinterpret_cast`用于转换不相关类型。尝试用`reinterpret_cast`将一种类型强制转换另一种类型,这破坏了类型的安全性与可靠性,是一种不安全的转换。不同类型之间尽量避免转换。 - -### 建议9.3.3 避免使用`const_cast` - -**说明**:`const_cast`用于移除对象的`const`和`volatile`性质。 - -使用const_cast转换后的指针或者引用来修改const对象,行为是未定义的。 - -```cpp -// 不好的例子 -const int i = 1024; -int* p = const_cast(&i); -*p = 2048; // 未定义行为 -``` - -```cpp -// 不好的例子 -class Foo { -public: - Foo() : i(3) {} - - void Fun(int v) - { - i = v; - } - -private: - int i; -}; - -int main(void) -{ - const Foo f; - Foo* p = const_cast(&f); - p->Fun(8); // 未定义行为 -} - -``` - - -## 资源分配和释放 - -### 规则9.4.1 单个对象释放使用delete,数组对象释放使用delete [] -说明:单个对象删除使用delete, 数组对象删除使用delete [],原因: - -- 调用new所包含的动作:从系统中申请一块内存,并调用此类型的构造函数。 -- 调用new[n]所包含的动作:申请可容纳n个对象的内存,并且对每一个对象调用其构造函数。 -- 调用delete所包含的动作:先调用相应的析构函数,再将内存归还系统。 -- 调用delete[]所包含的动作:对每一个对象调用析构函数,再释放所有内存 - -如果new和delete的格式不匹配,结果是未知的。对于非class类型, new和delete不会调用构造与析构函数。 - -错误写法: -```cpp -const int MAX_ARRAY_SIZE = 100; -int* numberArray = new int[MAX_ARRAY_SIZE]; -... -delete numberArray; -numberArray = nullptr; -``` - -正确写法: -```cpp -const int MAX_ARRAY_SIZE = 100; -int* numberArray = new int[MAX_ARRAY_SIZE]; -... -delete[] numberArray; -numberArray = nullptr; -``` - -### 建议9.4.1 使用 RAII 特性来帮助追踪动态分配 - -说明:RAII是“资源获取就是初始化”的缩语(Resource Acquisition Is Initialization),是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。 - -RAII 的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。这种做法有两大好处: -- 我们不需要显式地释放资源。 -- 对象所需的资源在其生命期内始终保持有效。这样,就不必检查资源有效性的问题,可以简化逻辑、提高效率。 - - -示例:使用RAII不需要显式地释放互斥资源。 - -```cpp -class LockGuard { -public: - LockGuard(const LockType& lockType): lock_(lockType) - { - lock_.Aquire(); - } - - ~LockGuard() - { - lock_.Relase(); - } - -private: - LockType lock_; -}; - - -bool Update() -{ - LockGuard lockGuard(mutex); - if (...) { - return false; - } else { - // 操作数据 - } - - return true; -} -``` - -## 标准库 - -STL标准模板库在不同产品使用程度不同,这里列出一些基本规则和建议,供各团队参考。 - -### 规则9.5.1 不要保存std::string的c_str()返回的指针 - -说明:在C++标准中并未规定string::c_str()指针持久有效,因此特定STL实现完全可以在调用string::c_str()时返回一个临时存储区并很快释放。所以为了保证程序的可移植性,不要保存string::c_str()的结果,而是在每次需要时直接调用。 - -示例: - -```cpp -void Fun1() -{ - std::string name = "demo"; - const char* text = name.c_str(); // 表达式结束以后,name的生命周期还在,指针有效 - - // 如果中间调用了string的非const成员函数,导致string被修改,比如operator[], begin()等 - // 可能会导致text的内容不可用,或者不是原来的字符串 - name = "test"; - name[1] = '2'; - - // 后续使用text指针,其字符串内容不再是"demo" -} - -void Fun2() -{ - std::string name = "demo"; - std::string test = "test"; - const char* text = (name + test).c_str(); // 表达式结束以后,+号产生的临时对象被销毁,指针无效 - - // 后续使用text指针,其已不再指向合法内存空间 -} -``` -例外:在少数对性能要求非常高的代码中,为了适配已有的只接受const char*类型入参的函数,可以临时保存string::c_str()返回的指针。但是必须严格保证string对象的生命周期长于所保存指针的生命周期,并且保证在所保存指针的生命周期内,string对象不会被修改。 - - -### 建议9.5.1 使用std::string代替char* - -说明:使用string代替`char*`有很多优势,比如: -1. 不用考虑结尾的’\0’; -2. 可以直接使用+, =, ==等运算符以及其它字符串操作函数; -3. 不需要考虑内存分配操作,避免了显式的new/delete,以及由此导致的错误; - -需要注意的是某些stl实现中string是基于写时复制策略的,这会带来2个问题,一是某些版本的写时复制策略没有实现线程安全,在多线程环境下会引起程序崩溃;二是当与动态链接库相互传递基于写时复制策略的string时,由于引用计数在动态链接库被卸载时无法减少可能导致悬挂指针。因此,慎重选择一个可靠的stl实现对于保证程序稳定是很重要的。 - -例外: -当调用系统或者其它第三方库的API时,针对已经定义好的接口,只能使用`char*`。但是在调用接口之前都可以使用string,在调用接口时使用string::c_str()获得字符指针。 -当在栈上分配字符数组当作缓冲区使用时,可以直接定义字符数组,不要使用string,也没有必要使用类似`vector`等容器。 - -### 规则9.5.2 禁止使用auto_ptr -说明:在stl库中的std::auto_ptr具有一个隐式的所有权转移行为,如下代码: -```cpp -auto_ptr p1(new T); -auto_ptr p2 = p1; -``` -当执行完第2行语句后,p1已经不再指向第1行中分配的对象,而是变为nullptr。正因为如此,auto_ptr不能被置于各种标准容器中。 -转移所有权的行为通常不是期望的结果。对于必须转移所有权的场景,也不应该使用隐式转移的方式。这往往需要程序员对使用auto_ptr的代码保持额外的谨慎,否则出现对空指针的访问。 -使用auto_ptr常见的有两种场景,一是作为智能指针传递到产生auto_ptr的函数外部,二是使用auto_ptr作为RAII管理类,在超出auto_ptr的生命周期时自动释放资源。 -对于第1种场景,可以使用std::shared_ptr来代替。 -对于第2种场景,可以使用C++11标准中的std::unique_ptr来代替。其中std::unique_ptr是std::auto_ptr的代替品,支持显式的所有权转移。 - -例外: -在C++11标准得到普遍使用之前,在一定需要对所有权进行转移的场景下,可以使用std::auto_ptr,但是建议对std::auto_ptr进行封装,并禁用封装类的拷贝构造函数和赋值运算符,以使该封装类无法用于标准容器。 - - -### 建议9.5.2 使用新的标准头文件 - -说明: -使用C++的标准头文件时,请使用``这样的,而不是``这种的。 - -## const的用法 -在声明的变量或参数前加上关键字 const 用于指明变量值不可被篡改 (如 `const int foo` ). 为类中的函数加上 const 限定符表明该函数不会修改类成员变量的状态 (如 `class Foo { int Bar(char c) const; };`)。 const 变量, 数据成员, 函数和参数为编译时类型检测增加了一层保障, 便于尽早发现错误。因此, 我们强烈建议在任何可能的情况下使用 const。 -有时候,使用C++11的constexpr来定义真正的常量可能更好。 - -### 规则9.6.1 对于指针和引用类型的形参,如果是不需要修改的,请使用const -不变的值更易于理解/跟踪和分析,把const作为默认选项,在编译时会对其进行检查,使代码更牢固/更安全。 -```cpp -class Foo; - -void PrintFoo(const Foo& foo); -``` - -### 规则9.6.2 对于不会修改成员变量的成员函数请使用const修饰 -尽可能将成员函数声明为 const。 访问函数应该总是 const。只要不修改数据成员的成员函数,都声明为const。 -对于虚函数,应当从设计意图上考虑继承链上的所有类是否需要在此虚函数中修改数据成员,而不是仅关注单个类的实现。 -```cpp -class Foo { -public: - - // ... - - int PrintValue() const // const修饰成员函数,不会修改成员变量 - { - std::cout << value_ << std::endl; - } - - int GetValue() const // const修饰成员函数,不会修改成员变量 - { - return value_; - } - -private: - int value_; -}; -``` - -### 建议9.6.1 初始化后不会再修改的成员变量定义为const - -```cpp -class Foo { -public: - Foo(int length) : dataLength_(length) {} -private: - const int dataLength_; -}; -``` - -## 异常 - -### 建议9.7.1 C++11中,如果函数不会抛出异常,声明为`noexcept` -**理由** -1. 如果函数不会抛出异常,声明为`noexcept`可以让编译器最大程度的优化函数,如减少执行路径,提高错误退出的效率。 -2. `vector`等STL容器,为了保证接口的健壮性,如果保存元素的`move运算符`没有声明为`noexcept`,则在容器扩张搬移元素时不会使用`move机制`,而使用`copy机制`,带来性能损失的风险。如果一个函数不能抛出异常,或者一个程序并没有截获某个函数所抛出的异常并进行处理,那么这个函数可以用新的`noexcept`关键字对其进行修饰,表示这个函数不会抛出异常或者抛出的异常不会被截获并处理。例如: - -```cpp -extern "C" double sqrt(double) noexcept; // 永远不会抛出异常 - -// 即使可能抛出异常,也可以使用 noexcept -// 这里不准备处理内存耗尽的异常,简单地将函数声明为noexcept -std::vector MyComputation(const std::vector& v) noexcept -{ - std::vector res = v; // 可能会抛出异常 - // do something - return res; -} -``` - -**示例** - -```cpp -RetType Function(Type params) noexcept; // 最大的优化 -RetType Function(Type params); // 更少的优化 - -// std::vector 的 move 操作需要声明 noexcept -class Foo1 { -public: - Foo1(Foo1&& other); // no noexcept -}; - -std::vector a1; -a1.push_back(Foo1()); -a1.push_back(Foo1()); // 触发容器扩张,搬移已有元素时调用copy constructor - -class Foo2 { -public: - Foo2(Foo2&& other) noexcept; -}; - -std::vector a2; -a2.push_back(Foo2()); -a2.push_back(Foo2()); // 触发容器扩张,搬移已有元素时调用move constructor -``` - -**注意** -默认构造函数、析构函数、`swap`函数,`move操作符`都不应该抛出异常。 - -## 模板 - -模板能够实现非常灵活简洁的类型安全的接口,实现类型不同但是行为相同的代码复用。 - -模板编程的缺点: - -1. 模板编程所使用的技巧对于使用c++不是很熟练的人是比较晦涩难懂的。在复杂的地方使用模板的代码让人更不容易读懂,并且debug 和维护起来都很麻烦。 -2. 模板编程经常会导致编译出错的信息非常不友好: 在代码出错的时候, 即使这个接口非常的简单, 模板内部复杂的实现细节也会在出错信息显示. 导致这个编译出错信息看起来非常难以理解。 -3. 模板如果使用不当,会导致运行时代码过度膨胀。 -4. 模板代码难以修改和重构。模板的代码会在很多上下文里面扩展开来, 所以很难确认重构对所有的这些展开的代码有用。 - -所以, 建议__模板编程最好只用在少量的基础组件,基础数据结构上面__。并且使用模板编程的时候尽可能把__复杂度最小化__,尽量__不要让模板对外暴露__。最好只在实现里面使用模板, 然后给用户暴露的接口里面并不使用模板, 这样能提高你的接口的可读性。 并且你应该在这些使用模板的代码上写尽可能详细的注释。 - - -## 宏 -在C++语言中,我们强烈建议尽可能少使用复杂的宏 -- 对于常量定义,请按照前面章节所述,使用const或者枚举; -- 对于宏函数,尽可能简单,并且遵循下面的原则,并且优先使用内联函数,模板函数等进行替换。 - -```cpp -// 不推荐使用宏函数 -#define SQUARE(a, b) ((a) * (b)) - -// 请使用模板函数,内联函数等来替换。 -template T Square(T a, T b) { return a * b; } -``` - -如果需要使用宏,请参考C语言规范的相关章节。 -**例外**:一些通用且成熟的应用,如:对 new, delete 的封装处理,可以保留对宏的使用。 - -# 10 现代C++特性 - -随着 ISO 在2011年发布 C++11 语言标准,以及2017年3月发布 C++17 ,现代C++(C++11/14/17等)增加了大量提高编程效率、代码质量的新语言特性和标准库。 -本章节描述了一些可以帮助团队更有效率的使用现代C++,规避语言陷阱的指导意见。 - -## 代码简洁性和安全性提升 -### 建议10.1.1 合理使用`auto` -**理由** - -* `auto`可以避免编写冗长、重复的类型名,也可以保证定义变量时初始化。 -* `auto`类型推导规则复杂,需要仔细理解。 -* 如果能够使代码更清晰,继续使用明确的类型,且只在局部变量使用`auto`。 - -**示例** - -```cpp -// 避免冗长的类型名 -std::map::iterator iter = m.find(val); -auto iter = m.find(val); - -// 避免重复类型名 -class Foo {...}; -Foo* p = new Foo; -auto p = new Foo; - -// 保证初始化 -int x; // 编译正确,没有初始化 -auto x; // 编译失败,必须初始化 -``` - -auto 的类型推导可能导致困惑: - -```cpp -auto a = 3; // int -const auto ca = a; // const int -const auto& ra = a; // const int& -auto aa = ca; // int, 忽略 const 和 reference -auto ila1 = { 10 }; // std::initializer_list -auto ila2{ 10 }; // std::initializer_list - -auto&& ura1 = x; // int& -auto&& ura2 = ca; // const int& -auto&& ura3 = 10; // int&& - -const int b[10]; -auto arr1 = b; // const int* -auto& arr2 = b; // const int(&)[10] -``` - -如果没有注意 `auto` 类型推导时忽略引用,可能引入难以发现的性能问题: - -```cpp -std::vector v; -auto s1 = v[0]; // auto 推导为 std::string,拷贝 v[0] -``` - -如果使用`auto`定义接口,如头文件中的常量,可能因为开发人员修改了值,而导致类型发生变化。 - -### 规则10.1.1 在重写虚函数时请使用`override`或`final`关键字 -**理由** -`override`和`final`关键字都能保证函数是虚函数,且重写了基类的虚函数。如果子类函数与基类函数原型不一致,则产生编译告警。`final`还保证虚函数不会再被子类重写。 - -使用`override`或`final`关键字后,如果修改了基类虚函数原型,但忘记修改子类重写的虚函数,在编译期就可以发现。也可以避免有多个子类时,重写虚函数的修改遗漏。 - -**示例** - -```cpp -class Base { -public: - virtual void Foo(); - virtual void Foo(int var); - void Bar(); -}; - -class Derived : public Base { -public: - void Foo() const override; // 编译失败: Derived::Foo 和 Base::Foo 原型不一致,不是重写 - void Foo() override; // 正确: Derived::Foo 重写 Base::Foo - void Foo(int var) final; // 正确: Derived::Foo(int) 重写 Base::Foo(int),且Derived的派生类不能再重写此函数 - void Bar() override; // 编译失败: Base::Bar 不是虚函数 -}; -``` - -**总结** -1. 基类首次定义虚函数,使用`virtual`关键字 -2. 子类重写基类虚函数(包括析构函数),使用`override`或`final`关键字(但不要两者一起使用),并且不使用`virtual`关键字 -3. 非虚函数,`virtual`、`override`和`final`都不使用 - -### 规则10.1.2 使用`delete`关键字删除函数 -**理由** -相比于将类成员函数声明为`private`但不实现,`delete`关键字更明确,且适用范围更广。 - -**示例** - -```cpp -class Foo { -private: - // 只看头文件不知道拷贝构造是否被删除 - Foo(const Foo&); -}; - -class Foo { -public: - // 明确删除拷贝赋值函数 - Foo& operator=(const Foo&) = delete; -}; -``` - -`delete`关键字还支持删除非成员函数 - -```cpp -template -void Process(T value); - -template<> -void Process(void) = delete; -``` - -### 规则10.1.3 使用`nullptr`,而不是`NULL`或`0` -**理由** -长期以来,C++没有一个代表空指针的关键字,这是一件很尴尬的事: - -```cpp -#define NULL ((void *)0) - -char* str = NULL; // 错误: void* 不能自动转换为 char* - -void(C::*pmf)() = &C::Func; -if (pmf == NULL) {} // 错误: void* 不能自动转换为指向成员函数的指针 -``` - -如果把`NULL`被定义为`0`或`0L`。可以解决上面的问题。 - -或者在需要空指针的地方直接使用`0`。但这引入另一个问题,代码不清晰,特别是使用`auto`自动推导: - -```cpp -auto result = Find(id); -if (result == 0) { // Find() 返回的是 指针 还是 整数? - // do something -} -``` - -`0`字面上是`int`类型(`0L`是`long`),所以`NULL`和`0`都不是指针类型。 -当重载指针和整数类型的函数时,传递`NULL`或`0`都调用到整数类型重载的函数: - -```cpp -void F(int); -void F(int*); - -F(0); // 调用 F(int),而非 F(int*) -F(NULL); // 调用 F(int),而非 F(int*) -``` - -另外,`sizeof(NULL) == sizeof(void*)`并不一定总是成立的,这也是一个潜在的风险。 - -总结: 直接使用`0`或`0L`,代码不清晰,且无法做到类型安全;使用`NULL`无法做到类型安全。这些都是潜在的风险。 - -`nullptr`的优势不仅仅是在字面上代表了空指针,使代码清晰,而且它不再是一个整数类型。 - -`nullptr`是`std::nullptr_t`类型,而`std::nullptr_t`可以隐式的转换为所有的原始指针类型,这使得`nullptr`可以表现成指向任意类型的空指针。 - -```cpp -void F(int); -void F(int*); -F(nullptr); // 调用 F(int*) - -auto result = Find(id); -if (result == nullptr) { // Find() 返回的是 指针 - // do something -} -``` - -### 规则10.1.4 使用`using`而非`typedef` -在`C++11`之前,可以通过`typedef`定义类型的别名。没人愿意多次重复`std::map>`这样的代码。 - -```cpp -typedef std::map> SomeType; -``` - -类型的别名实际是对类型的封装。而通过封装,可以让代码更清晰,同时在很大程度上避免类型变化带来的散弹式修改。 -在`C++11`之后,提供`using`,实现`声明别名(alias declarations)`: - -```cpp -using SomeType = std::map>; -``` - -对比两者的格式: - -```cpp -typedef Type Alias; // Type 在前,还是 Alias 在前 -using Alias = Type; // 符合'赋值'的用法,容易理解,不易出错 -``` - -如果觉得这点还不足以切换到`using`,我们接着看看`模板别名(alias template)`: - -```cpp -// 定义模板的别名,一行代码 -template -using MyAllocatorVector = std::vector>; - -MyAllocatorVector data; // 使用 using 定义的别名 - -template -class MyClass { -private: - MyAllocatorVector data_; // 模板类中使用 using 定义的别名 -}; -``` - -而`typedef`不支持带模板参数的别名,只能"曲线救国": - -```cpp -// 通过模板包装 typedef,需要实现一个模板类 -template -struct MyAllocatorVector { - typedef std::vector> type; -}; - -MyAllocatorVector::type data; // 使用 typedef 定义的别名,多写 ::type - -template -class MyClass { -private: - typename MyAllocatorVector::type data_; // 模板类中使用,除了 ::type,还需要加上 typename -}; -``` - -### 规则10.1.5 禁止使用std::move操作const对象 -从字面上看,`std::move`的意思是要移动一个对象。而const对象是不允许修改的,自然也无法移动。因此用`std::move`操作const对象会给代码阅读者带来困惑。 -在实际功能上,`std::move`会把对象转换成右值引用类型;对于const对象,会将其转换成const的右值引用。由于极少有类型会定义以const右值引用为参数的移动构造函数和移动赋值操作符,因此代码实际功能往往退化成了对象拷贝而不是对象移动,带来了性能上的损失。 - -**错误示例:** -```cpp -std::string g_string; -std::vector g_stringList; - -void func() -{ - const std::string myString = "String content"; - g_string = std::move(myString); // bad:并没有移动myString,而是进行了复制 - const std::string anotherString = "Another string content"; - g_stringList.push_back(std::move(anotherString)); // bad:并没有移动anotherString,而是进行了复制 -} -``` - -## 智能指针 -### 规则10.2.1 优先使用智能指针而不是原始指针管理资源 -**理由** -避免资源泄露。 - -**示例** - -```cpp -void Use(int i) -{ - auto p = new int {7}; // 不好: 通过 new 初始化局部指针 - auto q = std::make_unique(9); // 好: 保证释放内存 - if (i > 0) { - return; // 可能 return,导致内存泄露 - } - delete p; // 太晚了 -} -``` - -**例外** -在性能敏感、兼容性等场景可以使用原始指针。 - -### 规则10.2.2 优先使用`unique_ptr`而不是`shared_ptr` -**理由** -1. `shared_ptr`引用计数的原子操作存在可测量的开销,大量使用`shared_ptr`影响性能。 -2. 共享所有权在某些情况(如循环依赖)可能导致对象永远得不到释放。 -3. 相比于谨慎设计所有权,共享所有权是一种诱人的替代方案,但它可能使系统变得混乱。 - -### 规则10.2.3 使用`std::make_unique`而不是`new`创建`unique_ptr` -**理由** -1. `make_unique`提供了更简洁的创建方式 -2. 保证了复杂表达式的异常安全 - -**示例** - -```cpp -// 不好:两次出现 MyClass,重复导致不一致风险 -std::unique_ptr ptr(new MyClass(0, 1)); -// 好:只出现一次 MyClass,不存在不一致的可能 -auto ptr = std::make_unique(0, 1); -``` - -重复出现类型可能导致非常严重的问题,且很难发现: - -```cpp -// 编译正确,但new和delete不配套 -std::unique_ptr ptr(new uint8_t[10]); -std::unique_ptr ptr(new uint8_t); -// 非异常安全: 编译器可能按如下顺序计算参数: -// 1. 分配 Foo 的内存, -// 2. 构造 Foo, -// 3. 调用 Bar, -// 4. 构造 unique_ptr. -// 如果 Bar 抛出异常, Foo 不会被销毁,产生内存泄露。 -F(unique_ptr(new Foo()), Bar()); - -// 异常安全: 调用函数不会被打断. -F(make_unique(), Bar()); -``` - -**例外** -`std::make_unique`不支持自定义`deleter`。 -在需要自定义`deleter`的场景,建议在自己的命名空间实现定制版本的`make_unique`。 -使用`new`创建自定义`deleter`的`unique_ptr`是最后的选择。 - -### 规则10.2.4 使用`std::make_shared`而不是`new`创建`shared_ptr` -**理由** -使用`std::make_shared`除了类似`std::make_unique`一致性等原因外,还有性能的因素。 -`std::shared_ptr`管理两个实体: -* 控制块(存储引用计数,`deleter`等) -* 管理对象 - -`std::make_shared`创建`std::shared_ptr`,会一次性在堆上分配足够容纳控制块和管理对象的内存。而使用`std::shared_ptr(new MyClass)`创建`std::shared_ptr`,除了`new MyClass`会触发一次堆分配外,`std::shard_ptr`的构造函数还会触发第二次堆分配,产生额外的开销。 - -**例外** -类似`std::make_unique`,`std::make_shared`不支持定制`deleter` - -## Lambda -### 建议10.3.1 当函数不能工作时选择使用`lambda`(捕获局部变量,或编写局部函数) -**理由** -函数无法捕获局部变量或在局部范围内声明;如果需要这些东西,尽可能选择`lambda`,而不是手写的`functor`。 -另一方面,`lambda`和`functor`不会重载;如果需要重载,则使用函数。 -如果`lambda`和函数都可以的场景,则优先使用函数;尽可能使用最简单的工具。 - -**示例** - -```cpp -// 编写一个只接受 int 或 string 的函数 -// -- 重载是自然的选择 -void F(int); -void F(const string&); - -// 需要捕获局部状态,或出现在语句或表达式范围 -// -- lambda 是自然的选择 -vector v = LotsOfWork(); -for (int taskNum = 0; taskNum < max; ++taskNum) { - pool.Run([=, &v] {...}); -} -pool.Join(); -``` - -### 规则10.3.1 非局部范围使用`lambdas`,避免使用按引用捕获 -**理由** -非局部范围使用`lambdas`包括返回值,存储在堆上,或者传递给其它线程。局部的指针和引用不应该在它们的范围外存在。`lambdas`按引用捕获就是把局部对象的引用存储起来。如果这会导致超过局部变量生命周期的引用存在,则不应该按引用捕获。 - -**示例** - -```cpp -// 不好 -void Foo() -{ - int local = 42; - // 按引用捕获 local. - // 当函数返回后,local 不再存在, - // 因此 Process() 的行为未定义! - threadPool.QueueWork([&]{ Process(local); }); -} - -// 好 -void Foo() -{ - int local = 42; - // 按值捕获 local。 - // 因为拷贝,Process() 调用过程中,local 总是有效的 - threadPool.QueueWork([=]{ Process(local); }); -} -``` - -### 建议10.3.2 如果捕获`this`,则显式捕获所有变量 -**理由** -在成员函数中的`[=]`看起来是按值捕获。但因为是隐式的按值获取了`this`指针,并能够操作所有成员变量,数据成员实际是按引用捕获的,一般情况下建议避免。如果的确需要这样做,明确写出对`this`的捕获。 - -**示例** - -```cpp -class MyClass { -public: - void Foo() - { - int i = 0; - - auto Lambda = [=]() { Use(i, data_); }; // 不好: 看起来像是拷贝/按值捕获,成员变量实际上是按引用捕获 - - data_ = 42; - Lambda(); // 调用 use(42); - data_ = 43; - Lambda(); // 调用 use(43); - - auto Lambda2 = [i, this]() { Use(i, data_); }; // 好,显式指定按值捕获,最明确,最少的混淆 - } - -private: - int data_ = 0; -}; -``` - -### 建议10.3.3 避免使用默认捕获模式 -**理由** -lambda表达式提供了两种默认捕获模式:按引用(&)和按值(=)。 -默认按引用捕获会隐式的捕获所有局部变量的引用,容易导致访问悬空引用。相比之下,显式的写出需要捕获的变量可以更容易的检查对象生命周期,减小犯错可能。 -默认按值捕获会隐式的捕获this指针,且难以看出lambda函数所依赖的变量是哪些。如果存在静态变量,还会让阅读者误以为lambda拷贝了一份静态变量。 -因此,通常应当明确写出lambda需要捕获的变量,而不是使用默认捕获模式。 - -**错误示例** -```cpp -auto func() -{ - int addend = 5; - static int baseValue = 3; - - return [=]() { // 实际上只复制了addend - ++baseValue; // 修改会影响静态变量的值 - return baseValue + addend; - }; -} -``` - -**正确示例** -```cpp -auto func() -{ - int addend = 5; - static int baseValue = 3; - - return [addend, baseValue = baseValue]() mutable { // 使用C++14的捕获初始化拷贝一份变量 - ++baseValue; // 修改自己的拷贝,不会影响静态变量的值 - return baseValue + addend; - }; -} -``` - -参考:《Effective Modern C++》:Item 31: Avoid default capture modes. - -## 接口 -### 建议10.4.1 不涉及所有权的场景,使用`T*`或`T&`作为参数,而不是智能指针 -**理由** -1. 只在需要明确所有权机制时,才通过智能指针转移或共享所有权. -2. 通过智能指针传递,限制了函数调用者必须使用智能指针(如调用者希望传递`this`)。 -3. 传递共享所有权的智能指针存在运行时的开销。 - -**示例** - -```cpp -// 接受任何 int* -void F(int*); - -// 只能接受希望转移所有权的 int -void G(unique_ptr); - -// 只能接受希望共享所有权的 int -void G(shared_ptr); - -// 不改变所有权,但需要特定所有权的调用者 -void H(const unique_ptr&); - -// 接受任何 int -void H(int&); - -// 不好 -void F(shared_ptr& w) -{ - // ... - Use(*w); // 只使用 w -- 完全不涉及生命周期管理 - // ... -}; -``` - +# C++语言编程规范 + +## 目的 +规则并不是完美的,通过禁止在特定情况下有用的特性,可能会对代码实现造成影响。但是我们制定规则的目的“为了大多数程序员可以得到更多的好处”, 如果在团队运作中认为某个规则无法遵循,希望可以共同改进该规则。 +参考该规范之前,希望您具有相应的C++语言基础能力,而不是通过该文档来学习C++语言。 +1. 了解C++语言的ISO标准; +2. 熟知C++语言的基本语言特性,包括C++ 03/11/14/17相关特性; +3. 了解C++语言的标准库; + +## 总体原则 +代码需要在保证功能正确的前提下,满足**可读、可维护、安全、可靠、可测试、高效、可移植**的特征要求。 + +## 重点关注 +1. 约定C++语言的编程风格,比如命名,排版等。 +2. C++语言的模块化设计,如何设计头文件,类,接口和函数。 +3. C++语言相关特性的优秀实践,比如常量,类型转换,资源管理,模板等。 +4. 现代C++语言的优秀实践,包括C++11/14/17中可以提高代码可维护性,提高代码可靠性的相关约定。 + + +## 约定 +**规则**:编程时必须遵守的约定(must) + +**建议**:编程时应该遵守的约定(should) + +本规范适用通用C++标准, 如果没有特定的标准版本,适用所有的版本(C++03/11/14/17)。 + +## 例外 +无论是'规则'还是'建议',都必须理解该条目这么规定的原因,并努力遵守。 +但是,有些规则和建议可能会有例外。 + +在不违背总体原则,经过充分考虑,有充足的理由的前提下,可以适当违背规范中约定。 +例外破坏了代码的一致性,请尽量避免。'规则'的例外应该是极少的。 + +下列情况,应风格一致性原则优先: +**修改外部开源代码、第三方代码时,应该遵守开源代码、第三方代码已有规范,保持风格统一。** + +# 2 命名 +## 通用命名 +__驼峰风格(CamelCase)__ +大小写字母混用,单词连在一起,不同单词间通过单词首字母大写来分开。 +按连接后的首字母是否大写,又分: 大驼峰(UpperCamelCase)和小驼峰(lowerCamelCase) + + +| 类型 | 命名风格 | +| ---------------------------------------- | --------- | +| 类类型,结构体类型,枚举类型,联合体类型等类型定义, 作用域名称 | 大驼峰 | +| 函数(包括全局函数,作用域函数,成员函数) | 大驼峰 | +| 全局变量(包括全局和命名空间域下的变量,类静态变量),局部变量,函数参数,类、结构体和联合体中的成员变量 | 小驼峰 | +| 宏,常量(const),枚举值,goto 标签 | 全大写,下划线分割 | + +注意: +上表中__常量__是指全局作用域、namespace域、类的静态成员域下,以 const或constexpr 修饰的基本数据类型、枚举、字符串类型的变量,不包括数组和其他类型变量。 +上表中__变量__是指除常量定义以外的其他变量,均使用小驼峰风格。 + +## 文件命名 +### 建议2.2.1 C++文件以.cpp结尾,头文件以.h结尾 +我们推荐使用.h作为头文件的后缀,这样头文件可以直接兼容C和C++。 +我们推荐使用.cpp作为实现文件的后缀,这样可以直接区分C++代码,而不是C代码。 + +目前业界还有一些其他的后缀的表示方法: + +- 头文件: .hh, .hpp, .hxx +- cpp文件:.cc, .cxx, .c + +如果当前项目组使用了某种特定的后缀,那么可以继续使用,但是请保持风格统一。 +但是对于本文档,我们默认使用.h和.cpp作为后缀。 + + +### 建议2.2.2 C++文件名和类名保持一致 +C++的头文件和cpp文件名和类名保持一致,使用下划线小写风格。 + +如果有一个类叫DatabaseConnection,那么对应的文件名: +- database_connection.h +- database_connection.cpp + +结构体,命名空间,枚举等定义的文件名类似。 + +## 函数命名 +函数命名统一使用大驼峰风格,一般采用动词或者动宾结构。 +```cpp +class List { +public: + void AddElement(const Element& element); + Element GetElement(const unsigned int index) const; + bool IsEmpty() const; +}; + +namespace Utils { + void DeleteUser(); +} +``` + +## 类型命名 + +类型命名采用大驼峰命名风格。 +所有类型命名——类、结构体、联合体、类型定义(typedef)、枚举——使用相同约定,例如: +```cpp +// classes, structs and unions +class UrlTable { ... +class UrlTableTester { ... +struct UrlTableProperties { ... +union Packet { ... + +// typedefs +typedef std::map PropertiesMap; + +// enums +enum UrlTableErrors { ... +``` + +对于命名空间的命名,建议使用大驼峰: +```cpp +// namespace +namespace OsUtils { + +namespace FileUtils { + +} + +} +``` + + +### 建议2.4.1 避免滥用 typedef或者#define 对基本类型起别名 +除有明确的必要性,否则不要用 typedef/#define 对基本数据类型进行重定义。 +优先使用``头文件中的基本类型: + +| 有符号类型 | 无符号类型 | 描述 | +| -------- | --------- | ---------------- | +| int8_t | uint8_t | 宽度恰为8的有/无符号整数类型 | +| int16_t | uint16_t | 宽度恰为16的有/无符号整数类型 | +| int32_t | uint32_t | 宽度恰为32的有/无符号整数类型 | +| int64_t | uint64_t | 宽度恰为64的有/无符号整数类型 | +| intptr_t | uintptr_t | 足以保存指针的有/无符号整数类型 | + + +## 变量命名 +通用变量命名采用小驼峰,包括全局变量,函数形参,局部变量,成员变量。 +```cpp +std::string tableName; // Good: 推荐此风格 +std::string tablename; // Bad: 禁止此风格 +std::string path; // Good: 只有一个单词时,小驼峰为全小写 +``` + +### 规则2.5.1 全局变量应增加 'g_' 前缀,静态变量命名不需要加特殊前缀 +全局变量是应当尽量少使用的,使用时应特别注意,所以加上前缀用于视觉上的突出,促使开发人员对这些变量的使用更加小心。 +- 全局静态变量命名与全局变量相同。 +- 函数内的静态变量命名与普通局部变量相同。 +- 类的静态成员变量和普通成员变量相同。 + +```cpp +int g_activeConnectCount; + +void Func() +{ + static int packetCount = 0; + ... +} +``` + +### 规则2.5.2 类的成员变量命名以小驼峰加后下划线组成 + +```cpp +class Foo { +private: + std::string fileName_; // 添加_后缀,类似于K&R命名风格 +}; +``` +对于struct/union的成员变量,仍采用小驼峰不加后缀的命名方式,与局部变量命名风格一致。 + +## 宏、常量、枚举命名 +宏、枚举值采用全大写,下划线连接的格式。 +全局作用域内,有名和匿名namespace内的 const 常量,类的静态成员常量,全大写,下划线连接;函数局部 const 常量和类的普通const成员变量,使用小驼峰命名风格。 + +```cpp +#define MAX(a, b) (((a) < (b)) ? (b) : (a)) // 仅对宏命名举例,并不推荐用宏实现此类功能 + +enum TintColor { // 注意,枚举类型名用大驼峰,其下面的取值是全大写,下划线相连 + RED, + DARK_RED, + GREEN, + LIGHT_GREEN +}; + +int Func(...) +{ + const unsigned int bufferSize = 100; // 函数局部常量 + char *p = new char[bufferSize]; + ... +} + +namespace Utils { + const unsigned int DEFAULT_FILE_SIZE_KB = 200; // 全局常量 +} + +``` + +# 3 格式 + +## 行宽 + +### 建议3.1.1 行宽不超过 120 个字符 +建议每行字符数不要超过 120 个。如果超过120个字符,请选择合理的方式进行换行。 + +例外: +- 如果一行注释包含了超过120 个字符的命令或URL,则可以保持一行,以方便复制、粘贴和通过grep查找; +- 包含长路径的 #include 语句可以超出120 个字符,但是也需要尽量避免; +- 编译预处理中的error信息可以超出一行。 +预处理的 error 信息在一行便于阅读和理解,即使超过 120 个字符。 +```cpp +#ifndef XXX_YYY_ZZZ +#error Header aaaa/bbbb/cccc/abc.h must only be included after xxxx/yyyy/zzzz/xyz.h, because xxxxxxxxxxxxxxxxxxxxxxxxxxxxx +#endif +``` + +## 缩进 + +### 规则3.2.1 使用空格进行缩进,每次缩进4个空格 +只允许使用空格(space)进行缩进,每次缩进为 4 个空格。不允许使用Tab符进行缩进。 +当前几乎所有的集成开发环境(IDE)都支持配置将Tab符自动扩展为4空格输入;请配置你的IDE支持使用空格进行缩进。 + +## 大括号 +### 规则3.3.1 使用 K&R 缩进风格 +__K&R风格__ +换行时,函数(不包括lambda表达式)左大括号另起一行放行首,并独占一行;其他左大括号跟随语句放行末。 +右大括号独占一行,除非后面跟着同一语句的剩余部分,如 do 语句中的 while,或者 if 语句的 else/else if,或者逗号、分号。 + +如: +```cpp +struct MyType { // 跟随语句放行末,前置1空格 + ... +}; + +int Foo(int a) +{ // 函数左大括号独占一行,放行首 + if (...) { + ... + } else { + ... + } +} +``` +推荐这种风格的理由: + +- 代码更紧凑; +- 相比另起一行,放行末使代码阅读节奏感上更连续; +- 符合后来语言的习惯,符合业界主流习惯; +- 现代集成开发环境(IDE)都具有代码缩进对齐显示的辅助功能,大括号放在行尾并不会对缩进和范围产生理解上的影响。 + + +对于空函数体,可以将大括号放在同一行: +```cpp +class MyClass { +public: + MyClass() : value_(0) {} + +private: + int value_; +}; +``` + +## 函数声明和定义 + +### 规则3.4.1 函数声明和定义的返回类型和函数名在同一行;函数参数列表超出行宽时要换行并合理对齐 +在声明和定义函数的时候,函数的返回值类型应该和函数名在同一行;如果行宽度允许,函数参数也应该放在一行;否则,函数参数应该换行,并进行合理对齐。 +参数列表的左圆括号总是和函数名在同一行,不要单独一行;右圆括号总是跟随最后一个参数。 + +换行举例: +```cpp +ReturnType FunctionName(ArgType paramName1, ArgType paramName2) // Good:全在同一行 +{ + ... +} + +ReturnType VeryVeryVeryLongFunctionName(ArgType paramName1, // 行宽不满足所有参数,进行换行 + ArgType paramName2, // Good:和上一行参数对齐 + ArgType paramName3) +{ + ... +} + +ReturnType LongFunctionName(ArgType paramName1, ArgType paramName2, // 行宽限制,进行换行 + ArgType paramName3, ArgType paramName4, ArgType paramName5) // Good: 换行后 4 空格缩进 +{ + ... +} + +ReturnType ReallyReallyReallyReallyLongFunctionName( // 行宽不满足第1个参数,直接换行 + ArgType paramName1, ArgType paramName2, ArgType paramName3) // Good: 换行后 4 空格缩进 +{ + ... +} +``` + +## 函数调用 +### 规则3.5.1 函数调用入参列表应放在一行,超出行宽换行时,保持参数进行合理对齐 +函数调用时,函数参数列表放在一行。参数列表如果超过行宽,需要换行并进行合理的参数对齐。 +左圆括号总是跟函数名,右圆括号总是跟最后一个参数。 + +换行举例: +```cpp +ReturnType result = FunctionName(paramName1, paramName2); // Good:函数参数放在一行 + +ReturnType result = FunctionName(paramName1, + paramName2, // Good:保持与上方参数对齐 + paramName3); + +ReturnType result = FunctionName(paramName1, paramName2, + paramName3, paramName4, paramName5); // Good:参数换行,4 空格缩进 + +ReturnType result = VeryVeryVeryLongFunctionName( // 行宽不满足第1个参数,直接换行 + paramName1, paramName2, paramName3); // 换行后,4 空格缩进 +``` + +如果函数调用的参数存在内在关联性,按照可理解性优先于格式排版要求,对参数进行合理分组换行。 +```cpp +// Good:每行的参数代表一组相关性较强的数据结构,放在一行便于理解 +int result = DealWithStructureLikeParams(left.x, left.y, // 表示一组相关参数 + right.x, right.y); // 表示另外一组相关参数 +``` + +## if语句 + +### 规则3.6.1 if语句必须要使用大括号 +我们要求if语句都需要使用大括号,即便只有一条语句。 + +理由: +- 代码逻辑直观,易读; +- 在已有条件语句代码上增加新代码时不容易出错; +- 对于在if语句中使用函数式宏时,有大括号保护不易出错(如果宏定义时遗漏了大括号)。 + +```cpp +if (objectIsNotExist) { // Good:单行条件语句也加大括号 + return CreateNewObject(); +} +``` +### 规则3.6.2 禁止 if/else/else if 写在同一行 +条件语句中,若有多个分支,应该写在不同行。 + +如下是正确的写法: + +```cpp +if (someConditions) { + DoSomething(); + ... +} else { // Good: else 与 if 在不同行 + ... +} +``` + +下面是不符合规范的案例: + +```cpp +if (someConditions) { ... } else { ... } // Bad: else 与 if 在同一行 +``` + +## 循环语句 +### 规则3.7.1 循环语句必须使用大括号 +和条件表达式类似,我们要求for/while循环语句必须加上大括号,即便循环体是空的,或循环语句只有一条。 +```cpp +for (int i = 0; i < someRange; i++) { // Good: 使用了大括号 + DoSomething(); +} +``` +```cpp +while (condition) { } // Good:循环体是空,使用大括号 +``` +```cpp +while (condition) { + continue; // Good:continue 表示空逻辑,使用大括号 +} +``` + +坏的例子: +```cpp +for (int i = 0; i < someRange; i++) + DoSomething(); // Bad: 应该加上括号 +``` +```cpp +while (condition); // Bad:使用分号容易让人误解是while语句中的一部分 +``` + +## switch语句 +### 规则3.8.1 switch 语句的 case/default 要缩进一层 +switch 语句的缩进风格如下: +```cpp +switch (var) { + case 0: // Good: 缩进 + DoSomething1(); // Good: 缩进 + break; + case 1: { // Good: 带大括号格式 + DoSomething2(); + break; + } + default: + break; +} +``` + +```cpp +switch (var) { +case 0: // Bad: case 未缩进 + DoSomething(); + break; +default: // Bad: default 未缩进 + break; +} +``` + +## 表达式 + +### 建议3.9.1 表达式换行要保持换行的一致性,运算符放行末 +较长的表达式,不满足行宽要求的时候,需要在适当的地方换行。一般在较低优先级运算符或连接符后面截断,运算符或连接符放在行末。 +运算符、连接符放在行末,表示“未结束,后续还有”。 +例: + +// 假设下面第一行已经不满足行宽要求 +```cpp +if ((currentValue > threshold) && // Good:换行后,逻辑操作符放在行尾 + someConditionsion) { + DoSomething(); + ... +} + +int result = reallyReallyLongVariableName1 + // Good + reallyReallyLongVariableName2; +``` +表达式换行后,注意保持合理对齐,或者4空格缩进。参考下面例子 + +```cpp +int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + + longVaribleName4 + longVaribleName5 + longVaribleName6; // Good: 4空格缩进 + +int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + + longVaribleName4 + longVaribleName5 + longVaribleName6; // Good: 保持对齐 +``` +## 变量赋值 + +### 规则3.10.1 多个变量定义和赋值语句不允许写在一行 +每行只有一个变量初始化的语句,更容易阅读和理解。 + +```cpp +int maxCount = 10; +bool isCompleted = false; +``` + +下面是不符合规范的示例: + +```cpp +int maxCount = 10; bool isCompleted = false; // Bad:多个变量初始化需要分开放在多行,每行一个变量初始化 +int x, y = 0; // Bad:多个变量定义需要分行,每行一个 + +int pointX; +int pointY; +... +pointX = 1; pointY = 2; // Bad:多个变量赋值语句放同一行 +``` +例外:for 循环头、if 初始化语句(C++17)、结构化绑定语句(C++17)中可以声明和初始化多个变量。这些语句中的多个变量声明有较强关联,如果强行分成多行会带来作用域不一致,声明和初始化割裂等问题。 + +## 初始化 +初始化包括结构体、联合体、及数组的初始化 + +### 规则3.11.1 初始化换行时要有缩进,并进行合理对齐 +结构体或数组初始化时,如果换行应保持4空格缩进。 +从可读性角度出发,选择换行点和对齐位置。 + +```cpp +const int rank[] = { + 16, 16, 16, 16, 32, 32, 32, 32, + 64, 64, 64, 64, 32, 32, 32, 32 +}; +``` + +## 指针与引用 +### 建议3.12.1 指针类型"`*`"跟随变量名或者类型,不要两边都留有或者都没有空格 +指针命名: `*`靠左靠右都可以,但是不要两边都有或者都没有空格。 +```cpp +int* p = nullptr; // Good +int *p = nullptr; // Good + +int*p = nullptr; // Bad +int * p = nullptr; // Bad +``` + +例外:当变量被 const 修饰时,"`*`" 无法跟随变量,此时也不要跟随类型。 +```cpp +const char * const VERSION = "V100"; +``` + +### 建议3.12.2 引用类型"`&`"跟随变量名或者类型,不要两边都留有或者都没有空格 +引用命名:`&`靠左靠右都可以,但是不要两边都有或者都没有空格。 +```cpp +int i = 8; + +int& p = i; // Good +int &p = i; // Good +int*& rp = pi; // Good,指针的引用,*& 一起跟随类型 +int *&rp = pi; // Good,指针的引用,*& 一起跟随变量名 +int* &rp = pi; // Good,指针的引用,* 跟随类型,& 跟随变量名 + +int & p = i; // Bad +int&p = i; // Bad +``` + +## 编译预处理 +### 规则3.13.1 编译预处理的"#"统一放在行首,嵌套编译预处理语句时,"#"可以进行缩进 +编译预处理的"#"统一放在行首,即使编译预处理的代码是嵌入在函数体中的,"#"也应该放在行首。 + + +## 空格和空行 +### 规则3.14.1 水平空格应该突出关键字和重要信息,避免不必要的留白 +水平空格应该突出关键字和重要信息,每行代码尾部不要加空格。总体规则如下: + +- if, switch, case, do, while, for等关键字之后加空格; +- 小括号内部的两侧,不要加空格; +- 大括号内部两侧有无空格,左右必须保持一致; +- 一元操作符(& * + ‐ ~ !)之后不要加空格; +- 二元操作符(= + ‐ < > * / % | & ^ <= >= == != )左右两侧加空格 +- 三目运算符(? :)符号两侧均需要空格 +- 前置和后置的自增、自减(++ --)和变量之间不加空格 +- 结构体成员操作符(. ->)前后不加空格 +- 逗号(,)前面不加空格,后面增加空格 +- 对于模板和类型转换(<>)和类型之间不要添加空格 +- 域操作符(::)前后不要添加空格 +- 冒号(:)前后根据情况来判断是否要添加空格 + +常规情况: +```cpp +void Foo(int b) { // Good:大括号前应该留空格 + +int i = 0; // Good:变量初始化时,=前后应该有空格,分号前面不要留空格 + +int buf[BUF_SIZE] = {0}; // Good:大括号内两侧都无空格 +``` + +函数定义和函数调用: +```cpp +int result = Foo(arg1,arg2); + ^ // Bad: 逗号后面需要增加空格 + +int result = Foo( arg1, arg2 ); + ^ ^ // Bad: 函数参数列表的左括号后面不应该有空格,右括号前面不应该有空格 +``` + +指针和取地址 +```cpp +x = *p; // Good:*操作符和指针p之间不加空格 +p = &x; // Good:&操作符和变量x之间不加空格 +x = r.y; // Good:通过.访问成员变量时不加空格 +x = r->y; // Good:通过->访问成员变量时不加空格 +``` + +操作符: +```cpp +x = 0; // Good:赋值操作的=前后都要加空格 +x = -5; // Good:负数的符号和数值之前不要加空格 +++x; // Good:前置和后置的++/--和变量之间不要加空格 +x--; + +if (x && !y) // Good:布尔操作符前后要加上空格,!操作和变量之间不要空格 +v = w * x + y / z; // Good:二元操作符前后要加空格 +v = w * (x + z); // Good:括号内的表达式前后不需要加空格 + +int a = (x < y) ? x : y; // Good: 三目运算符, ?和:前后需要添加空格 +``` + +循环和条件语句: +```cpp +if (condition) { // Good:if关键字和括号之间加空格,括号内条件语句前后不加空格 + ... +} else { // Good:else关键字和大括号之间加空格 + ... +} + +while (condition) {} // Good:while关键字和括号之间加空格,括号内条件语句前后不加空格 + +for (int i = 0; i < someRange; ++i) { // Good:for关键字和括号之间加空格,分号之后加空格 + ... +} + +switch (condition) { // Good: switch 关键字后面有1空格 + case 0: // Good:case语句条件和冒号之间不加空格 + ... + break; + ... + default: + ... + break; +} +``` + +模板和转换 +```cpp +// 尖括号(< and >) 不与空格紧邻, < 前没有空格, > 和 ( 之间也没有. +vector x; +y = static_cast(x); + +// 在类型与指针操作符之间留空格也可以, 但要保持一致. +vector x; +``` + +域操作符 +```cpp +std::cout; // Good: 命名空间访问,不要留空格 + +int MyClass::GetValue() const {} // Good: 对于成员函数定义,不要留空格 +``` + +冒号 +```cpp +// 添加空格的场景 + +// Good: 类的派生需要留有空格 +class Sub : public Base { + +}; + +// 构造函数初始化列表需要留有空格 +MyClass::MyClass(int var) : someVar_(var) +{ + DoSomething(); +} + +// 位域表示也留有空格 +struct XX { + char a : 4; + char b : 5; + char c : 4; +}; +``` + +```cpp +// 不添加空格的场景 + +// Good: 对于public:, private:这种类访问权限的冒号不用添加空格 +class MyClass { +public: + MyClass(int var); +private: + int someVar_; +}; + +// 对于switch-case的case和default后面的冒号不用添加空格 +switch (value) +{ + case 1: + DoSomething(); + break; + default: + break; +} +``` + +注意:当前的集成开发环境(IDE)可以设置删除行尾的空格,请正确配置。 + +### 建议3.14.1 合理安排空行,保持代码紧凑 + +减少不必要的空行,可以显示更多的代码,方便代码阅读。下面有一些建议遵守的规则: +- 根据上下内容的相关程度,合理安排空行; +- 函数内部、类型定义内部、宏内部、初始化表达式内部,不使用连续空行 +- 不使用连续 **3** 个空行,或更多 +- 大括号内的代码块行首之前和行尾之后不要加空行,但namespace的大括号内不作要求。 + +```cpp +int Foo() +{ + ... +} + + + +int Bar() // Bad:最多使用连续2个空行。 +{ + ... +} + + +if (...) { + // Bad:大括号内的代码块行首不要加入空行 + ... + // Bad:大括号内的代码块行尾不要加入空行 +} + +int Foo(...) +{ + // Bad:函数体内行首不要加空行 + ... +} +``` + +## 类 +### 规则3.15.1 类访问控制块的声明依次序是 public:, protected:, private:,缩进和 class 关键字对齐 +```cpp +class MyClass : public BaseClass { +public: // 注意没有缩进 + MyClass(); // 标准的4空格缩进 + explicit MyClass(int var); + ~MyClass() {} + + void SomeFunction(); + void SomeFunctionThatDoesNothing() + { + } + + void SetVar(int var) { someVar_ = var; } + int GetVar() const { return someVar_; } + +private: + bool SomeInternalFunction(); + + int someVar_; + int someOtherVar_; +}; +``` + +在各个部分中,建议将类似的声明放在一起, 并且建议以如下的顺序: 类型 (包括 typedef, using 和嵌套的结构体与类), 常量, 工厂函数, 构造函数, 赋值运算符, 析构函数, 其它成员函数, 数据成员。 + + +### 规则3.15.2 构造函数初始化列表放在同一行或按四格缩进并排多行 +```cpp +// 如果所有变量能放在同一行: +MyClass::MyClass(int var) : someVar_(var) +{ + DoSomething(); +} + +// 如果不能放在同一行, +// 必须置于冒号后, 并缩进4个空格 +MyClass::MyClass(int var) + : someVar_(var), someOtherVar_(var + 1) // Good: 逗号后面留有空格 +{ + DoSomething(); +} + +// 如果初始化列表需要置于多行, 需要逐行对齐 +MyClass::MyClass(int var) + : someVar_(var), // 缩进4个空格 + someOtherVar_(var + 1) +{ + DoSomething(); +} +``` + +# 4 注释 +一般的,尽量通过清晰的架构逻辑,好的符号命名来提高代码可读性;需要的时候,才辅以注释说明。 +注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,**按需注释**。 + +注释内容要简洁、明了、无二义性,信息全面且不冗余。 + +**注释跟代码一样重要。** +写注释时要换位思考,用注释去表达此时读者真正需要的信息。在代码的功能、意图层次上进行注释,即注释解释代码难以表达的意图,不要重复代码信息。 +修改代码时,也要保证其相关注释的一致性。只改代码,不改注释是一种不文明行为,破坏了代码与注释的一致性,让阅读者迷惑、费解,甚至误解。 + +使用英文进行注释。 + +## 注释风格 + +在 C++ 代码中,使用 `/*` `*/`和 `//` 都是可以的。 +按注释的目的和位置,注释可分为不同的类型,如文件头注释、函数头注释、代码注释等等; +同一类型的注释应该保持统一的风格。 + +注意:本文示例代码中,大量使用 '//' 后置注释只是为了更精确的描述问题,并不代表这种注释风格更好。 + +## 文件头注释 +### 规则3.1 文件头注释必须包含版权许可 + +/* + * Copyright (c) 2020 XXX + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +## 函数头注释 +### 规则4.3.1 禁止空有格式的函数头注释 +并不是所有的函数都需要函数头注释; +函数签名无法表达的信息,加函数头注释辅助说明; + +函数头注释统一放在函数声明或定义上方,使用如下风格之一: +使用`//`写函数头 + +```cpp +// 单行函数头 +int Func1(void); + +// 多行函数头 +// 第二行 +int Func2(void); +``` + +使用`/* */`写函数头 +```cpp +/* 单行函数头 */ +int Func1(void); + +/* + * 另一种单行函数头 + */ +int Func2(void); + +/* + * 多行函数头 + * 第二行 + */ +int Func3(void); +``` +函数尽量通过函数名自注释,按需写函数头注释。 +不要写无用、信息冗余的函数头;不要写空有格式的函数头。 + +函数头注释内容可选,但不限于:功能说明、返回值,性能约束、用法、内存约定、算法实现、可重入的要求等等。 +模块对外头文件中的函数接口声明,其函数头注释,应当将重要、有用的信息表达清楚。 + +例: + +```cpp +/* + * 返回实际写入的字节数,-1表示写入失败 + * 注意,内存 buf 由调用者负责释放 + */ +int WriteString(const char *buf, int len); +``` + +坏的例子: +```cpp +/* + * 函数名:WriteString + * 功能:写入字符串 + * 参数: + * 返回值: + */ +int WriteString(const char *buf, int len); +``` +上面例子中的问题: + +- 参数、返回值,空有格式没内容 +- 函数名信息冗余 +- 关键的 buf 由谁释放没有说清楚 + +## 代码注释 +### 规则4.4.1 代码注释放于对应代码的上方或右边 +### 规则4.4.2 注释符与注释内容间要有1空格;右置注释与前面代码至少1空格 +代码上方的注释,应该保持对应代码一样的缩进。 +选择并统一使用如下风格之一: +使用`//` +```cpp + +// 这是单行注释 +DoSomething(); + +// 这是多行注释 +// 第二行 +DoSomething(); +``` + +使用`/*' '*/` +```cpp +/* 这是单行注释 */ +DoSomething(); + +/* + * 另一种方式的多行注释 + * 第二行 + */ +DoSomething(); +``` +代码右边的注释,与代码之间,至少留1空格,建议不超过4空格。 +通常使用扩展后的 TAB 键即可实现 1-4 空格的缩进。 + +选择并统一使用如下风格之一: + +```cpp +int foo = 100; // 放右边的注释 +int bar = 200; /* 放右边的注释 */ +``` +右置格式在适当的时候,上下对齐会更美观。 +对齐后的注释,离左边代码最近的那一行,保证1-4空格的间隔。 +例: + +```cpp +const int A_CONST = 100; /* 相关的同类注释,可以考虑上下对齐 */ +const int ANOTHER_CONST = 200; /* 上下对齐时,与左侧代码保持间隔 */ +``` +当右置的注释超过行宽时,请考虑将注释置于代码上方。 + +### 规则4.4.3 不用的代码段直接删除,不要注释掉 +被注释掉的代码,无法被正常维护;当企图恢复使用这段代码时,极有可能引入易被忽略的缺陷。 +正确的做法是,不需要的代码直接删除掉。若再需要时,考虑移植或重写这段代码。 + +这里说的注释掉代码,包括用 /* */ 和 //,还包括 #if 0, #ifdef NEVER_DEFINED 等等。 + +# 5 头文件 +## 头文件职责 +头文件是模块或文件的对外接口,头文件的设计体现了大部分的系统设计。 +头文件中适合放置接口的声明,不适合放置实现(内联函数除外)。对于cpp文件中内部才需要使用的函数、宏、枚举、结构定义等不要放在头文件中。 +头文件应当职责单一。头文件过于复杂,依赖过于复杂还是导致编译时间过长的主要原因。 + +### 建议5.1.1 每一个.cpp文件应有一个对应的.h文件,用于声明需要对外公开的类与接口 +通常情况下,每个.cpp文件都有一个相应的.h,用于放置对外提供的函数声明、宏定义、类型定义等。 +如果一个.cpp文件不需要对外公布任何接口,则其就不应当存在。 +例外:__程序的入口(如main函数所在的文件),单元测试代码,动态库代码。__ + +示例: +```cpp +// Foo.h + +#ifndef FOO_H +#define FOO_H + +class Foo { +public: + Foo(); + void Fun(); + +private: + int value_; +}; + +#endif +``` + +```cpp +// Foo.cpp +#include "Foo.h" + +namespace { // Good: 对内函数的声明放在.cpp文件的头部,并声明为匿名namespace或者static限制其作用域 + void Bar() + { + } +} + +... + +void Foo::Fun() +{ + Bar(); +} +``` + +## 头文件依赖 +### 规则5.2.1 禁止头文件循环依赖 +头文件循环依赖,指 a.h 包含 b.h,b.h 包含 c.h,c.h 包含 a.h, 导致任何一个头文件修改,都导致所有包含了a.h/b.h/c.h的代码全部重新编译一遍。 +而如果是单向依赖,如a.h包含b.h,b.h包含c.h,而c.h不包含任何头文件,则修改a.h不会导致包含了b.h/c.h的源代码重新编译。 + +头文件循环依赖直接体现了架构设计上的不合理,可通过优化架构去避免。 + + +### 规则5.2.2 头文件必须编写`#define`保护,防止重复包含 +为防止头文件被重复包含,所有头文件都应当使用 #define 保护;不要使用 #pragma once + +定义包含保护符时,应该遵守如下规则: +1)保护符使用唯一名称; +2)不要在受保护部分的前后放置代码或者注释,文件头注释除外。 + +示例:假定timer模块的timer.h,其目录为timer/include/timer.h,应按如下方式保护: + +```cpp +#ifndef TIMER_INCLUDE_TIMER_H +#define TIMER_INCLUDE_TIMER_H +... +#endif +``` + +### 规则5.2.3 禁止通过声明的方式引用外部函数接口、变量 +只能通过包含头文件的方式使用其他模块或文件提供的接口。 +通过 extern 声明的方式使用外部函数接口、变量,容易在外部接口改变时可能导致声明和定义不一致。 +同时这种隐式依赖,容易导致架构腐化。 + +不符合规范的案例: + +// a.cpp内容 +```cpp +extern int Fun(); // Bad: 通过extern的方式使用外部函数 + +void Bar() +{ + int i = Fun(); + ... +} +``` + +// b.cpp内容 +```cpp +int Fun() +{ + // Do something +} +``` +应该改为: + +// a.cpp内容 +```cpp +#include "b.h" // Good: 通过包含头文件的方式使用其他.cpp提供的接口 + +void Bar() +{ + int i = Fun(); + ... +} +``` + +// b.h内容 +```cpp +int Fun(); +``` + +// b.cpp内容 +```cpp +int Fun() +{ + // Do something +} +``` +例外,有些场景需要引用其内部函数,但并不想侵入代码时,可以 extern 声明方式引用。 +如: +针对某一内部函数进行单元测试时,可以通过 extern 声明来引用被测函数; +当需要对某一函数进行打桩、打补丁处理时,允许 extern 声明该函数。 + +### 规则5.2.4 禁止在extern "C"中包含头文件 +在 extern "C" 中包含头文件,有可能会导致 extern "C" 嵌套,部分编译器对 extern "C" 嵌套层次有限制,嵌套层次太多会编译错误。 + +在C,C++混合编程的情况下,在extern "C"中包含头文件,可能会导致被包含头文件的原有意图遭到破坏,比如链接规范被不正确地更改。 + +示例,存在a.h和b.h两个头文件: + +// a.h内容 +```cpp +... +#ifdef __cplusplus +void Foo(int); +#define A(value) Foo(value) +#else +void A(int) +#endif +``` +// b.h内容 +```cpp +... +#ifdef __cplusplus +extern "C" { +#endif + +#include "a.h" +void B(); + +#ifdef __cplusplus +} +#endif +``` + +使用C++预处理器展开b.h,将会得到 +```cpp +extern "C" { + void Foo(int); + void B(); +} +``` + +按照 a.h 作者的本意,函数 Foo 是一个 C++ 自由函数,其链接规范为 "C++"。 +但在 b.h 中,由于 `#include "a.h"` 被放到了 `extern "C"` 的内部,函数 Foo 的链接规范被不正确地更改了。 + +例外: +如果在 C++ 编译环境中,想引用纯C的头文件,这些C头文件并没有` extern "C"` 修饰。非侵入式的做法是,在 `extern "C"` 中去包含C头文件。 + +### 建议5.2.1尽量避免使用前置声明,而是通过`#include`来包含头文件 +前置声明(forward declaration)通常指类、模板的纯粹声明,没伴随着其定义。 + +- 优点: + 1. 前置声明能够节省编译时间,多余的 #include 会迫使编译器展开更多的文件,处理更多的输入。 + 2. 前置声明能够节省不必要的重新编译的时间。 #include 使代码因为头文件中无关的改动而被重新编译多次。 +- 缺点: + 1. 前置声明隐藏了依赖关系,头文件改动时,用户的代码会跳过必要的重新编译过程。 + 2. 前置声明可能会被库的后续更改所破坏。前置声明模板有时会妨碍头文件开发者变动其 API. 例如扩大形参类型,加个自带默认参数的模板形参等等。 + 3. 前置声明来自命名空间` std::` 的 symbol 时,其行为未定义(在C++11标准规范中明确说明)。 + 4. 前置声明了不少来自头文件的 symbol 时,就会比单单一行的 include 冗长。 + 5. 仅仅为了能前置声明而重构代码(比如用指针成员代替对象成员)会使代码变得更慢更复杂。 + 6. 很难判断什么时候该用前置声明,什么时候该用`#include`,某些场景下面前置声明和`#include`互换以后会导致意想不到的结果。 + +所以我们尽可能避免使用前置声明,而是使用#include头文件来保证依赖关系。 + +# 6 作用域 + +## 命名空间 + +### 建议6.1.1 对于cpp文件中不需要导出的变量,常量或者函数,请使用匿名namespace封装或者用static修饰 +在C++ 2003标准规范中,使用static修饰文件作用域的变量,函数等被标记为deprecated特性,所以更推荐使用匿名namespace。 + +主要原因如下: +1. static在C++中已经赋予了太多的含义,静态函数成员变量,静态成员函数,静态全局变量,静态函数局部变量,每一种都有特殊的处理。 +2. static只能保证变量,常量和函数的文件作用域,但是namespace还可以封装类型等。 +3. 统一namespace来处理C++的作用域,而不需要同时使用static和namespace来管理。 +4. static修饰的函数不能用来实例化模板,而匿名namespace可以。 + +但是不要在 .h 中使用中使用匿名namespace或者static。 + +```cpp +// Foo.cpp + +namespace { + const int MAX_COUNT = 20; + void InternalFun() {}; +} + +void Foo::Fun() +{ + int i = MAX_COUNT; + + InternalFun(); +} + +``` + +### 规则6.1.1 不要在头文件中或者#include之前使用using导入命名空间 +说明:使用using导入命名空间会影响后续代码,易造成符号冲突,所以不要在头文件以及源文件中的#include之前使用using导入命名空间。 +示例: + +```cpp +// 头文件a.h +namespace NamespaceA { + int Fun(int); +} +``` + +```cpp +// 头文件b.h +namespace NamespaceB { + int Fun(int); +} + +using namespace NamespaceB; + +void G() +{ + Fun(1); +} +``` + +```cpp +// 源代码a.cpp +#include "a.h" +using namespace NamespaceA; +#include "b.h" + +void main() +{ + G(); // using namespace NamespaceA在#include “b.h”之前,引发歧义:NamespaceA::Fun,NamespaceB::Fun调用不明确 +} +``` + +对于在头文件中使用using导入单个符号或定义别名,允许在模块自定义名字空间中使用,但禁止在全局名字空间中使用。 +```cpp +// foo.h + +#include +using fancy::string; // Bad,禁止向全局名字空间导入符号 + +namespace Foo { + using fancy::string; // Good,可以在模块自定义名字空间中导入符号 + using MyVector = fancy::vector; // Good,C++11可在自定义名字空间中定义别名 +} +``` + + +## 全局函数和静态成员函数 + +### 建议6.2.1 优先使用命名空间来管理全局函数,如果和某个class有直接关系的,可以使用静态成员函数 +说明:非成员函数放在名字空间内可避免污染全局作用域, 也不要用类+静态成员方法来简单管理全局函数。 如果某个全局函数和某个类有紧密联系, 那么可以作为类的静态成员函数。 + +如果你需要定义一些全局函数,给某个cpp文件使用,那么请使用匿名namespace来管理。 +```cpp +namespace MyNamespace { + int Add(int a, int b); +} + +class File { +public: + static File CreateTempFile(const std::string& fileName); +}; +``` + +## 全局常量和静态成员常量 + +### 建议6.3.1 优先使用命名空间来管理全局常量,如果和某个class有直接关系的,可以使用静态成员常量 +说明:全局常量放在命名空间内可避免污染全局作用域, 也不要用类+静态成员常量来简单管理全局常量。 如果某个全局常量和某个类有紧密联系, 那么可以作为类的静态成员常量。 + +如果你需要定义一些全局常量,只给某个cpp文件使用,那么请使用匿名namespace来管理。 +```cpp +namespace MyNamespace { + const int MAX_SIZE = 100; +} + +class File { +public: + static const std::string SEPARATOR; +}; +``` + +## 全局变量 + +### 建议6.4.1 尽量避免使用全局变量,考虑使用单例模式 +说明:全局变量是可以修改和读取的,那么这样会导致业务代码和这个全局变量产生数据耦合。 +```cpp +int g_counter = 0; + +// a.cpp +g_counter++; + +// b.cpp +g_counter++; + +// c.cpp +cout << g_counter << endl; +``` + +使用单实例模式 +```cpp +class Counter { +public: + static Counter& GetInstance() + { + static Counter counter; + return counter; + } // 单实例实现简单举例 + + void Increase() + { + value_++; + } + + void Print() const + { + std::cout << value_ << std::endl; + } + +private: + Counter() : value_(0) {} + +private: + int value_; +}; + +// a.cpp +Counter::GetInstance().Increase(); + +// b.cpp +Counter::GetInstance().Increase(); + +// c.cpp +Counter::GetInstance().Print(); +``` + +实现单例模式以后,实现了全局唯一一个实例,和全局变量同样的效果,并且单实例提供了更好的封装性。 + +例外:有的时候全局变量的作用域仅仅是模块内部,这样进程空间里面就会有多个全局变量实例,每个模块持有一份,这种场景下是无法使用单例模式解决的。 + +# 7 类 + +## 构造,拷贝构造,赋值和析构函数 +构造,拷贝,移动和析构函数提供了对象的生命周期管理方法: +- 构造函数(constructor): `X()` +- 拷贝构造函数(copy constructor):`X(const X&)` +- 拷贝赋值操作符(copy assignment):`operator=(const X&)` +- 移动构造函数(move constructor):`X(X&&)` *C++11以后提供* +- 移动赋值操作符(move assignment):`operator=(X&&)` *C++11以后提供* +- 析构函数(destructor):`~X()` + +### 规则7.1.1 类的成员变量必须显式初始化 +说明:如果类有成员变量,没有定义构造函数,又没有定义默认构造函数,编译器将自动生成一个构造函数,但编译器生成的构造函数并不会对成员变量进行初始化,对象状态处于一种不确定性。 + +例外: +- 如果类的成员变量具有默认构造函数,那么可以不需要显式初始化。 + +示例:如下代码没有构造函数,私有数据成员无法初始化: +```cpp +class Message { +public: + void ProcessOutMsg() + { + //… + } + +private: + unsigned int msgID_; + unsigned int msgLength_; + unsigned char* msgBuffer_; + std::string someIdentifier_; +}; + +Message message; // message成员变量没有初始化 +message.ProcessOutMsg(); // 后续使用存在隐患 + +// 因此,有必要定义默认构造函数,如下: +class Message { +public: + Message() : msgID_(0), msgLength_(0), msgBuffer_(nullptr) + { + } + + void ProcessOutMsg() + { + // … + } + +private: + unsigned int msgID_; + unsigned int msgLength_; + unsigned char* msgBuffer_; + std::string someIdentifier_; // 具有默认构造函数,不需要显式初始化 +}; +``` + +### 建议7.1.1 成员变量优先使用声明时初始化(C++11)和构造函数初始化列表初始化 +说明:C++11的声明时初始化可以一目了然的看出成员初始值,应当优先使用。如果成员初始化值和构造函数相关,或者不支持C++11,则应当优先使用构造函数初始化列表来初始化成员。相比起在构造函数体中对成员赋值,初始化列表的代码更简洁,执行性能更好,而且可以对const成员和引用成员初始化。 + +```cpp +class Message { +public: + Message() : msgLength_(0) // Good,优先使用初始化列表 + { + msgBuffer_ = nullptr; // Bad,不推荐在构造函数中赋值 + } + +private: + unsigned int msgID_{0}; // Good,C++11中使用 + unsigned int msgLength_; + unsigned char* msgBuffer_; +}; +``` + +### 规则7.1.2 为避免隐式转换,将单参数构造函数声明为explicit +说明:单参数构造函数如果没有用explicit声明,则会成为隐式转换函数。 +示例: + +```cpp +class Foo { +public: + explicit Foo(const string& name): name_(name) + { + } +private: + string name_; +}; + + +void ProcessFoo(const Foo& foo){} + +int main(void) +{ + std::string test = "test"; + ProcessFoo(test); // 编译不通过 + return 0; +} +``` + +上面的代码编译不通过,因为`ProcessFoo`需要的参数是Foo类型,传入的string类型不匹配。 + +如果将Foo构造函数的explicit关键字移除,那么调用`ProcessFoo`传入的string就会触发隐式转换,生成一个临时的Foo对象。往往这种隐式转换是让人迷惑的,并且容易隐藏Bug,得到了一个不期望的类型转换。所以对于单参数的构造函数是要求explicit声明。 + +### 规则7.1.3 如果不需要拷贝构造函数、赋值操作符 / 移动构造函数、赋值操作符,请明确禁止 +说明:如果用户不定义,编译器默认会生成拷贝构造函数和拷贝赋值操作符, 移动构造和移动赋值操作符(移动语义的函数C++11以后才有)。 +如果我们不要使用拷贝构造函数,或者赋值操作符,请明确拒绝: + +1. 将拷贝构造函数或者赋值操作符设置为private,并且不实现: +```cpp +class Foo { +private: + Foo(const Foo&); + Foo& operator=(const Foo&); +}; +``` +2. 使用C++11提供的delete, 请参见后面现代C++的相关章节。 + +### 规则7.1.4 拷贝构造和拷贝赋值操作符应该是成对出现或者禁止 +拷贝构造函数和拷贝赋值操作符都是具有拷贝语义的,应该同时出现或者禁止。 + +```cpp +// 同时出现 +class Foo { +public: + ... + Foo(const Foo&); + Foo& operator=(const Foo&); + ... +}; + +// 同时default, C++11支持 +class Foo { +public: + Foo(const Foo&) = default; + Foo& operator=(const Foo&) = default; +}; + +// 同时禁止, C++11可以使用delete +class Foo { +private: + Foo(const Foo&); + Foo& operator=(const Foo&); +}; +``` + +### 规则7.1.5 移动构造和移动赋值操作符应该是成对出现或者禁止 +在C++11中增加了move操作,如果需要某个类支持移动操作,那么需要实现移动构造和移动赋值操作符。 + +移动构造函数和移动赋值操作符都是具有移动语义的,应该同时出现或者禁止。 +```cpp +// 同时出现 +class Foo { +public: + ... + Foo(Foo&&); + Foo& operator=(Foo&&); + ... +}; + +// 同时default, C++11支持 +class Foo { +public: + Foo(Foo&&) = default; + Foo& operator=(Foo&&) = default; +}; + +// 同时禁止, 使用C++11的delete +class Foo { +public: + Foo(Foo&&) = delete; + Foo& operator=(Foo&&) = delete; +}; +``` + +### 规则7.1.6 禁止在构造函数和析构函数中调用虚函数 +说明:在构造函数和析构函数中调用当前对象的虚函数,会导致未实现多态的行为。 +在C++中,一个基类一次只构造一个完整的对象。 + +示例:类Base是基类,Sub是派生类 +```cpp +class Base { +public: + Base(); + virtual void Log() = 0; // 不同的派生类调用不同的日志文件 +}; + +Base::Base() // 基类构造函数 +{ + Log(); // 调用虚函数Log +} + +class Sub : public Base { +public: + virtual void Log(); +}; +``` + +当执行如下语句: +`Sub sub;` +会先执行Sub的构造函数,但首先调用Base的构造函数,由于Base的构造函数调用虚函数Log,此时Log还是基类的版本,只有基类构造完成后,才会完成派生类的构造,从而导致未实现多态的行为。 +同样的道理也适用于析构函数。 + + +## 继承 + +### 规则7.2.1 基类的析构函数应该声明为virtual +说明:只有基类析构函数是virtual,通过多态调用的时候才能保证派生类的析构函数被调用。 + +示例:基类的析构函数没有声明为virtual导致了内存泄漏。 +```cpp +class Base { +public: + virtual std::string getVersion() = 0; + + ~Base() + { + std::cout << "~Base" << std::endl; + } +}; +``` + +```cpp +class Sub : public Base { +public: + Sub() : numbers_(nullptr) + { + } + + ~Sub() + { + delete[] numbers_; + std::cout << "~Sub" << std::endl; + } + + int Init() + { + const size_t numberCount = 100; + numbers_ = new (std::nothrow) int[numberCount]; + if (numbers_ == nullptr) { + return -1; + } + + ... + } + + std::string getVersion() + { + return std::string("hello!"); + } +private: + int* numbers_; +}; +``` + +```cpp +int main(int argc, char* args[]) +{ + Base* b = new Sub(); + + delete b; + return 0; +} +``` +由于基类Base的析构函数没有声明为virtual,当对象被销毁时,只会调用基类的析构函数,不会调用派生类Sub的析构函数,导致内存泄漏。 + + +### 规则7.2.2 禁止虚函数使用缺省参数值 +说明:在C++中,虚函数是动态绑定的,但函数的缺省参数却是在编译时就静态绑定的。这意味着你最终执行的函数是一个定义在派生类,但使用了基类中的缺省参数值的虚函数。为了避免虚函数重载时,因参数声明不一致给使用者带来的困惑和由此导致的问题,规定所有虚函数均不允许声明缺省参数值。 +示例:虚函数display缺省参数值text是由编译时刻决定的,而非运行时刻,没有达到多态的目的: +```cpp +class Base { +public: + virtual void Display(const std::string& text = "Base!") + { + std::cout << text << std::endl; + } + + virtual ~Base(){} +}; + +class Sub : public Base { +public: + virtual void Display(const std::string& text = "Sub!") + { + std::cout << text << std::endl; + } + + virtual ~Sub(){} +}; + +int main() +{ + Base* base = new Sub(); + Sub* sub = new Sub(); + + ... + + base->Display(); // 程序输出结果: Base! 而期望输出:Sub! + sub->Display(); // 程序输出结果: Sub! + + delete base; + delete sub; + return 0; +}; +``` + +### 规则7.2.3 禁止重新定义继承而来的非虚函数 +说明:因为非虚函数无法实现动态绑定,只有虚函数才能实现动态绑定:只要操作基类的指针,即可获得正确的结果。 + +示例: +```cpp +class Base { +public: + void Fun(); +}; + +class Sub : public Base { +public: + void Fun(); +}; + +Sub* sub = new Sub(); +Base* base = sub; + +sub->Fun(); // 调用子类的Fun +base->Fun(); // 调用父类的Fun +//... + +``` + +## 多重继承 +在实际开发过程中使用多重继承的场景是比较少的,因为多重继承使用过程中有下面的典型问题: +1. 菱形继承所带来的数据重复,以及名字二义性。因此,C++引入了virtual继承来解决这类问题; +2. 即便不是菱形继承,多个父类之间的名字也可能存在冲突,从而导致的二义性; +3. 如果子类需要扩展或改写多个父类的方法时,造成子类的职责不明,语义混乱; +4. 相对于委托,继承是一种白盒复用,即子类可以访问父类的protected成员, 这会导致更强的耦合。而多重继承,由于耦合了多个父类,相对于单根继承,这会产生更强的耦合关系。 + +多重继承具有下面的优点: +多重继承提供了一种更简单的组合来实现多种接口或者类的组装与复用。 + +所以,对于多重继承的只有下面几种情况下面才允许使用多重继承。 + +### 建议7.3.1 使用多重继承来实现接口分离与多角色组合 +如果某个类需要实现多重接口,可以通过多重继承把多个分离的接口组合起来,类似 scala 语言的 traits 混入。 + +```cpp +class Role1 {}; +class Role2 {}; +class Role3 {}; + +class Object1 : public Role1, public Role2 { + // ... +}; + +class Object2 : public Role2, public Role3 { + // ... +}; + +``` + +在C++标准库中也有类似的实现样例: +```cpp +class basic_istream {}; +class basic_ostream {}; + +class basic_iostream : public basic_istream, public basic_ostream { + +}; +``` + +## 重载 + +重载操作符要有充分理由,而且不要改变操作符原有语义,例如不要使用 ‘+’ 操作符来做减运算。 +操作符重载令代码更加直观,但也有一些不足: +- 混淆直觉,误以为该操作和内建类型一样是高性能的,忽略了性能降低的可能; +- 问题定位时不够直观,按函数名查找比按操作符显然更方便。 +- 重载操作符如果行为定义不直观(例如将‘+’ 操作符来做减运算),会让代码产生混淆。 +- 赋值操作符的重载引入的隐式转换会隐藏很深的bug。可以定义类似Equals()、CopyFrom()等函数来替代=,==操作符。 + + + +# 8 函数 +## 函数设计 +### 规则8.1.1 避免函数过长,函数不超过50行(非空非注释) +函数应该可以一屏显示完 (50行以内),只做一件事情,而且把它做好。 + +过长的函数往往意味着函数功能不单一,过于复杂,或过分呈现细节,未进行进一步抽象。 + +例外:某些实现算法的函数,由于算法的聚合性与功能的全面性,可能会超过50行。 + +即使一个长函数现在工作的非常好, 一旦有人对其修改, 有可能出现新的问题, 甚至导致难以发现的bug。 +建议将其拆分为更加简短并易于管理的若干函数,以便于他人阅读和修改代码。 + +## 内联函数 + +### 建议8.2.1 内联函数不超过10行(非空非注释) +**说明**:内联函数具有一般函数的特性,它与一般函数不同之处只在于函数调用的处理。一般函数进行调用时,要将程序执行权转到被调用函数中,然后再返回到调用它的函数中;而内联函数在调用时,是将调用表达式用内联函数体来替换。 + +内联函数只适合于只有 1~10 行的小函数。对一个含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,也没有必要用内联函数实现,一般的编译器会放弃内联方式,而采用普通的方式调用函数。 + +如果内联函数包含复杂的控制结构,如循环、分支(switch)、try-catch 等语句,一般编译器将该函数视同普通函数。 +**虚函数、递归函数不能被用来做内联函数**。 + +## 函数参数 + +### 建议8.3.1 函数参数使用引用取代指针 + +**说明**:引用比指针更安全,因为它一定非空,且一定不会再指向其他目标;引用不需要检查非法的NULL指针。 + +如果是基于老平台开发的产品,则优先顺从原有平台的处理方式。 +选择 const 避免参数被修改,让代码阅读者清晰地知道该参数不被修改,可大大增强代码可读性。 + +例外:当传入参数为编译期长度未知的数组时,可以使用指针而不是引用。 + +### 建议8.3.2 使用强类型参数,避免使用void* +尽管不同的语言对待强类型和弱类型有自己的观点,但是一般认为c/c++是强类型语言,既然我们使用的语言是强类型的,就应该保持这样的风格。 +好处是尽量让编译器在编译阶段就检查出类型不匹配的问题。 + +使用强类型便于编译器帮我们发现错误,如下代码中注意函数 FooListAddNode 的使用: +```cpp +struct FooNode { + struct List link; + int foo; +}; + +struct BarNode { + struct List link; + int bar; +} + +void FooListAddNode(void *node) // Bad: 这里用 void * 类型传递参数 +{ + FooNode *foo = (FooNode *)node; + ListAppend(&g_FooList, &foo->link); +} + +void MakeTheList() +{ + FooNode *foo = nullptr; + BarNode *bar = nullptr; + ... + + FooListAddNode(bar); // Wrong: 这里本意是想传递参数 foo,但错传了 bar,却没有报错 +} +``` + +1. 可以使用模板函数来实现参数类型的变化。 +2. 可以使用基类指针来实现多态。 + +### 建议8.3.3 函数的参数个数不超过5个 +函数的参数过多,会使得该函数易于受外部变化的影响,从而影响维护工作。函数的参数过多同时也会增大测试的工作量。 + +如果超过可以考虑: +- 看能否拆分函数 +- 看能否将相关参数合在一起,定义结构体 + +# 9 C++其他特性 + +## 常量与初始化 + +不变的值更易于理解、跟踪和分析,所以应该尽可能地使用常量代替变量,定义值的时候,应该把const作为默认的选项。 + +### 规则9.1.1 不允许使用宏来表示常量 + +**说明**:宏是简单的文本替换,在预处理阶段时完成,运行报错时直接报相应的值;跟踪调试时也是显示值,而不是宏名;宏没有类型检查,不安全;宏没有作用域。 + +```cpp +#define MAX_MSISDN_LEN 20 // 不好 + +// C++请使用const常量 +const int MAX_MSISDN_LEN = 20; // 好 + +// 对于C++11以上版本,可以使用constexpr +constexpr int MAX_MSISDN_LEN = 20; +``` + +### 建议9.1.1 一组相关的整型常量应定义为枚举 + +**说明**:枚举比`#define`或`const int`更安全。编译器会检查参数值是否位于枚举取值范围内,避免错误发生。 + +```cpp +// 好的例子: +enum Week { + SUNDAY, + MONDAY, + TUESDAY, + WEDNESDAY, + THURSDAY, + FRIDAY, + SATURDAY +}; + +enum Color { + RED, + BLACK, + BLUE +}; + +void ColorizeCalendar(Week today, Color color); + +ColorizeCalendar(BLUE, SUNDAY); // 编译报错,参数类型错误 + +// 不好的例子: +const int SUNDAY = 0; +const int MONDAY = 1; + +const int BLACK = 0; +const int BLUE = 1; + +bool ColorizeCalendar(int today, int color); +ColorizeCalendar(BLUE, SUNDAY); // 不会报错 +``` + +当枚举值需要对应到具体数值时,须在声明时显式赋值。否则不需要显式赋值,以避免重复赋值,降低维护(增加、删除成员)工作量。 + +```cpp +// 好的例子:S协议里定义的设备ID值,用于标识设备类型 +enum DeviceType { + DEV_UNKNOWN = -1, + DEV_DSMP = 0, + DEV_ISMG = 1, + DEV_WAPPORTAL = 2 +}; +``` + +程序内部使用,仅用于分类的情况,不应该进行显式的赋值。 + +```cpp +// 好的例子:程序中用来标识会话状态的枚举定义 +enum SessionState { + INIT, + CLOSED, + WAITING_FOR_RESPONSE +}; +``` + +应当尽量避免枚举值重复,如必须重复也要用已定义的枚举来修饰 + +```cpp +enum RTCPType { + RTCP_SR = 200, + RTCP_MIN_TYPE = RTCP_SR, + RTCP_RR = 201, + RTCP_SDES = 202, + RTCP_BYE = 203, + RTCP_APP = 204, + RTCP_RTPFB = 205, + RTCP_PSFB = 206, + RTCP_XR = 207, + RTCP_RSI = 208, + RTCP_PUBPORTS = 209, + RTCP_MAX_TYPE = RTCP_PUBPORTS +}; +``` + +### 规则9.1.2 不允许使用魔鬼数字 +所谓魔鬼数字即看不懂、难以理解的数字。 + +魔鬼数字并非一个非黑即白的概念,看不懂也有程度,需要自行判断。 +例如数字 12,在不同的上下文中情况是不一样的: +type = 12; 就看不懂,但 `monthsCount = yearsCount * 12`; 就能看懂。 +数字 0 有时候也是魔鬼数字,比如 `status = 0`; 并不能表达是什么状态。 + +解决途径: +对于局部使用的数字,可以增加注释说明 +对于多处使用的数字,必须定义 const 常量,并通过符号命名自注释。 + +禁止出现下列情况: +没有通过符号来解释数字含义,如` const int ZERO = 0` +符号命名限制了其取值,如 `const int XX_TIMER_INTERVAL_300MS = 300`,直接使用`XX_TIMER_INTERVAL_MS`来表示该常量是定时器的时间间隔。 + +### 规则9.1.3 常量应该保证单一职责 + +**说明**:一个常量只用来表示一个特定功能,即一个常量不能有多种用途。 + +```cpp +// 好的例子:协议A和协议B,手机号(MSISDN)的长度都是20。 +const unsigned int A_MAX_MSISDN_LEN = 20; +const unsigned int B_MAX_MSISDN_LEN = 20; + +// 或者使用不同的名字空间: +namespace Namespace1 { + const unsigned int MAX_MSISDN_LEN = 20; +} + +namespace Namespace2 { + const unsigned int MAX_MSISDN_LEN = 20; +} +``` + +### 规则9.1.4 禁止用memcpy_s、memset_s初始化非POD对象 + +**说明**:`POD`全称是`Plain Old Data`,是C++ 98标准(ISO/IEC 14882, first edition, 1998-09-01)中引入的一个概念,`POD`类型主要包括`int`, `char`, `float`,`double`,`enumeration`,`void`,指针等原始类型以及聚合类型,不能使用封装和面向对象特性(如用户定义的构造/赋值/析构函数、基类、虚函数等)。 + +由于非POD类型比如非聚合类型的class对象,可能存在虚函数,内存布局不确定,跟编译器有关,滥用内存拷贝可能会导致严重的问题。 + +即使对聚合类型的class,使用直接的内存拷贝和比较,破坏了信息隐蔽和数据保护的作用,也不提倡`memcpy_s`、`memset_s`操作。 + +对于POD类型的详细说明请参见附录。 + +### 建议9.1.2 变量使用时才声明并初始化 + +**说明**:变量在使用前未赋初值,是常见的低级编程错误。使用前才声明变量并同时初始化,非常方便地避免了此类低级错误。 + +在函数开始位置声明所有变量,后面才使用变量,作用域覆盖整个函数实现,容易导致如下问题: +* 程序难以理解和维护:变量的定义与使用分离。 +* 变量难以合理初始化:在函数开始时,经常没有足够的信息进行变量初始化,往往用某个默认的空值(比如零)来初始化,这通常是一种浪费,如果变量在被赋于有效值以前使用,还会导致错误。 + +遵循变量作用域最小化原则与就近声明原则, 使得代码更容易阅读,方便了解变量的类型和初始值。特别是,应使用初始化的方式替代声明再赋值。 + +```cpp +// 不好的例子:声明与初始化分离 +string name; // 声明时未初始化:调用缺省构造函数 +name = "zhangsan"; // 再次调用赋值操作符函数;声明与定义在不同的地方,理解相对困难 + +// 好的例子:声明与初始化一体,理解相对容易 +string name("zhangsan"); // 调用构造函数 +``` + + +## 表达式 +### 规则9.2.1 含有变量自增或自减运算的表达式中禁止再次引用该变量 +含有变量自增或自减运算的表达式中,如果再引用该变量,其结果在C++标准中未明确定义。各个编译器或者同一个编译器不同版本实现可能会不一致。 +为了更好的可移植性,不应该对标准未定义的运算次序做任何假设。 + +注意,运算次序的问题不能使用括号来解决,因为这不是优先级的问题。 + +示例: +```cpp +x = b[i] + i++; // Bad: b[i]运算跟 i++,先后顺序并不明确。 +``` +正确的写法是将自增或自减运算单独放一行: +```cpp +x = b[i] + i; +i++; // Good: 单独一行 +``` + +函数参数 +```cpp +Func(i++, i); // Bad: 传递第2个参数时,不确定自增运算有没有发生 +``` + +正确的写法 +```cpp +i++; // Good: 单独一行 +x = Func(i, i); +``` + +### 规则9.2.2 switch语句要有default分支 +大部分情况下,switch语句中要有default分支,保证在遗漏case标签处理时能够有一个缺省的处理行为。 + +特例: +如果switch条件变量是枚举类型,并且 case 分支覆盖了所有取值,则加上default分支处理有些多余。 +现代编译器都具备检查是否在switch语句中遗漏了某些枚举值的case分支的能力,会有相应的warning提示。 + +```cpp +enum Color { + RED = 0, + BLUE +}; + +// 因为switch条件变量是枚举值,这里可以不用加default处理分支 +switch (color) { + case RED: + DoRedThing(); + break; + case BLUE: + DoBlueThing(); + ... + break; +} +``` + +### 建议9.2.1 表达式的比较,应当遵循左侧倾向于变化、右侧倾向于不变的原则 +当变量与常量比较时,如果常量放左边,如 if (MAX == v) 不符合阅读习惯,而 if (MAX > v) 更是难于理解。 +应当按人的正常阅读、表达习惯,将常量放右边。写成如下方式: +```cpp +if (value == MAX) { + +} + +if (value < MAX) { + +} +``` +也有特殊情况,如:`if (MIN < value && value < MAX)` 用来描述区间时,前半段是常量在左的。 + +不用担心将 '==' 误写成 '=',因为` if (value = MAX)` 会有编译告警,其他静态检查工具也会报错。让工具去解决笔误问题,代码要符合可读性第一。 + +### 建议9.2.2 使用括号明确操作符的优先级 +使用括号明确操作符的优先级,防止因默认的优先级与设计思想不符而导致程序出错;同时使得代码更为清晰可读,然而过多的括号会分散代码使其降低了可读性。下面是如何使用括号的建议。 + +- 二元及以上操作符, 如果涉及多种操作符,则应该使用括号 +```cpp +x = a + b + c; /* 操作符相同,可以不加括号 */ +x = Foo(a + b, c); /* 逗号两边的表达式,不需要括号 */ +x = 1 << (2 + 3); /* 操作符不同,需要括号 */ +x = a + (b / 5); /* 操作符不同,需要括号 */ +x = (a == b) ? a : (a – b); /* 操作符不同,需要括号 */ +``` + + +## 类型转换 + +避免使用类型分支来定制行为:类型分支来定制行为容易出错,是企图用C++编写C代码的明显标志。这是一种很不灵活的技术,要添加新类型时,如果忘记修改所有分支,编译器也不会告知。使用模板和虚函数,让类型自己而不是调用它们的代码来决定行为。 + +建议避免类型转换,我们在代码的类型设计上应该考虑到每种数据的数据类型是什么,而不是应该过度使用类型转换来解决问题。在设计某个基本类型的时候,请考虑: +- 是无符号还是有符号的 +- 是适合float还是double +- 是使用int8,int16,int32还是int64,确定整形的长度 + +但是我们无法禁止使用类型转换,因为C++语言是一门面向机器编程的语言,涉及到指针地址,并且我们会与各种第三方或者底层API交互,他们的类型设计不一定是合理的,在这个适配的过程中很容易出现类型转换。 + +例外:在调用某个函数的时候,如果我们不想处理函数结果,首先要考虑这个是否是你的最好的选择。如果确实不想处理函数的返回值,那么可以使用(void)转换来解决。 + +### 规则9.3.1 如果确定要使用类型转换,请使用由C++提供的类型转换,而不是C风格的类型转换 + +**说明**: + +C++提供的类型转换操作比C风格更有针对性,更易读,也更加安全,C++提供的转换有: +- 类型转换: +1. `dynamic_cast`:主要用于继承体系下行转换,`dynamic_cast`具有类型检查的功能,请做好基类和派生类的设计,避免使用dynamic_cast来进行转换。 +2. `static_cast`:和C风格转换相似可做值的强制转换,或上行转换(把派生类的指针或引用转换成基类的指针或引用)。该转换经常用于消除多重继承带来的类型歧义,是相对安全的。如果是纯粹的算数转换,那么请使用后面的大括号转换方式。 +3. `reinterpret_cast`:用于转换不相关的类型。`reinterpret_cast`强制编译器将某个类型对象的内存重新解释成另一种类型,这是一种不安全的转换,建议尽可能少用`reinterpret_cast`。 +4. `const_cast`:用于移除对象的`const`属性,使对象变得可修改,这样会破坏数据的不变性,建议尽可能少用。 + +- 算数转换: (C++11开始支持) + 对于那种算数转换,并且类型信息没有丢失的,比如float到double, int32到int64的转换,推荐使用大括号的初始方式。 +```cpp + double d{ someFloat }; + int64_t i{ someInt32 }; +``` + +### 建议9.3.1 避免使用`dynamic_cast` +1. `dynamic_cast`依赖于C++的RTTI, 让程序员在运行时识别C++类对象的类型。 +2. `dynamic_cast`的出现一般说明我们的基类和派生类设计出现了问题,派生类破坏了基类的契约,不得不通过`dynamic_cast`转换到子类进行特殊处理,这个时候更希望来改善类的设计,而不是通过`dynamic_cast`来解决问题。 + +### 建议9.3.2 避免使用`reinterpret_cast` + +**说明**:`reinterpret_cast`用于转换不相关类型。尝试用`reinterpret_cast`将一种类型强制转换另一种类型,这破坏了类型的安全性与可靠性,是一种不安全的转换。不同类型之间尽量避免转换。 + +### 建议9.3.3 避免使用`const_cast` + +**说明**:`const_cast`用于移除对象的`const`和`volatile`性质。 + +使用const_cast转换后的指针或者引用来修改const对象,行为是未定义的。 + +```cpp +// 不好的例子 +const int i = 1024; +int* p = const_cast(&i); +*p = 2048; // 未定义行为 +``` + +```cpp +// 不好的例子 +class Foo { +public: + Foo() : i(3) {} + + void Fun(int v) + { + i = v; + } + +private: + int i; +}; + +int main(void) +{ + const Foo f; + Foo* p = const_cast(&f); + p->Fun(8); // 未定义行为 +} + +``` + + +## 资源分配和释放 + +### 规则9.4.1 单个对象释放使用delete,数组对象释放使用delete [] +说明:单个对象删除使用delete, 数组对象删除使用delete [],原因: + +- 调用new所包含的动作:从系统中申请一块内存,并调用此类型的构造函数。 +- 调用new[n]所包含的动作:申请可容纳n个对象的内存,并且对每一个对象调用其构造函数。 +- 调用delete所包含的动作:先调用相应的析构函数,再将内存归还系统。 +- 调用delete[]所包含的动作:对每一个对象调用析构函数,再释放所有内存 + +如果new和delete的格式不匹配,结果是未知的。对于非class类型, new和delete不会调用构造与析构函数。 + +错误写法: +```cpp +const int MAX_ARRAY_SIZE = 100; +int* numberArray = new int[MAX_ARRAY_SIZE]; +... +delete numberArray; +numberArray = nullptr; +``` + +正确写法: +```cpp +const int MAX_ARRAY_SIZE = 100; +int* numberArray = new int[MAX_ARRAY_SIZE]; +... +delete[] numberArray; +numberArray = nullptr; +``` + +### 建议9.4.1 使用 RAII 特性来帮助追踪动态分配 + +说明:RAII是“资源获取就是初始化”的缩语(Resource Acquisition Is Initialization),是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。 + +RAII 的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。这种做法有两大好处: +- 我们不需要显式地释放资源。 +- 对象所需的资源在其生命期内始终保持有效。这样,就不必检查资源有效性的问题,可以简化逻辑、提高效率。 + + +示例:使用RAII不需要显式地释放互斥资源。 + +```cpp +class LockGuard { +public: + LockGuard(const LockType& lockType): lock_(lockType) + { + lock_.Aquire(); + } + + ~LockGuard() + { + lock_.Relase(); + } + +private: + LockType lock_; +}; + + +bool Update() +{ + LockGuard lockGuard(mutex); + if (...) { + return false; + } else { + // 操作数据 + } + + return true; +} +``` + +## 标准库 + +STL标准模板库在不同产品使用程度不同,这里列出一些基本规则和建议,供各团队参考。 + +### 规则9.5.1 不要保存std::string的c_str()返回的指针 + +说明:在C++标准中并未规定string::c_str()指针持久有效,因此特定STL实现完全可以在调用string::c_str()时返回一个临时存储区并很快释放。所以为了保证程序的可移植性,不要保存string::c_str()的结果,而是在每次需要时直接调用。 + +示例: + +```cpp +void Fun1() +{ + std::string name = "demo"; + const char* text = name.c_str(); // 表达式结束以后,name的生命周期还在,指针有效 + + // 如果中间调用了string的非const成员函数,导致string被修改,比如operator[], begin()等 + // 可能会导致text的内容不可用,或者不是原来的字符串 + name = "test"; + name[1] = '2'; + + // 后续使用text指针,其字符串内容不再是"demo" +} + +void Fun2() +{ + std::string name = "demo"; + std::string test = "test"; + const char* text = (name + test).c_str(); // 表达式结束以后,+号产生的临时对象被销毁,指针无效 + + // 后续使用text指针,其已不再指向合法内存空间 +} +``` +例外:在少数对性能要求非常高的代码中,为了适配已有的只接受const char*类型入参的函数,可以临时保存string::c_str()返回的指针。但是必须严格保证string对象的生命周期长于所保存指针的生命周期,并且保证在所保存指针的生命周期内,string对象不会被修改。 + + +### 建议9.5.1 使用std::string代替char* + +说明:使用string代替`char*`有很多优势,比如: +1. 不用考虑结尾的’\0’; +2. 可以直接使用+, =, ==等运算符以及其它字符串操作函数; +3. 不需要考虑内存分配操作,避免了显式的new/delete,以及由此导致的错误; + +需要注意的是某些stl实现中string是基于写时复制策略的,这会带来2个问题,一是某些版本的写时复制策略没有实现线程安全,在多线程环境下会引起程序崩溃;二是当与动态链接库相互传递基于写时复制策略的string时,由于引用计数在动态链接库被卸载时无法减少可能导致悬挂指针。因此,慎重选择一个可靠的stl实现对于保证程序稳定是很重要的。 + +例外: +当调用系统或者其它第三方库的API时,针对已经定义好的接口,只能使用`char*`。但是在调用接口之前都可以使用string,在调用接口时使用string::c_str()获得字符指针。 +当在栈上分配字符数组当作缓冲区使用时,可以直接定义字符数组,不要使用string,也没有必要使用类似`vector`等容器。 + +### 规则9.5.2 禁止使用auto_ptr +说明:在stl库中的std::auto_ptr具有一个隐式的所有权转移行为,如下代码: +```cpp +auto_ptr p1(new T); +auto_ptr p2 = p1; +``` +当执行完第2行语句后,p1已经不再指向第1行中分配的对象,而是变为nullptr。正因为如此,auto_ptr不能被置于各种标准容器中。 +转移所有权的行为通常不是期望的结果。对于必须转移所有权的场景,也不应该使用隐式转移的方式。这往往需要程序员对使用auto_ptr的代码保持额外的谨慎,否则出现对空指针的访问。 +使用auto_ptr常见的有两种场景,一是作为智能指针传递到产生auto_ptr的函数外部,二是使用auto_ptr作为RAII管理类,在超出auto_ptr的生命周期时自动释放资源。 +对于第1种场景,可以使用std::shared_ptr来代替。 +对于第2种场景,可以使用C++11标准中的std::unique_ptr来代替。其中std::unique_ptr是std::auto_ptr的代替品,支持显式的所有权转移。 + +例外: +在C++11标准得到普遍使用之前,在一定需要对所有权进行转移的场景下,可以使用std::auto_ptr,但是建议对std::auto_ptr进行封装,并禁用封装类的拷贝构造函数和赋值运算符,以使该封装类无法用于标准容器。 + + +### 建议9.5.2 使用新的标准头文件 + +说明: +使用C++的标准头文件时,请使用``这样的,而不是``这种的。 + +## const的用法 +在声明的变量或参数前加上关键字 const 用于指明变量值不可被篡改 (如 `const int foo` ). 为类中的函数加上 const 限定符表明该函数不会修改类成员变量的状态 (如 `class Foo { int Bar(char c) const; };`)。 const 变量, 数据成员, 函数和参数为编译时类型检测增加了一层保障, 便于尽早发现错误。因此, 我们强烈建议在任何可能的情况下使用 const。 +有时候,使用C++11的constexpr来定义真正的常量可能更好。 + +### 规则9.6.1 对于指针和引用类型的形参,如果是不需要修改的,请使用const +不变的值更易于理解/跟踪和分析,把const作为默认选项,在编译时会对其进行检查,使代码更牢固/更安全。 +```cpp +class Foo; + +void PrintFoo(const Foo& foo); +``` + +### 规则9.6.2 对于不会修改成员变量的成员函数请使用const修饰 +尽可能将成员函数声明为 const。 访问函数应该总是 const。只要不修改数据成员的成员函数,都声明为const。 +对于虚函数,应当从设计意图上考虑继承链上的所有类是否需要在此虚函数中修改数据成员,而不是仅关注单个类的实现。 +```cpp +class Foo { +public: + + // ... + + int PrintValue() const // const修饰成员函数,不会修改成员变量 + { + std::cout << value_ << std::endl; + } + + int GetValue() const // const修饰成员函数,不会修改成员变量 + { + return value_; + } + +private: + int value_; +}; +``` + +### 建议9.6.1 初始化后不会再修改的成员变量定义为const + +```cpp +class Foo { +public: + Foo(int length) : dataLength_(length) {} +private: + const int dataLength_; +}; +``` + +## 异常 + +### 建议9.7.1 C++11中,如果函数不会抛出异常,声明为`noexcept` +**理由** +1. 如果函数不会抛出异常,声明为`noexcept`可以让编译器最大程度的优化函数,如减少执行路径,提高错误退出的效率。 +2. `vector`等STL容器,为了保证接口的健壮性,如果保存元素的`move运算符`没有声明为`noexcept`,则在容器扩张搬移元素时不会使用`move机制`,而使用`copy机制`,带来性能损失的风险。如果一个函数不能抛出异常,或者一个程序并没有截获某个函数所抛出的异常并进行处理,那么这个函数可以用新的`noexcept`关键字对其进行修饰,表示这个函数不会抛出异常或者抛出的异常不会被截获并处理。例如: + +```cpp +extern "C" double sqrt(double) noexcept; // 永远不会抛出异常 + +// 即使可能抛出异常,也可以使用 noexcept +// 这里不准备处理内存耗尽的异常,简单地将函数声明为noexcept +std::vector MyComputation(const std::vector& v) noexcept +{ + std::vector res = v; // 可能会抛出异常 + // do something + return res; +} +``` + +**示例** + +```cpp +RetType Function(Type params) noexcept; // 最大的优化 +RetType Function(Type params); // 更少的优化 + +// std::vector 的 move 操作需要声明 noexcept +class Foo1 { +public: + Foo1(Foo1&& other); // no noexcept +}; + +std::vector a1; +a1.push_back(Foo1()); +a1.push_back(Foo1()); // 触发容器扩张,搬移已有元素时调用copy constructor + +class Foo2 { +public: + Foo2(Foo2&& other) noexcept; +}; + +std::vector a2; +a2.push_back(Foo2()); +a2.push_back(Foo2()); // 触发容器扩张,搬移已有元素时调用move constructor +``` + +**注意** +默认构造函数、析构函数、`swap`函数,`move操作符`都不应该抛出异常。 + +## 模板 + +模板能够实现非常灵活简洁的类型安全的接口,实现类型不同但是行为相同的代码复用。 + +模板编程的缺点: + +1. 模板编程所使用的技巧对于使用c++不是很熟练的人是比较晦涩难懂的。在复杂的地方使用模板的代码让人更不容易读懂,并且debug 和维护起来都很麻烦。 +2. 模板编程经常会导致编译出错的信息非常不友好: 在代码出错的时候, 即使这个接口非常的简单, 模板内部复杂的实现细节也会在出错信息显示. 导致这个编译出错信息看起来非常难以理解。 +3. 模板如果使用不当,会导致运行时代码过度膨胀。 +4. 模板代码难以修改和重构。模板的代码会在很多上下文里面扩展开来, 所以很难确认重构对所有的这些展开的代码有用。 + +所以, 建议__模板编程最好只用在少量的基础组件,基础数据结构上面__。并且使用模板编程的时候尽可能把__复杂度最小化__,尽量__不要让模板对外暴露__。最好只在实现里面使用模板, 然后给用户暴露的接口里面并不使用模板, 这样能提高你的接口的可读性。 并且你应该在这些使用模板的代码上写尽可能详细的注释。 + + +## 宏 +在C++语言中,我们强烈建议尽可能少使用复杂的宏 +- 对于常量定义,请按照前面章节所述,使用const或者枚举; +- 对于宏函数,尽可能简单,并且遵循下面的原则,并且优先使用内联函数,模板函数等进行替换。 + +```cpp +// 不推荐使用宏函数 +#define SQUARE(a, b) ((a) * (b)) + +// 请使用模板函数,内联函数等来替换。 +template T Square(T a, T b) { return a * b; } +``` + +如果需要使用宏,请参考C语言规范的相关章节。 +**例外**:一些通用且成熟的应用,如:对 new, delete 的封装处理,可以保留对宏的使用。 + +# 10 现代C++特性 + +随着 ISO 在2011年发布 C++11 语言标准,以及2017年3月发布 C++17 ,现代C++(C++11/14/17等)增加了大量提高编程效率、代码质量的新语言特性和标准库。 +本章节描述了一些可以帮助团队更有效率的使用现代C++,规避语言陷阱的指导意见。 + +## 代码简洁性和安全性提升 +### 建议10.1.1 合理使用`auto` +**理由** + +* `auto`可以避免编写冗长、重复的类型名,也可以保证定义变量时初始化。 +* `auto`类型推导规则复杂,需要仔细理解。 +* 如果能够使代码更清晰,继续使用明确的类型,且只在局部变量使用`auto`。 + +**示例** + +```cpp +// 避免冗长的类型名 +std::map::iterator iter = m.find(val); +auto iter = m.find(val); + +// 避免重复类型名 +class Foo {...}; +Foo* p = new Foo; +auto p = new Foo; + +// 保证初始化 +int x; // 编译正确,没有初始化 +auto x; // 编译失败,必须初始化 +``` + +auto 的类型推导可能导致困惑: + +```cpp +auto a = 3; // int +const auto ca = a; // const int +const auto& ra = a; // const int& +auto aa = ca; // int, 忽略 const 和 reference +auto ila1 = { 10 }; // std::initializer_list +auto ila2{ 10 }; // std::initializer_list + +auto&& ura1 = x; // int& +auto&& ura2 = ca; // const int& +auto&& ura3 = 10; // int&& + +const int b[10]; +auto arr1 = b; // const int* +auto& arr2 = b; // const int(&)[10] +``` + +如果没有注意 `auto` 类型推导时忽略引用,可能引入难以发现的性能问题: + +```cpp +std::vector v; +auto s1 = v[0]; // auto 推导为 std::string,拷贝 v[0] +``` + +如果使用`auto`定义接口,如头文件中的常量,可能因为开发人员修改了值,而导致类型发生变化。 + +### 规则10.1.1 在重写虚函数时请使用`override`或`final`关键字 +**理由** +`override`和`final`关键字都能保证函数是虚函数,且重写了基类的虚函数。如果子类函数与基类函数原型不一致,则产生编译告警。`final`还保证虚函数不会再被子类重写。 + +使用`override`或`final`关键字后,如果修改了基类虚函数原型,但忘记修改子类重写的虚函数,在编译期就可以发现。也可以避免有多个子类时,重写虚函数的修改遗漏。 + +**示例** + +```cpp +class Base { +public: + virtual void Foo(); + virtual void Foo(int var); + void Bar(); +}; + +class Derived : public Base { +public: + void Foo() const override; // 编译失败: Derived::Foo 和 Base::Foo 原型不一致,不是重写 + void Foo() override; // 正确: Derived::Foo 重写 Base::Foo + void Foo(int var) final; // 正确: Derived::Foo(int) 重写 Base::Foo(int),且Derived的派生类不能再重写此函数 + void Bar() override; // 编译失败: Base::Bar 不是虚函数 +}; +``` + +**总结** +1. 基类首次定义虚函数,使用`virtual`关键字 +2. 子类重写基类虚函数(包括析构函数),使用`override`或`final`关键字(但不要两者一起使用),并且不使用`virtual`关键字 +3. 非虚函数,`virtual`、`override`和`final`都不使用 + +### 规则10.1.2 使用`delete`关键字删除函数 +**理由** +相比于将类成员函数声明为`private`但不实现,`delete`关键字更明确,且适用范围更广。 + +**示例** + +```cpp +class Foo { +private: + // 只看头文件不知道拷贝构造是否被删除 + Foo(const Foo&); +}; + +class Foo { +public: + // 明确删除拷贝赋值函数 + Foo& operator=(const Foo&) = delete; +}; +``` + +`delete`关键字还支持删除非成员函数 + +```cpp +template +void Process(T value); + +template<> +void Process(void) = delete; +``` + +### 规则10.1.3 使用`nullptr`,而不是`NULL`或`0` +**理由** +长期以来,C++没有一个代表空指针的关键字,这是一件很尴尬的事: + +```cpp +#define NULL ((void *)0) + +char* str = NULL; // 错误: void* 不能自动转换为 char* + +void(C::*pmf)() = &C::Func; +if (pmf == NULL) {} // 错误: void* 不能自动转换为指向成员函数的指针 +``` + +如果把`NULL`被定义为`0`或`0L`。可以解决上面的问题。 + +或者在需要空指针的地方直接使用`0`。但这引入另一个问题,代码不清晰,特别是使用`auto`自动推导: + +```cpp +auto result = Find(id); +if (result == 0) { // Find() 返回的是 指针 还是 整数? + // do something +} +``` + +`0`字面上是`int`类型(`0L`是`long`),所以`NULL`和`0`都不是指针类型。 +当重载指针和整数类型的函数时,传递`NULL`或`0`都调用到整数类型重载的函数: + +```cpp +void F(int); +void F(int*); + +F(0); // 调用 F(int),而非 F(int*) +F(NULL); // 调用 F(int),而非 F(int*) +``` + +另外,`sizeof(NULL) == sizeof(void*)`并不一定总是成立的,这也是一个潜在的风险。 + +总结: 直接使用`0`或`0L`,代码不清晰,且无法做到类型安全;使用`NULL`无法做到类型安全。这些都是潜在的风险。 + +`nullptr`的优势不仅仅是在字面上代表了空指针,使代码清晰,而且它不再是一个整数类型。 + +`nullptr`是`std::nullptr_t`类型,而`std::nullptr_t`可以隐式的转换为所有的原始指针类型,这使得`nullptr`可以表现成指向任意类型的空指针。 + +```cpp +void F(int); +void F(int*); +F(nullptr); // 调用 F(int*) + +auto result = Find(id); +if (result == nullptr) { // Find() 返回的是 指针 + // do something +} +``` + +### 规则10.1.4 使用`using`而非`typedef` +在`C++11`之前,可以通过`typedef`定义类型的别名。没人愿意多次重复`std::map>`这样的代码。 + +```cpp +typedef std::map> SomeType; +``` + +类型的别名实际是对类型的封装。而通过封装,可以让代码更清晰,同时在很大程度上避免类型变化带来的散弹式修改。 +在`C++11`之后,提供`using`,实现`声明别名(alias declarations)`: + +```cpp +using SomeType = std::map>; +``` + +对比两者的格式: + +```cpp +typedef Type Alias; // Type 在前,还是 Alias 在前 +using Alias = Type; // 符合'赋值'的用法,容易理解,不易出错 +``` + +如果觉得这点还不足以切换到`using`,我们接着看看`模板别名(alias template)`: + +```cpp +// 定义模板的别名,一行代码 +template +using MyAllocatorVector = std::vector>; + +MyAllocatorVector data; // 使用 using 定义的别名 + +template +class MyClass { +private: + MyAllocatorVector data_; // 模板类中使用 using 定义的别名 +}; +``` + +而`typedef`不支持带模板参数的别名,只能"曲线救国": + +```cpp +// 通过模板包装 typedef,需要实现一个模板类 +template +struct MyAllocatorVector { + typedef std::vector> type; +}; + +MyAllocatorVector::type data; // 使用 typedef 定义的别名,多写 ::type + +template +class MyClass { +private: + typename MyAllocatorVector::type data_; // 模板类中使用,除了 ::type,还需要加上 typename +}; +``` + +### 规则10.1.5 禁止使用std::move操作const对象 +从字面上看,`std::move`的意思是要移动一个对象。而const对象是不允许修改的,自然也无法移动。因此用`std::move`操作const对象会给代码阅读者带来困惑。 +在实际功能上,`std::move`会把对象转换成右值引用类型;对于const对象,会将其转换成const的右值引用。由于极少有类型会定义以const右值引用为参数的移动构造函数和移动赋值操作符,因此代码实际功能往往退化成了对象拷贝而不是对象移动,带来了性能上的损失。 + +**错误示例:** +```cpp +std::string g_string; +std::vector g_stringList; + +void func() +{ + const std::string myString = "String content"; + g_string = std::move(myString); // bad:并没有移动myString,而是进行了复制 + const std::string anotherString = "Another string content"; + g_stringList.push_back(std::move(anotherString)); // bad:并没有移动anotherString,而是进行了复制 +} +``` + +## 智能指针 +### 规则10.2.1 优先使用智能指针而不是原始指针管理资源 +**理由** +避免资源泄露。 + +**示例** + +```cpp +void Use(int i) +{ + auto p = new int {7}; // 不好: 通过 new 初始化局部指针 + auto q = std::make_unique(9); // 好: 保证释放内存 + if (i > 0) { + return; // 可能 return,导致内存泄露 + } + delete p; // 太晚了 +} +``` + +**例外** +在性能敏感、兼容性等场景可以使用原始指针。 + +### 规则10.2.2 优先使用`unique_ptr`而不是`shared_ptr` +**理由** +1. `shared_ptr`引用计数的原子操作存在可测量的开销,大量使用`shared_ptr`影响性能。 +2. 共享所有权在某些情况(如循环依赖)可能导致对象永远得不到释放。 +3. 相比于谨慎设计所有权,共享所有权是一种诱人的替代方案,但它可能使系统变得混乱。 + +### 规则10.2.3 使用`std::make_unique`而不是`new`创建`unique_ptr` +**理由** +1. `make_unique`提供了更简洁的创建方式 +2. 保证了复杂表达式的异常安全 + +**示例** + +```cpp +// 不好:两次出现 MyClass,重复导致不一致风险 +std::unique_ptr ptr(new MyClass(0, 1)); +// 好:只出现一次 MyClass,不存在不一致的可能 +auto ptr = std::make_unique(0, 1); +``` + +重复出现类型可能导致非常严重的问题,且很难发现: + +```cpp +// 编译正确,但new和delete不配套 +std::unique_ptr ptr(new uint8_t[10]); +std::unique_ptr ptr(new uint8_t); +// 非异常安全: 编译器可能按如下顺序计算参数: +// 1. 分配 Foo 的内存, +// 2. 构造 Foo, +// 3. 调用 Bar, +// 4. 构造 unique_ptr. +// 如果 Bar 抛出异常, Foo 不会被销毁,产生内存泄露。 +F(unique_ptr(new Foo()), Bar()); + +// 异常安全: 调用函数不会被打断. +F(make_unique(), Bar()); +``` + +**例外** +`std::make_unique`不支持自定义`deleter`。 +在需要自定义`deleter`的场景,建议在自己的命名空间实现定制版本的`make_unique`。 +使用`new`创建自定义`deleter`的`unique_ptr`是最后的选择。 + +### 规则10.2.4 使用`std::make_shared`而不是`new`创建`shared_ptr` +**理由** +使用`std::make_shared`除了类似`std::make_unique`一致性等原因外,还有性能的因素。 +`std::shared_ptr`管理两个实体: +* 控制块(存储引用计数,`deleter`等) +* 管理对象 + +`std::make_shared`创建`std::shared_ptr`,会一次性在堆上分配足够容纳控制块和管理对象的内存。而使用`std::shared_ptr(new MyClass)`创建`std::shared_ptr`,除了`new MyClass`会触发一次堆分配外,`std::shard_ptr`的构造函数还会触发第二次堆分配,产生额外的开销。 + +**例外** +类似`std::make_unique`,`std::make_shared`不支持定制`deleter` + +## Lambda +### 建议10.3.1 当函数不能工作时选择使用`lambda`(捕获局部变量,或编写局部函数) +**理由** +函数无法捕获局部变量或在局部范围内声明;如果需要这些东西,尽可能选择`lambda`,而不是手写的`functor`。 +另一方面,`lambda`和`functor`不会重载;如果需要重载,则使用函数。 +如果`lambda`和函数都可以的场景,则优先使用函数;尽可能使用最简单的工具。 + +**示例** + +```cpp +// 编写一个只接受 int 或 string 的函数 +// -- 重载是自然的选择 +void F(int); +void F(const string&); + +// 需要捕获局部状态,或出现在语句或表达式范围 +// -- lambda 是自然的选择 +vector v = LotsOfWork(); +for (int taskNum = 0; taskNum < max; ++taskNum) { + pool.Run([=, &v] {...}); +} +pool.Join(); +``` + +### 规则10.3.1 非局部范围使用`lambdas`,避免使用按引用捕获 +**理由** +非局部范围使用`lambdas`包括返回值,存储在堆上,或者传递给其它线程。局部的指针和引用不应该在它们的范围外存在。`lambdas`按引用捕获就是把局部对象的引用存储起来。如果这会导致超过局部变量生命周期的引用存在,则不应该按引用捕获。 + +**示例** + +```cpp +// 不好 +void Foo() +{ + int local = 42; + // 按引用捕获 local. + // 当函数返回后,local 不再存在, + // 因此 Process() 的行为未定义! + threadPool.QueueWork([&]{ Process(local); }); +} + +// 好 +void Foo() +{ + int local = 42; + // 按值捕获 local。 + // 因为拷贝,Process() 调用过程中,local 总是有效的 + threadPool.QueueWork([=]{ Process(local); }); +} +``` + +### 建议10.3.2 如果捕获`this`,则显式捕获所有变量 +**理由** +在成员函数中的`[=]`看起来是按值捕获。但因为是隐式的按值获取了`this`指针,并能够操作所有成员变量,数据成员实际是按引用捕获的,一般情况下建议避免。如果的确需要这样做,明确写出对`this`的捕获。 + +**示例** + +```cpp +class MyClass { +public: + void Foo() + { + int i = 0; + + auto Lambda = [=]() { Use(i, data_); }; // 不好: 看起来像是拷贝/按值捕获,成员变量实际上是按引用捕获 + + data_ = 42; + Lambda(); // 调用 use(42); + data_ = 43; + Lambda(); // 调用 use(43); + + auto Lambda2 = [i, this]() { Use(i, data_); }; // 好,显式指定按值捕获,最明确,最少的混淆 + } + +private: + int data_ = 0; +}; +``` + +### 建议10.3.3 避免使用默认捕获模式 +**理由** +lambda表达式提供了两种默认捕获模式:按引用(&)和按值(=)。 +默认按引用捕获会隐式的捕获所有局部变量的引用,容易导致访问悬空引用。相比之下,显式的写出需要捕获的变量可以更容易的检查对象生命周期,减小犯错可能。 +默认按值捕获会隐式的捕获this指针,且难以看出lambda函数所依赖的变量是哪些。如果存在静态变量,还会让阅读者误以为lambda拷贝了一份静态变量。 +因此,通常应当明确写出lambda需要捕获的变量,而不是使用默认捕获模式。 + +**错误示例** +```cpp +auto func() +{ + int addend = 5; + static int baseValue = 3; + + return [=]() { // 实际上只复制了addend + ++baseValue; // 修改会影响静态变量的值 + return baseValue + addend; + }; +} +``` + +**正确示例** +```cpp +auto func() +{ + int addend = 5; + static int baseValue = 3; + + return [addend, baseValue = baseValue]() mutable { // 使用C++14的捕获初始化拷贝一份变量 + ++baseValue; // 修改自己的拷贝,不会影响静态变量的值 + return baseValue + addend; + }; +} +``` + +参考:《Effective Modern C++》:Item 31: Avoid default capture modes. + +## 接口 +### 建议10.4.1 不涉及所有权的场景,使用`T*`或`T&`作为参数,而不是智能指针 +**理由** +1. 只在需要明确所有权机制时,才通过智能指针转移或共享所有权. +2. 通过智能指针传递,限制了函数调用者必须使用智能指针(如调用者希望传递`this`)。 +3. 传递共享所有权的智能指针存在运行时的开销。 + +**示例** + +```cpp +// 接受任何 int* +void F(int*); + +// 只能接受希望转移所有权的 int +void G(unique_ptr); + +// 只能接受希望共享所有权的 int +void G(shared_ptr); + +// 不改变所有权,但需要特定所有权的调用者 +void H(const unique_ptr&); + +// 接受任何 int +void H(int&); + +// 不好 +void F(shared_ptr& w) +{ + // ... + Use(*w); // 只使用 w -- 完全不涉及生命周期管理 + // ... +}; +``` + diff --git a/docs-en/bundles/Readme-EN.md b/docs-en/bundles/Readme-EN.md index c049d8411fd27604c94036fba42621080cc7de11..8b17093e4ca85e6ca66d92982cacdf2f2f5adb56 100755 --- a/docs-en/bundles/Readme-EN.md +++ b/docs-en/bundles/Readme-EN.md @@ -1,15 +1,15 @@ # Bundle Development -- [Development Guidelines](development-guidelines.md) - - [Overview](overview.md) - - [Preparations](preparations.md) - - [Bundle Development](bundle-development.md) - - [Development Specifications](development-specifications.md) - - [Overview](overview-0.md) + - [Overview](overview.md) - [Bundle Composition](bundle-composition.md) - [Bundle Management](bundle-management.md) - [Bundle Version](bundle-version.md) - [Distribution](distribution.md) - [Environment Variables](environment-variables.md) +- [Development Guidelines](development-guidelines.md) + - [Overview](overview-0.md) + - [Preparations](preparations.md) + - [Bundle Development](bundle-development.md) + diff --git a/docs-en/bundles/bundle-management.md b/docs-en/bundles/bundle-management.md index 70cece9879ec12b11bc64355e64c3b095646e609..2e1ec4d48838a792af24060c94a3c7a95001d5ac 100755 --- a/docs-en/bundles/bundle-management.md +++ b/docs-en/bundles/bundle-management.md @@ -214,7 +214,7 @@ You can use the hpm-cli tool to manage the lifecycle of a bundle. The following

hpm gen-keys

-

Generates a public-private key pair and configures the public key on the hpm server to enable hpm-cli.

+

Generates a public-private key pair and configures the public key on the hpm server, achieving password-free hpm-cli login for bundle publishing.

Generating third-party open source notice

diff --git a/docs-en/bundles/development-guidelines.md b/docs-en/bundles/development-guidelines.md index c93e0d648cfaa6b0c3876a6d9086584ddaad4a0f..382f907feffa6179e7cc056c016d3b26e430a612 100755 --- a/docs-en/bundles/development-guidelines.md +++ b/docs-en/bundles/development-guidelines.md @@ -1,6 +1,6 @@ # Development Guidelines -- **[Overview](overview.md)** +- **[Overview](overview-0.md)** - **[Preparations](preparations.md)** diff --git a/docs-en/bundles/development-specifications.md b/docs-en/bundles/development-specifications.md index af2eec9dbddd5f2d2665428c51bc65ee6c1a9f07..65c4502c9e05754a2d4a67e2122f2f00ad56cdc5 100755 --- a/docs-en/bundles/development-specifications.md +++ b/docs-en/bundles/development-specifications.md @@ -1,6 +1,6 @@ # Development Specifications -- **[Overview](overview-0.md)** +- **[Overview](overview.md)** - **[Bundle Composition](bundle-composition.md)** diff --git a/docs-en/bundles/overview-0.md b/docs-en/bundles/overview-0.md index da961065816973053161acb949ae70ab185ae3fc..30513906bf6da223b799233c090b486c43d2c1df 100755 --- a/docs-en/bundles/overview-0.md +++ b/docs-en/bundles/overview-0.md @@ -1,34 +1,54 @@ -# Overview - -This document describes the basic concepts of a bundle and how to define it in compliance with specifications. - -## Definition - -OpenHarmony software is developed on a per-bundle basis. In terms of the operating system, all software running on OpenHarmony are bundles. Generally, bundles are classified into the following types based on their application scopes: - -- Board-level bundles: device hardware-specific bundles, such as **board**, **arch**, and **mcu** -- System-level bundles: a set of bundles with independent features, such as the kernel, file system, and framework -- Application-level bundles: applications that provide services to users, such as **wifi\_iot** and **ip\_camera** - -Bundles are designed for the reuse purpose. Any reusable modules can be defined as bundles. They are classified into the following types based on their forms: - -- Source code -- Binary system -- Code snippet -- Distribution - -## Bundle Division Principles - -In principle, bundles should be grouped at a fine-grained granularity as much as possible to achieve maximum reuse. The following factors are taken into account regarding bundle division: - -- Independence: Bundles provide relatively independent features and can be independently compiled. Each of them is capable of providing its own APIs and services for external systems. -- Coupling: If a bundle must depend on another bundle to provide services, they can be coupled to one bundle. -- Correlation: If a group of bundles jointly implement a feature, and if other bundles never depend on them, the group of bundles can be combined into one bundle. - -## Bundle Dependency - -A bundle dependency can be mandatory or optional. - -- Mandatory dependency: If bundle A must depend on bundle B to implement a feature, that is, the APIs or services specific to bundle B must be invoked, then bundle B is defined as the mandatory dependency of bundle A. -- Optional dependency: If either bundle C or bundle D is required for bundle A to implement a feature, and if bundle C and bundle D are interchangeable, then bundle C and bundle D are defined as optional dependencies of bundle A. +# Overview + +This document describes how to develop OpenHarmony bundles and distributions, and how to create, develop, and build code, as well as burn and debug devices by using a command line tool. + +- A bundle usually maps onto a code repository, which is a code archive with the **bundle.json**, **README**, and **LICENSE** files. +- A distribution consists of multiple bundles. Each distribution integrates various bundles of a comprehensive system, such as the driver, kernel, framework, and applications. These bundles can be used for device burning. + +**Table 1** Differences between a bundle and a distribution + + + + + + + + + + + + + + + + + + + + + + + + +

Aspect

+

Bundle

+

Distribution

+

Application scenario

+

Feature-oriented

+

System-oriented

+

Content

+

Codes or a binary library for implementing features

+

List of dependent bundles as well as their compiling and building scripts

+

Integrity

+

A part of the operating system

+

An entire operating system

+

Compilation result

+

Bundles

+

System image

+
+ +**Figure 1** Composition of bundles and distributions + + +![](figures/en-us_image_0000001054663940.png) diff --git a/docs-en/bundles/overview.md b/docs-en/bundles/overview.md index 30513906bf6da223b799233c090b486c43d2c1df..da961065816973053161acb949ae70ab185ae3fc 100755 --- a/docs-en/bundles/overview.md +++ b/docs-en/bundles/overview.md @@ -1,54 +1,34 @@ -# Overview - -This document describes how to develop OpenHarmony bundles and distributions, and how to create, develop, and build code, as well as burn and debug devices by using a command line tool. - -- A bundle usually maps onto a code repository, which is a code archive with the **bundle.json**, **README**, and **LICENSE** files. -- A distribution consists of multiple bundles. Each distribution integrates various bundles of a comprehensive system, such as the driver, kernel, framework, and applications. These bundles can be used for device burning. - -**Table 1** Differences between a bundle and a distribution - - - - - - - - - - - - - - - - - - - - - - - - -

Aspect

-

Bundle

-

Distribution

-

Application scenario

-

Feature-oriented

-

System-oriented

-

Content

-

Codes or a binary library for implementing features

-

List of dependent bundles as well as their compiling and building scripts

-

Integrity

-

A part of the operating system

-

An entire operating system

-

Compilation result

-

Bundles

-

System image

-
- -**Figure 1** Composition of bundles and distributions - - -![](figures/en-us_image_0000001054663940.png) +# Overview + +This document describes the basic concepts of a bundle and how to define it in compliance with specifications. + +## Definition + +OpenHarmony software is developed on a per-bundle basis. In terms of the operating system, all software running on OpenHarmony are bundles. Generally, bundles are classified into the following types based on their application scopes: + +- Board-level bundles: device hardware-specific bundles, such as **board**, **arch**, and **mcu** +- System-level bundles: a set of bundles with independent features, such as the kernel, file system, and framework +- Application-level bundles: applications that provide services to users, such as **wifi\_iot** and **ip\_camera** + +Bundles are designed for the reuse purpose. Any reusable modules can be defined as bundles. They are classified into the following types based on their forms: + +- Source code +- Binary system +- Code snippet +- Distribution + +## Bundle Division Principles + +In principle, bundles should be grouped at a fine-grained granularity as much as possible to achieve maximum reuse. The following factors are taken into account regarding bundle division: + +- Independence: Bundles provide relatively independent features and can be independently compiled. Each of them is capable of providing its own APIs and services for external systems. +- Coupling: If a bundle must depend on another bundle to provide services, they can be coupled to one bundle. +- Correlation: If a group of bundles jointly implement a feature, and if other bundles never depend on them, the group of bundles can be combined into one bundle. + +## Bundle Dependency + +A bundle dependency can be mandatory or optional. + +- Mandatory dependency: If bundle A must depend on bundle B to implement a feature, that is, the APIs or services specific to bundle B must be invoked, then bundle B is defined as the mandatory dependency of bundle A. +- Optional dependency: If either bundle C or bundle D is required for bundle A to implement a feature, and if bundle C and bundle D are interchangeable, then bundle C and bundle D are defined as optional dependencies of bundle A. diff --git a/docs-en/bundles/preparations.md b/docs-en/bundles/preparations.md index b7471893cb72ec8e5d98a272ac4eea3c55ddeb88..3a0750c1411494e16c930c67f479b073138c4f52 100755 --- a/docs-en/bundles/preparations.md +++ b/docs-en/bundles/preparations.md @@ -54,15 +54,15 @@ login = https://hpm.harmonyos.com/hpm/auth/pk # Configure t loginUser = {your-account} # Configure the account for HPM login, mandatory for publishing bundles. shellPath = C:\WINDOWS\System32\cmd.exe # Configure the shell for running HPM commands. globalRepo = C:\Users\yourname\.global # Configure the path for storing bundles that are installed globally. -http_proxy = http://your-proxy-server:port # Configure the HTTP proxy. -https_proxy = http://your-proxy-server:port # Configure the HTTPS proxy. +http_proxy = http://your-proxy-server:port # Configure the HTTP proxy. +https_proxy = http://your-proxy-server:port # Configure the HTTPS proxy. ``` For details about **hpm-cli** commands, see [HPM Commands](bundle-management.md#table10510164515371). ## Downloading OpenHarmony Code -For details, see [Source Code Acquisition](../get-code/source-code-acquisition.md). +For details, see . ## Installing Dependent Bundles diff --git a/docs-en/contribute/OpenHarmony-JavaScript-coding-style-guide.md b/docs-en/contribute/OpenHarmony-JavaScript-coding-style-guide.md old mode 100644 new mode 100755 diff --git a/docs-en/contribute/OpenHarmony-c-coding-style-guide.md b/docs-en/contribute/OpenHarmony-c-coding-style-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..6231319194293ad1bfdf9dfd6d51affe408889a1 --- /dev/null +++ b/docs-en/contribute/OpenHarmony-c-coding-style-guide.md @@ -0,0 +1,2116 @@ +# C Coding Style Guide + +## Purpose + +Rules are not perfect. Disabling useful features in specific situations may affect code implementation. However, the rules are formulated "to help most programmers to get more benefits". If a rule is found unhelpful or difficult to follow in team coding, please send your feedback to us so we can improve the rule accordingly. Before referring to this guide, you are expected to have the following basic capabilities for C rather than being a beginner who wants to learn about C. + +1. Understand the ISO standard of C. +2. Be familiar with the basic features of C. +3. Understand the standard library of C. + +## General Principles + +Code must meet the requirements for **readability**, **maintainability**, **security**, **reliability**, **testability**, **efficiency**, and **portability** while ensuring functionality correctness. + +## Conventions + +**Rule**: Conventions that must be followed during programming. +**Suggestion**: Conventions that must be considered during programming. + +It is necessary to understand the reason for these conventions and to try and comply with them, no matter if they are rules or recommendations. + +## Exceptions + +The only acceptable exceptions are those that do not violate the general principles and provide appropriate reasons for their existence. +Exceptions destroy code consistency. Try to avoid them. Exceptions to 'Rules' should be very rare. + +The style consistency principle is preferred in the following case: +**When you modify open source or third-party code. The existing code specifications prevail.** + +# 1 Naming + +Names include file, function, variable, type, and macro names. + +Naming is considered the most difficult and important thing in software development. +The name of an identifier must be clear, well defined, and easy to understand, adapting to reading habit. + +The unified naming style is the most direct expression of the consistency principle. + +## General Conventions + +**CamelCase** +Camel case is the practice of writing compound words or phrases so that each word or abbreviation in the phrase begins with a capital letter, and with no intervening spaces or punctuation. +This style has two alternatives depending on the case of the first letter: **UpperCamelCase and lowerCamelCase** + +### Rule 1.1 Name identifiers in camel case style. + +| Type| Naming Style +|----------|---------- +| Function, struct, enumeration, union| UpperCamelCase +| Variable, function parameter, macro parameter, struct body, union member| lowerCamelCase +| Macro, constant, enumerated value, goto tag| All capitalized, separated with underscores (\_) + +Note: +The `constant` in the above table refers to the variable of the basic data type, enumeration, and string type of the const modifier under the global scope, excluding array, struct and union. +The `variable` in the above table refers to variables other than the constant definition, all using lowercase. + +### Rec 1.1 The larger the scope, the more accurate the name should be. + +C is different from C++. There is no namespace or class. Therefore, the names of identifiers in the global scope must not conflict with each other. +Names of global functions, global variables, macros, types, and enumerations must be accurately described and unique in the global scope. + +Example: + +```c +int GetCount(void); // Bad: inaccurate description. +int GetActiveConnectCount(void); // Good +``` + +For accurate naming, a module prefix can be added if necessary. +The module prefix and the naming body can be connected by following the CamelCase style. +Example: + +```c +int PrefixFuncName(void); // OK: CamelCase, with no prefix in the format, but prefix in the content. + +enum XxxMyEnum { // OK. + ... +}; +``` + +## File Naming + +### Rec 1.2 Use lowercase file names. + +File names naming are allowed only with lowercase letters, numbers, and underscores (\_). +File names should be as short, accurate, and unambiguous as possible. +The reason for using lowercase file names is that different systems process file names in different ways (for example, Microsoft DOS and Windows OS are not case sensitive, but Unix/Linux and Mac systems are by default). + +Good example: +`dhcp_user_log.c` + +Bad examples: +`dhcp_user-log.c`: It is not recommended you separate names with '-'. +`dhcpuserlog.c`: The words are not separated, causing poor readability. + +## Function Naming + +Functions are named in UpperCamelCase style. + +### Rec 1.3 Name functions that comply with reading habits. + +The "verb + object" structure can be used for action related function names. For example: + +```c +AddTableEntry() // OK +DeleteUser() // OK +GetUserInfo() // OK +``` + +An adjective or a prefix "is" can be used in a function returning a Boolean value. For example: + +```c +DataReady() // OK +IsRunning() // OK +JobDone() // OK +``` + +Data or Getter function: + +```c +TotalCount() // OK +GetTotalCount() // OK +``` + +## Variable Naming + +Variables are named in the lowerCamelCase style. This includes global variables, local variables, parameters in the function declaration or definition as well as parameters in function-like macro. + +### Rule 1.2 Add the 'g\_' prefix to global variables. Do not add this prefix to static variables in a function. + +Global variables should be used as little as possible, and special attention should be paid to their use. Prefixes are used for visual prominence, prompting developers to be more careful about using global variables. +The naming rule of global static variables is the same as that of global variables. The name of a static variable in a function is the same as that of a common local variable. + +```c +int g_activeConnectCount; + +void Func(void) +{ + static int pktCount = 0; + ... +} +``` + +Notes: The nature of a constant is also a global variable, but it does not apply to current rule if the naming style is all uppercase and connected by underline. + +### Rec 1.4 Keep local variables short and to the point. + +The name of a local variable should be short on the premise that meanings can be expressed through context. + +The following is an example: + +```c +int Func(...) +{ + enum PowerBoardStatus powerBoardStatusOfSlot; // Not good: Long redundant local variable. + powerBoardStatusOfSlot = GetPowerBoardStatus(slot); + if (powerBoardStatusOfSlot == POWER_OFF) { + ... + } + ... +} +``` + +Better writing style: + +```c +int Func(...) +{ + enum PowerBoardStatus status; // Good: The status can be clearly expressed in context. + status = GetPowerBoardStatus(slot); + if (status == POWER_OFF) { + ... + } + ... +} +``` + +Similarly, "tmp" can be used to address any type of temporary variable. +A short variable name should be used with caution, but sometimes a single character variable is allowed, for example, a counter variable in a loop statement. + +```c +int i; +... +for (i = 0; i < COUNTER_RANGE; i++) { + ... +} +``` + +Or, variables in simple math functions: + +```c +int Mul(int a, int b) +{ + return a * b; +} +``` + +## Type Naming + +Types are named in the UpperCamelCase style. +The type can be a structure, a union, or an enumeration. + +Example: + +```c +struct MsgHead { + enum MsgType type; + int msgLen; + char *msgBuf; +}; + +union Packet { + struct SendPacket send; + struct RecvPacket recv; +}; + +enum BaseColor { + RED, // Note: The enumeration is in UpperCamelCase style while the enumerated values adopt the macro naming style. + GREEN, + BLUE +}; + +typedef int (*NodeCmpFunc)(struct Node *a, struct Node *b); +``` + +When you use typedef to set an alias for a struct, a union or an enum type, try to use anonymous type. +If you need self-nesting pointers, you can add a 'tag' prefix or an underscore suffix. + +```c +typedef struct { // Good: The anonymous struct is used because self-nesting is not required. + int a; + int b; +} MyType; // Struct alias with UpperCamelCase. + +​```c +typedef struct tagNode { // Good: Add the 'tag' prefix or use 'Node_'. + struct tagNode *prev; + struct tagNode *next; +} Node; // UpperCamelCase. +``` + +## Macros, Constants, and Enumeration Naming + +Macros and enumerated values are capitalized and are separated with underscores (\_). +Constants are recommended to be capitalized and be separated with underscores (\_). Also, as global const variables, they can be named with global variable style. +The constants here are defined as global const variables of basic data type, enumeration, and string type. + +Function-like macros, can be named the same way as functions, using the UpperCamelCase naming style. +However, this approach makes the macros look the same as functions. It is confusing and needs special attention. + +Macro example: + +```c +#define PI 3.14 +#define MAX(a, b) (((a) < (b)) ? (b) : (a)) +``` + +```c +#ifdef SOME_DEFINE +void Bar(int); +#define Foo(a) Bar(a) // The function-like macro is named as a function style. +#else +void Foo(int); +#endif +``` + +Constant example: + +```c +const int VERSION = 200; // OK. + +const enum Color DEFAULT_COLOR = BLUE; // OK. + +const char PATH_SEP = '/'; // OK. + +const char * const GREETINGS = "Hello, World!"; // OK. +``` + +Non-constant example: + +```c +// Structure type does not meet the definition of constant. +const struct MyType g_myData = { ... }; // OK: Name it in lowerCamelCase style. + +// Array type does not meet the definition of constant. +const int g_xxxBaseValue[4] = { 1, 2, 4, 8 }; // OK: Name it in lowerCamelCase style. + +int Foo(...) +{ + // The scope does not meet the definition of constant. + const int bufSize = 100; // OK: Name it in lowerCamelCase style. + ... +} +``` + +Enumeration example: + +```c +// Note: The enumeration type name is in the UpperCamelCase style, while enumerated values are all capitalized and separated with underscores (_). +enum BaseColor { + RED, + GREEN, + BLUE +}; +``` + +### Rec 1.5 Do not name temporary variables in function-like macros and pollute the external scope. + +First, **use function-like macros as little as possible.** + +When a function-like macro needs to define local variables, to avoid naming conflicts with local variables in external functions, + +an underline is a good solution. Example: + +```c +#define SWAP_INT(a, b) do { \ + int tmp_ = a; \ + a = b; \ + b = tmp_; \ +} while (0) +``` + +# 2 Formatting + +## Line Length + +### Rec 2.1 Ensure that each line is no more than 120 characters in length. + +The line width of the code should not be too long, otherwise it is not conducive to reading. +The line length requirement indirectly guides developers in shortening function and variable names, reducing nesting, and improving readability. +It is strongly recommended that the number of characters per line do not exceed **120** unless readability is significantly improved as a result and no information is hidden. +While the resolution of modern monitors is very high, long lines will increase the difficulty of reading comprehension, which is against the principles of "clear" and "concise" defined in this document. + +The following scenarios should not be wrapped, and can be treated as exceptions: + +- Line breaks can cause strings truncated and hard to retrieved (grep), such as command lines or URLs. Codes or comments that contain these can be treated as exceptions appropriately. +- '#include', '#error' statements are allowed to exceed the line width requirement, but you should try to avoid this. + +Example: + +```c +#ifndef XXX_YYY_ZZZ +#error Header aaaa/bbbb/cccc/abc.h must only be included after xxxx/yyyy/zzzz/xyz.h +#endif +``` + +## Indentation + +### Rule 2.1 Use spaces to indent and indent four spaces at a time. + +Only spaces can be used for indentation. **4** spaces are indented each time. Do not use the Tab character to indent. +Currently, almost all integrated development environments (IDEs) and code editors support automatically converting a Tab input to **4** spaces. Please set your code editor to support indentation with spaces, if it is not the default. + +## Braces + +### Rule 2.2 Use the K\&R indentation style. + +**K\&R style** +While wrapping a line, the left brace of the function starts a new line and takes a single line. Other left braces are placed at the end of the line along with the statement. +The right brace takes a single line, unless it is followed by the rest of the same statement, such as 'while' in the 'do' statement, or 'else'/'else if' of the 'if' statement, or a comma or semicolon. + +For example: + +```c +struct MyType { // Good: Follow the statement to the end, and indent one space. + ... +}; // Good: The right brace is followed by the semicolon. + +int Foo(int a) +{ // Good: The left brace of the function starts a new line, and nothing else is placed on the line. + if (...) { + ... + } else { // Good: The 'else' statement follows the right brace. + ... + } // Good: The right brace takes a single line. +} +``` + +## Function Declaration and Definition + +### Rule 2.3 The return type and function name of the function declaration and definition must be on the same line. The function parameter list must be aligned appropriately if it needs to be wrapped. + +When a function is declared and defined, the return value type of the function should be in the same line as the function name. + +When the function parameter list is wrapped, it should be aligned appropriately. +The left parenthesis of a parameter list is always in the same line as the function name. The right parenthesis always follows the last parameter. + +The following is an example of line breaks: + +```c +ReturnType FunctionName(ArgType paramName1, ArgType paramName2) // Good:All in one line +{ + ... +} + +ReturnType VeryVeryVeryLongFunctionName(ArgType paramName1, // The line length cannot accommodate all parameters and thus a line break is required. + ArgType paramName2, // Good:Aligned with the previous line. + ArgType paramName3) +{ + ... +} + +ReturnType LongFunctionName(ArgType paramName1, ArgType paramName2, // Subject to line length, a line break is required. + ArgType paramName3, ArgType paramName4, ArgType paramName5) // Good: After the line break, 4 spaces are used for indentation. +{ + ... +} + +ReturnType ReallyReallyReallyReallyLongFunctionName( // The line length cannot accommodate the first parameter, and thus a line break is required. + ArgType paramName1, ArgType paramName2, ArgType paramName3) // Good: After the line break, 4 spaces are used for indentation. +{ + ... +} +``` + +## Function Calls + +### Rule 2.4 The parameter list should be aligned appropriately when the parameter list requires a line break. + +When the function is called, if the function parameter list is wrapped, it should be aligned appropriately. +The left parenthesis is always followed by a function name, and the right parenthesis always follows the last parameter. + +The following is an example of line breaks: + +```c +ReturnType result = FunctionName(paramName1, paramName2); // Good:Function parameters are placed in one line. + +ReturnType result = FunctionName(paramName1, + paramName2, // Good:Aligned with the above parameters. + paramName3); + +ReturnType result = FunctionName(paramName1, paramName2, + paramName3, paramName4, paramName5); // Good:After the line break, 4 spaces are used for indentation. + +ReturnType result = VeryVeryVeryLongFunctionName( // The line length cannot accommodate the first parameter, and thus a line break is required. + paramName1, paramName2, paramName3); // After the line break, 4 spaces are used for indentation. +``` + +If the parameters in a function call are associated with each other, the parameters are grouped for better understanding, rather than strictly adhering to formatting requirements. + +```c +// Good:The parameters in each line represent a group of data structures with a strong correlation. They are placed on one line for ease of understanding. +int result = DealWithStructureLikeParams(left.x, left.y, // Indicates a group of parameters. + right.x, right.y); // Indicates another group of related parameters. +``` + +## Conditional Statements + +### Rule 2.5 Conditional statements must use braces. + +We require that all conditional statements use braces, even if there is only one statement. +Reason: + +- Logic is intuitive and easy to read. +- It is not easy to make mistakes when adding new code to the existing conditional statement. +- Function-like macros without braces are used in conditional statements, can be error prone if braces do not surround the conditional statement. + +```c +if (objectIsNotExist) { // Good: Braces are added to a single-line conditional statement. + return CreateNewObject(); +} +``` + +### Rule 2.6 Do not place 'if', 'else' and 'else if' in the same line. + +In a conditional statement, if there are multiple branches, they should be written in different lines. + +The correct format: + +```c +if (someConditions) { + ... +} else { // Good: The 'else' is in a different line of 'if'. + ... +} +``` + +The following is a case that does not comply with the specifications: + +```c +if (someConditions) { ... } else { ... } // Bad: They are in the same line. +``` + +## Loops + +### Rule 2.7 Use braces in loop statements. + +Similar to the condition expression, we require that the for and while loop conditional statements contain braces, even if there is only one loop. + +```c +for (int i = 0; i < someRange; i++) { // Good: Braces are used. + DoSomething(); +} +``` + +```c +while (condition) { } // Good: The while loop body is empty. And braces are used. +``` + +```c +while (condition) { + continue; // Good: The continue keyword highlights the end of the empty loop. And braces are used. +} +``` + +Bad example: + +```c +for (int i = 0; i < someRange; i++) + DoSomething(); // Bad: Braces should be added. +``` + +```c +while (condition); // Bad: Using semicolons will make people misunderstand which code is a part of the while statement. +``` + +## Switch Statements + +### Rule 2.8 Indent the case or default statement in a switch statement block. + +The indentation style of the switch statement is as follows: + +```c +switch (var) { + case 0: // Good: Indented + DoSomething1(); // Good: Indented + break; + case 1: { // Good: Braces are added. + DoSomething2(); + break; + } + default: + break; +} +``` + +```c +switch (var) { +case 0: // Bad: 'case' not indented + DoSomething(); + break; +default: // Bad: 'default' not indented + break; +} +``` + +## Expressions + +### Rec 2.2 Keep a consistent expression line break style and ensure that operators are placed at the end of the line. + +A long expression that does not meet the line length requirement must be wrapped appropriately. Generally, the expression is wrapped at an operator of a lower priority or a hyphen, and the operator or hyphen is placed at the end of the line. +The operator and hyphen are placed at the end of the line, indicating that the operation is to be continued. + +Example: + +```c +// Pretend that the following first line does not meet the line length requirement. +if ((currentValue > MIN) && // Good: After the line break, the Boolean operator is placed at the end of the line. + (currentValue < MAX)) { + DoSomething(); + ... +} + +int result = reallyReallyLongVariableName1 + // Good: The plus sign is placed at the end of the line. + reallyReallyLongVariableName2; +``` + +After an expression is wrapped, ensure that the lines are properly aligned or indented by 4 spaces. See the following example. + +```c +int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + + longVaribleName4 + longVaribleName5 + longVaribleName6; // OK: indented with 4 spaces + +int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + + longVaribleName4 + longVaribleName5 + longVaribleName6; // OK: aligned +``` + +## Variable Assignment + +### Rule 2.9 Multiple variable definitions and assignment statements cannot be written on one line. + +It is best to have only one variable initialization statement on each line. It is easier to read and understand. + +```c +int maxCount = 10; +bool isCompleted = false; +``` + +The following is an example that does not comply with the specifications: + +```c +int maxCount = 10; bool isCompleted = false; // Bad: Multiple initialization statements are placed on the same line. +int x, y = 0; // Bad: Variable definitions need to be placed on different lines. Each definition occupies one line. + +int pointX; +int pointY; +... +pointX = 1; pointY = 2; // Bad: Multiple variable assignment statements are placed on the same line. +``` + +Exception: +If multiple variables with strong correlation are defined and do not need to be initialized, you can define the variables in a line to reduce repeated information so that the code is more compact. + +```c +int i, j; // Good: Multiple variables that are defined and do not need to be initialized immediately can be written in one line. +for (i = 0; i < row; i++) { + for (j = 0; j < col; j++) { + ... + } +} +``` + +## Initialization + +Initialization is applicable to structs, unions, and arrays. + +### Rule 2.10 Indent when initiating a new line, or make a reasonable alignment. + +When a structure or array is initialized, if a line break is made, ensure that the line is indented with 4 spaces. +From the readability point of view, make a reasonable alignment. + +```c +// Good: No line break for a short line. +int arr[4] = { 1, 2, 3, 4 }; + +// Good: A line break here makes the readability better. +const int rank[] = { + 16, 16, 16, 16, 32, 32, 32, 32, + 64, 64, 64, 64, 32, 32, 32, 32 +}; +``` + +For complex data, the initialization should be clear and compact. +Refer to the following format: + +```c +int a[][4] = { + { 1, 2, 3, 4 }, { 2, 2, 3, 4 }, // OK. + { 3, 2, 3, 4 }, { 4, 2, 3, 4 } +}; + +int b[][8] = { + { 1, 2, 3, 4, 5, 6, 7, 8 }, // OK. + { 2, 2, 3, 4, 5, 6, 7, 8 } +}; +``` + +```c +int c[][8] = { + { + 1, 2, 3, 4, 5, 6, 7, 8 // OK. + }, { + 2, 2, 3, 4, 5, 6, 7, 8 + } +}; +``` + +Note: + +- If the left brace is placed at the end of the line, the corresponding right brace shoud be placed into a new line. +- If the left brace is followed by the content, the corresponding right brace should also follow the content. + +### Rule 2.11 When struct and union members are initialized, each member is initialized on a separate line. + +The C99 standard supports the initialization of the struct and union members in their definition. This is called the designated initializer. If initialization is performed in this way, each member is initialized in a separate line. + +```c +struct Date { + int year; + int month; + int day; +}; + +struct Date date = { // Good: When the designated initializer is used, each member is initialized on a separate line. + .year = 2000, + .month = 1, + .day = 1 +}; +``` + +## Pointers + +### Rec 2.3 The pointer type asterisk "\*" follows the variable name or the type. Do not leave spaces on both sides and always use at least one space. + +When you declare or define a pointer variable or return a pointer type function, "\*" can be placed on the left or right, adhering to the type or name. Do not leave spaces on both sides. And do not leave out spaces altogether. + +```c +int *p1; // OK. +int* p2; // OK. + +int*p3; // Bad: No spaces. +int * p4; // Bad: Spaces on both sides. +``` + +Choose a style and stay consistent. + +When using style that "\*" follows type, avoid declaring multiple variables with pointer in a line. + +```c +int* a, b; // Bad: It is easy to misinterpret b as a pointer. +``` + +When using style that "\*" follows variable, there may be situations that cannot be followed. +Do not follow when it cannot. Style consistency first. + +```c +char * const VERSION = "V100"; // OK. +int Foo(const char * restrict p); // OK. +``` + +"\*" never follows 'const' or 'restrict' keywords anytime. + +## Compilation Preprocessing + +### Rule 2.12 The number sign (#) must be placed at the beginning of a line for compilation preprocessing and can be indented in nested compilation preprocessing. + +The number sign (#) must be placed at the beginning of a line for compilation preprocessing, even if the code is embedded in the function body. + +```c +#if defined(__x86_64__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) // Good: "#" is at the beginning of the line. +#define ATOMIC_X86_HAS_CMPXCHG16B 1 // Good: "#" is at the beginning of the line. +#else +#define ATOMIC_X86_HAS_CMPXCHG16B 0 +#endif + +int FunctionName(void) +{ + if (someThingError) { + ... +#ifdef HAS_SYSLOG // Good: "#" is at the beginning of the line even though it's in a function body. + WriteToSysLog(); +#else + WriteToFileLog(); +#endif + } +} +``` + +Nested preprocessing statements starting with "#" can be indented and aligned based on indentation requirements to different layers. + +```c +#if defined(__x86_64__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) + #define ATOMIC_X86_HAS_CMPXCHG16B 1 // Good: Statements are layered, facilitating reading. +#else + #define ATOMIC_X86_HAS_CMPXCHG16B 0 +#endif +``` + +## Whitespace + +### Rule 2.13 Ensure that horizontal whitespace is used to highlight keywords, important information and avoid unnecessary whitespace. + +Horizontal spaces should be used to highlight keywords and important information. Do not add spaces at the end of each line of code. The general rules are as follows: + +- Add spaces after keywords such as if, switch, case, do, while, and for. +- Do not add spaces after the left parenthesis or before the right parenthesis. +- Add a space before and after each binary operator (= + - \< > \* / % \| \& \^ \<= >= == !=). +- Do not add a space after any unary operator (\& \* + - ~!). +- A space is required before and after each ternary operator (? :). +- Add spaces before and after the colon of bit field description. +- There is no space between ++/-- and variables. +- There is no space before and after the struct member operator (. ->). +- Adding or not adding spaces inside the brace must be consistent. +- Do not add spaces before commas, semicolons, colons (without the colon in the ternary operator or the bit field description); Add spaces after them. +- There is no space between the parentheses of the function parameter list and the function name. +- There is no space between the parenthesis of the type cast and the object being converted. +- There is no space between the square bracket of the array and the array name. +- Spaces at the end of the line can be omitted. + +For spaces inside the braces, the following are **recommended**: + +- In general, spaces should be added after the left brace or before the right brace. +- For empty, or a single identifier, or a single literal constant, spaces are not required. Such as: '{}', '{0}', '{NULL}', '{"hi"}'. +- Spaces between consecutively nested multiple parentheses, spaces are not required. Such as: '{{0}}', '{{ 1, 2 }}'. Bad example: '{ 0, {1}}'. It is not a continuous nested scene, and the spaces inside the outermost braces are inconsistent. + +In normal cases: + +```c +int i = 0; // Good: When the variable is initialized, there should be spaces before and after the =. Do not leave a space before the semicolon. +int buf[BUF_SIZE] = {0}; // Good: During array initialization, spaces in curly braces are optional. +int arr[] = { 10, 20 }; // Good: A space is added before and after the brace. +``` + +Function definition and call: + +```c +int result = Foo(arg1,arg2); + ^ // Bad: There should be a space after the comma. + +int result = Foo( arg1, arg2 ); + ^ ^ // Bad: No space should be added to either side in the parentheses. +``` + +Pointer and address-of operator: + +```c +x = *p; // Good: There is no space between the operator * and the pointer p. +p = &x; // Good: There is no space between the operator & and the variable x. +x = r.y; // Good: When a member variable is accessed through the operator (.), no space is added. +x = r->y; // Good: When a member variable is accessed through the operator (.), no space is added. +``` + +Operator: + +```c +x = 0; // Good: A space must be added before and after the assignment operator (=). +x = -5; // Good: Do not add spaces before the minus sign (-) and the number. +++x; // Good: Do not add spaces before the minus sign (-) and the number. +x--; + +if (x && !y) // Good: A space must be added before and after the Boolean operator. Do not leave spaces between the ! operator and variables. +v = w * x + y / z; // Good: A space must be added before and after binary operators. +v = w * (x + z); // Good: No space is required before and after the expression in the parentheses. +``` + +Loops and conditional statements: + +```c +if (condition) { // Good: A space is added between the if keyword and the parenthesis and no space is added before or after the conditional statement inside the parentheses. + ... +} else { // Good: A space is added between the else keyword and the curly brace. + ... +} + +while (condition) {} // Good: A space is added between the while keyword and the parenthesis. No space is added before or after the conditional statement inside the parentheses. + +for (int i = 0; i < someRange; ++i) { // Good: A space is added between the for keyword and the parenthesis, and after the semicolons (;). + ... +} + +switch (var) { // Good: A space is added after the switch keyword. + case 0: // Good: No space is added between the case conditional statement and the colon. + ... + break; + ... + default: + ... + break; +} +``` + +Note: Current integrated development environments (IDEs) and code editors can be set to automatically delete spaces at the end of a line. Please configure your editor as such. + +### Rec 2.4 Arrange blank lines reasonably keep the code compact. + +Reduce unnecessary blank lines so that more code can be displayed for easy reading. The following rules are recommended: + +- Make a reasonable arrangement of blank lines according to the degree of relevance of neighboring content. +- Do not put two or more consecutive blank lines inside a function, a type definition, a macro, or an initialization expression. +- Do not use **three** or more consecutive blank lines. +- Do not add blank lines at the start and end of a code block defined by braces. + +```c +ret = DoSomething(); + +if (ret != OK) { // Bad: Return value judgment should follow the function call. + return -1; +} +``` + +```c +int Foo(void) +{ + ... +} + + + +int Bar(void) // Bad: Use no more than two continuous blank lines. +{ + ... +} +``` + +```c +int Foo(void) +{ + + DoSomething(); // Bad: The blank lines above and below are unnecessary. + ... + +} +``` + +# 3 Comments + +Generally, clear architecture and good symbol naming are recommended to improve code readability, and comments are provided only when necessary. +Comments help readers quickly understand code. Therefore, **comments should be provided when necessary** for the sake of readers. + +The comments must be concise, clear, and unambiguous, ensuring that the information is complete and not redundant. + +**Comments are as important as code.** +When writing a comment, you need to step into the reader's shoes and use comments to express what the reader really needs. Comments are used to express the function and intention of code, rather than repeating code. +When modifying the code, ensure that the comments are consistent. It is impolite to only modify code and not update the comments. It destroys the consistency between code and comments, and may confuse or even mislead readers. + +Comment code in fluent English. + +## Comment Style + +In C code, both `/*` `*/` and `//` can be used. +Comments can be classified into different types depending on purpose and position, such as file header comments, function header comments, and general comments. +Comments of the same type must keep a consistent style. + +Note: The sample code used in this article sees extensive use of the '//' post-comment. This is only to aid understanding, and does not mean that this comment style is better. + +## File Header Comments + +### Rule 3.1 File header comments must contain the copyright license. + +/\* + +* Copyright (c) 2020 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* ​ +* http://www.apache.org/licenses/LICENSE-2.0 +* ​ +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. \*/ + +## Function Header Comments + +### Rule 3.2 Empty function header comments with no content are forbidden. + +Not all functions need function header comments. +Function header comments must be added for any information that cannot be expressed just with function prototype. + +Function header comments are placed above the function declaration or definition. +Select and use one of the following styles: +**Use '//' to start the function header.** + +```c +// Single-line function header +int Func1(void); + +// Multi-line function header +// Second line +int Func2(void); +``` + +**Use '/\*' '\*/' to start the function header.** + +```c +/* Single-line function header */ +int Func1(void); + +/* + * Single-line or multi-line function header + * Second line + */ +int Func2(void); +``` + +Use function names to describe functions, and only add function header comments if necessary. +Do not write useless or redundant function headers. Do not write empty function headers with no content. + +The function header comment content is **optional** any may include the following: function description, return value, performance constraint, usage, memory convention, algorithm implementation, and reentering requirements. +In the function interface declaration in the external header file of the module, the function header comment should clearly describe important and useful information. + +Example: + +```c +/* + * The number of written bytes is returned. If -1 is returned, the write operation fails. + * Note that, the memory buffer is released by the caller. + */ +int WriteString(char *buf, int len); +``` + +Bad example: + +```c +/* + * Function name: WriteString + * Function: Write a character string. + * Parameter: + * Return value: + */ +int WriteString(char *buf, int len); +``` + +Problems in the preceding example are as follows: + +- The 'Parameter' and 'Return value' headings have no content. +- The function name has redundant information. +- The most important thing, the notice to release the buffer, is not clearly stated. + +## Code Comments + +### Rule 3.3 Code comments are placed above or to the right of the corresponding code. + +### Rule 3.4 There must be a space between the comment character and the comment content. At least one space is required between the comment and code if the comment is placed to the right of code. + +Comments placed above code should be indented to the same level as the code. +Select and use one of the following styles: +**Use '//' to start the comment.** + +```c +// Single-line comment +DoSomething(); + +// Multi-line comment +// Second line +DoSomething(); +``` + +**Use '/\*' '\*/' to start the comment.** + +```c +/*Single-line comment */ +DoSomething(); + +/* + * Single-/Multi-line comment + * Second line + */ +DoSomething(); +``` + +Leave at least one space between the code and the comment on the right. No more than four spaces is recommended. +You can use the extended Tab key to indent 1-4 spaces. + +Select and use one of the following styles: + +```c +int foo = 100; // Comment on the right +int bar = 200; /* Comment on the right */ +``` + +It is more appealing sometimes when the comment is placed to the right of code and the comments and code are aligned vertically. +After the alignment, ensure that the comment is 1–4 spaces separated from the closest code line on the left. +Example: + +```c +#define A_CONST 100 /* Related comments of the same type can be aligned vertically. */ +#define ANOTHER_CONST 200 /* Leave spaces after code to align comments vertically. */ +``` + +If the comment on the right exceeds the permitted line length, the comment can be placed above the code. + +### Rule 3.5 Delete unused code segments. Do not comment them out. + +Code that is commented out cannot be maintained. If you attempt to restore the code, it is very likely to introduce ignorable defects. +The correct method is to delete unnecessary code. If necessary, consider porting or rewriting the code. + +Here, commenting out refers to the removal of code from compilation without actually deleting it. This is done by using /\* \*/, //, #if 0, #ifdef NEVER\_DEFINED, and so on. + +### Rec 3.1 No TODO, TBD, or FIXME comment is allowed in code delivered to a customer. + +TODO and TBD comments are used to describe required improvements and supplements. +FIXME comments are used to describe defects that need fixing. +They should have a standardized style, which facilitates text search. For example: + +```c +// TODO(): Supplement to XX +// FIXME: XX defect +``` + +During version development, this type of comments can be used for highlighting, and must be processed and deleted before delivery. + +### Rec 3.2 If 'break' or 'return' is not added to the end of the case statement block (fall-through), comments must be provided. + +Sometimes, the same thing is needed for multiple case tags. When a case statement ends without 'break' or 'return', the statement in the next case tag will be executed. This is called "fall-through". +In this case, you need to add comments for the "fall-through" to clearly describe your intention. + +For example, explicitly specify the "fall-through”: + +```c +switch (var) { + case 0: + DoSomething(); + /* fall-through */ + case 1: + DoSomeOtherThing(); + ... + break; + default: + DoNothing(); + break; +} +``` + +If the case statement is empty, the comment explaining the "fall-through" can be omitted. + +```c +switch (var) { + case 0: + case 1: + DoSomething(); + break; + default: + DoNothing(); + break; +} +``` + +# 4 Header Files + +**For the C language, the design of the header file reflects most of the system design.** +The correct use of the header file makes code more readable, reduces file size, and speeds up compilation and build performance. + +This chapter summarizes some methods from the perspective of programming specifications to help you properly plan header files. + +## Header File Responsibility + +A header file is an external interface of a module or file. +The interface declaration for most functions (except inline functions), is more suitable in the header file, than as interface implementations. +Header responsibility should be simple. A complex header file will make dependencies complex and cause long compilation times. + +### Rec 4.1 Each .c file must have a corresponding .h file, which is used to declare the interfaces that need to be disclosed externally. + +Generally, each .c file has a corresponding .h file (Not necessarily the same name.), which is used to store the function declarations, macro definitions, and type definitions that are to be exposed externally. +If a .c file does not need to open any interface externally, it should not exist. + +Exception: the entry point of the program (for example, the file where the main function is located), unit test code, and dynamic library code. + +Example: +foo.h content + +```c +#ifndef FOO_H +#define FOO_H + +int Foo(void); // Good: Declare an external interface in the header file. + +#endif +``` + +foo.c content + +```c +static void Bar(void); // Good: The declaration of the internal function is placed in the header of the .c file, declaring its static scope. + +void Foo(void) +{ + Bar(); +} + +static void Bar(void) +{ + // Do something; +} +``` + +Internally used functions declarations, macros, enumerations, structures, and others should not be placed in header files. + +In some products, one .c file corresponds to two .h files. One is used to store external public interfaces, and the other is used to store definitions and declarations among others for internal use to limit the number of code lines in the .c file. +This style is not recommended. It is used only because the .c file is too large. It should be split into another file instead. +In addition, if private definitions and declarations are placed in independent header files, they technically cannot avoid inclusion. + +This rule, in turn, is not necessarily correct. For example: +Some simple header files, such as the command ID definition header file, do not need to have the corresponding .c file. +If a set of interface protocols has multiple instances and the interface is fixed, one .h file can have multiple .c files. + +### Rec 4.2 Use .h as the extension of the header file, rather than other unconventional extensions, for example, .inc. + +Some products use .inc as the header file name extension, which does not comply with the C language. A header file using .inc as the file name extension usually indicates a private header file. However, in practice, this recommendation is not followed properly. An .inc file is generally contained in multiple .c files. This document does not recommend that private definitions be stored in header files. For details, see [Rec 4.1](#a4-1). + +## Header File Dependency + +The header file contains a dependency, and the dependency should be stable. +Generally, an unstable module depends on a stable module. When the unstable module changes, the build of the stable module is not affected. + +Dependency direction is as follows: Products depend on the platform, and the platform depends on the standard library. + +In addition to unstable modules depending on stable modules, each module depends on the interface. In this way, in case of any internal implementation changes to one module, users do not need to recompile another module. +This assumes that the interface itself is the most stable. + +### Rule 4.1 Forbid cyclic dependency of header files. + +Cyclic dependency (also known as a circular dependency) of header files means that a.h contains b.h, b.h contains c.h, and c.h contains a.h. If any header file is modified, all code containing a.h, b.h, and c.h needs to be recompiled. +For a unidirectional dependency: a.h contains b.h, b.h contains c.h, and c.h does not contain any header file, modifying a.h does not mean a need to recompile the source code for b.h or c.h. + +The cyclic dependency of header files reflects an obviously unreasonable architecture design, which can be avoided through optimization. + +### Rule 4.2 The header file must have the internal #include protection character (#define protection). + +To prevent header files from being included multiple times, all header files should be protected by #define. Do not use #pragma once. + +When defining a protection character, comply with the following rules: + +- The protection character uses a unique name. It is recommended to consider the file path and name below the top layer of the project code tree. +- Do not place code or comments before or after the protected part, except for file header comments. + +Assume that the path to timer.h of the timer module is `timer/include/timer.h`. If the protection character resembles 'TIME\_H', it is not unique. Add a path, for example: + +```c +#ifndef TIMER_INCLUDE_TIMER_H +#define TIMER_INCLUDE_TIMER_H + +... + +#endif +``` + +### Rule 4.3 Do not reference external function interfaces and variables by using declaration. + +You can use the interfaces provided by other modules or files only by using header files. +Using external function interfaces and variables with an extern declaration may cause inconsistency between declarations and definitions when external interfaces are changed. +In addition, this kind of implicit dependency may cause architecture corruption. + +Cases that do not comply with specifications: +a.c content + +```c +extern int Foo(void); // Bad: Reference external functions by using the extern declaration. +void Bar(void) +{ + int i = Foo(); // Here, the external interface Foo is used. + ... +} +``` + +It should be changed to: +a.c content + +```c +#include "b.h" // Good: Use the interface providing the interface. +void Bar(void) +{ + int i = Foo(); + ... +} +``` + +b.h content + +```c +int Foo(void); +``` + +b.c content + +```c +int Foo(void) +{ + // Do something +} +``` + +In some scenarios, if internal functions need to be referenced with no intrusion to the code, the extern declaration mode can be used. +For example: +When performing unit testing on an internal function, you can use the extern declaration to reference the tested function. +When a function needs to be stubbed or patched, the function can be declared using extern. + +### Rule 4.4 Do not include header files in extern "C". + +If a header file is included in extern "C", extern "C" may be nested. Some compilers restrict the nesting of extern "C". If there are too many nested layers, compilation errors may occur. + +extern "C" usually occurs in mixed programming using both C and C++. If the extern "C" includes a header file, the original intent behind the header file may be hindered. For example, when the linkage specifications are modified incorrectly. + +For example, assume that there are two header files a.h and b.h. +a.h content + +```c +... +#ifdef __cplusplus +void Foo(int); +#define A(value) Foo(value) +#else +void A(int) +#endif +``` + +b.h content + +```c +... +#ifdef __cplusplus +extern "C" { +#endif + +#include "a.h" +void B(void); + +#ifdef __cplusplus +} +#endif +``` + +Using the C++ preprocessor to expand b.h, the following information is displayed: + +```c +extern "C" { + void Foo(int); + void B(void); +} +``` + +In the a.h file, the function Foo is intended to be a C++ free function following the C++ specifications. However, in the b.h file, because `#include "a.h"` is placed inside `extern "C"`, the linking specification of function Foo is changed incorrectly. + +Exception: In the C++ compilation environment, if you want to reference a header file written in pure C, a non-intrusive approach is to exclude the C header file from `extern "C"`. + +# 5 Functions + +Functions help avoid repeated code and increase reusability. In addition, functions act to layer code and reduce code complexity, hiding implementation details, making programs more modular, and facilitating reading and maintenance. + +The function should be concise and short. +One function completes only one thing. + +## Function Design + +Writing clean functions and organizing code effectively is the essence of good function design. The code should be simple and not conceal the designer's intention, using clean abstractions and straightforward control statements to organize the function naturally. + +### Rule 5.1 Avoid long functions and ensure that functions contain no more than 50 lines (not including blank lines and comments). + +A function should fit on one screen, (be no longer than 50 lines), do only one thing, and do it well. + +Large functions are often caused by the fulfillment of more than one purpose by the function, or over complication when parts could be abstracted. + +Exception: +Considering the code's aggregation and functionality, some functions may exceed 50 lines, but only if the code is readable and concise. +These exceptions should be minimal, such as specific algorithm processing. + +Even if a large function works well in the moment, once someone modifies it, new problems may occur. It may even cause bugs that are difficult to discover. +It is recommended that you split it into several simpler and easier to manage functions, facilitating reading and modification of code. + +### Rule 5.2 Avoid nesting a code block more than four times within a function. + +The nested code block depth of a function refers to the layered depth of a code control block (created by statements such as: if, for, while, and switch). +Each layer of nesting increases the brain power required to read the code, because you need to maintain a "stack" in your mind comprised of each conditional statement, loop, and so on. +Code should be broken down functionally to prevent the reader from remembering too much context at a time. + +Using `guardian statements`, (a short conditional return), can effectively reduce the nesting layers of if statements. For example: +The original code nesting level is 3: + +```c +int Foo(...) +{ + if (received) { + type = GetMsgType(msg); + if (type != UNKNOWN) { + return DealMsg(...); + } + } + return -1; +} +``` + +Refactored code using the `guardian statement`, the nesting level is 2: + +```c +int Foo(...) +{ + if (!received) { // Good: Use the 'guardian statement'. + return -1; + } + + type = GetMsgType(msg); + if (type == UNKNOWN) { + return -1; + } + + return DealMsg(..); +} +``` + +Exception: +Considering the code's aggregation and functionality, some functions may exceed 4 times nested, but only if the code is readable and concise. +These exceptions should be rare. + +### Rec 5.1 Process all returned error codes. + +A function (in a standard library, a third-party library, or a user-defined function) must be able to indicate errors. This can be done by using error tags, special return data, or other means. No matter when a function provides such a mechanism, the caller should immediately check the error indication after the function returns. + +Example: + +```c +char fileHead[128]; +ReadFileHead(fileName, fileHead, sizeof(fileHead)); // Bad: The returned value is not checked. + +DealWithFileHead(fileHead, sizeof(fileHead)); // The 'fileHead' is possibly invalid. +``` + +The correct format is as follows: + +```c +char fileHead[128]; +ret = ReadFileHead(fileName, fileHead, sizeof(fileHead)); +if (ret != OK) { // Good: Ensure that the 'fileHead' is written. + return ERROR; +} + +DealWithFileHead(fileHead, sizeof(fileHead)); // Process the file header. +``` + +Note that when the return value of a function is ignored and instead void is returned frequently, consider whether the return value of the function is designed reasonably. +Only if the caller of a function really doesn't need a return value, should you design the function to return `void`. + +## Function Parameters + +### Rec 5.2 Use the return value instead of the output parameter when designing a function. + +Using return values rather than output parameters improves readability and usually provides the same or better performance. + +Readability can be improved by naming functions such as GetXxx, FindXxx, or directly using a single noun, to directly return the corresponding object. + +### Rec 5.3 Use strongly typed parameters and avoid using void\* + +While different languages have their own views on strong typing and weak typing, it is generally believed that C/C++ is a strongly typed language. Since we use such a strongly typed language, we should keep this style. +The advantage of this strongly typed style is to prevent evasive errors by catching errors at the compilation stage. + +Strong types help the compiler find more errors for us. Pay attention to the usage of the `FooListAddNode` function in the following code: + +```c +struct FooNode { + struct List link; + int foo; +}; + +struct BarNode { + struct List link; + int bar; +} + +void FooListAddNode(void *node) // Bad: Here, the void * type is used to pass parameters. +{ + FooNode *foo = (FooNode *)node; + ListAppend(&g_fooList, &foo->link); +} + +void MakeTheList(...) +{ + FooNode *foo; + BarNode *bar; + ... + + FooListAddNode(bar); // Wrong: In this example, the foo parameter was supposed to be passed, but the bar parameter is incorrectly passed instead. No error is reported immediately and issues may occur as a result. +} + +``` + +The preceding problems may be difficult to expose and are more destructive than compiler errors. +If the parameter type of `FooListAddNode` is specified clearly, instead of with the `void *` type, the preceding problem can be detected during compilation. + +```c +void FooListAddNode(FooNode *foo) +{ + ListAppend(&g_fooList, &foo->link); +} +``` + +Exception: For some generic interfaces, you can use the input parameter `void *` to pass different types of pointers. + +### It is the caller's responsibility to check the validity of internal function parameters of a module. + +Validity checks must be performed on data received from external modules to protect programs against illegal input. +When calling internal functions, by default, the caller is responsible for ensuring the validity of any returned data. If the callee takes responsibility for checking data validity, checks may be performed multiple times and redundant code is generated. This is not concise. + +The caller ensures the validity of any received data. This type of contractual programming can make logic simpler and code more readable. +Example: + +```c +int SomeProc(...) +{ + int data; + + bool dataOK = GetData(&data); // Get data. + if (!dataOK) { // Check the result of the previous step ensuring data validity. + return -1; + } + + DealWithData(data); // Call the data processing function. + ... +} + +void DealWithData(int data) +{ + if (data < MIN || data > MAX) { // Bad: The caller has already ensured the validity of the data. + return; + } + + ... +} +``` + +### Rec 5.5 The pointer argument of a function should be declared as 'const' if it is not used to modify the pointed object. + +The const pointer argument, which restricts the function from modifying the object through the pointer, makes code stronger and safer. + +Example: In the example of the strncmp in the 7.21.4.4 of the C99 standard, the invariant parameter is declared as const. + +```c +int strncmp(const char *s1, const char *s2, size_t n); // Good: The invariant parameter is declared as const. +``` + +Note: Whether or not the pointer parameter is to be declared as 'const' depends on the function design, not on whether there is a "modify object" action in the function entity. + +### Rec 5.6 Ensure that the number of parameters in a function is less than or equal to 5. + +If a function has too many parameters, the function is easy to be affected by changes in external code, hindering maintenance. Too many function parameters will also increases the workload for testing. + +The number of parameters in a function must not exceed 5. If the number of parameters exceeds 5, consider the following: + +- Check whether the function can be split. +- Check whether any related parameters can be combined and defined as a struct. + +## Inline Functions + +An inline function is a function optimization method introduced by C99. Function inlining can eliminate the overhead of function calls; thanks to inlining, combination with the called code is implemented, so that the compiler can achieve further code optimization from a larger perspective. The inline function is similar to a function-like macro. For details, see [Rec 6.1](#a6-1). + +### Rec 5.7 Ensure that the inline function contains no more than 10 lines (non-blank and non-comment). + +Defining a function as an inline function generally aims to improve performance, but it does not always improve performance. If the function body is short, function inlining can effectively reduce the size of the target code and improve the function execution efficiency. +Vice versa, if the function body is large, inlining will cause expansion of the target code, especially when there are many call points. +It is recommended that inline functions be controlled to within **10** lines. + +Do not abuse inline functions to improve performance. Avoid premature optimization. In general, a function can be defined as an inline function only when actual test data proves that the inlining achieves higher performance. Functions such as setter and getter functions, that are short and called frequently can be defined as inline functions. + +### Rule 5.3 Define inline functions that will be called by multiple source files in the header file. + +Inline functions are unfolded in compilation. Therefore, the inline function definition must be visible in each source file that calls this function. +As shown in the following code, inline.h has only the declaration of the `SomeInlineFunc` function but no definition. The other.c file includes inline.h. As a result, inlining fails when `SomeInlineFunc` is called. + +inline.h + +```c +inline int SomeInlineFunc(void); +``` + +inline.c + +```c +inline int SomeInlineFunc(void) +{ + // Implementation code +} +``` + +other.c + +```c +#include "inline.h" +int OtherFunc(void) +{ + int ret = SomeInlineFunc(); +} +``` + +Due to this restriction, if multiple source files need to call the same inline function, the definition of the inline function needs to be placed in the header file. +The inline function implementation in **gnu89** differs from that in the **C99** standard. For compatibility, you can declare the function as **static inline**. + +# 6 Macros + +## Function-like Macros + +A function-like macro is a macro (as shown in the following example) similar to a function. It contains several statements to implement a specific function. + +```c +#define ASSERT(x) do { \ + if (!(x)) { \ + printk(KERN_EMERG "assertion failed %s: %d: %s\n", \ + __FILE__, __LINE__, #x); \ + BUG(); \ + } \ +} while (0) +``` + +### Use functions instead of function-like macros. + +Before defining a function-like macro, consider whether it can be replaced with a function. If so, you are advised to replace macros with functions. +The disadvantages of the function-like macro are as follows: + +- Function-like macros haves no type check, which is not as strict as the function call check. For the example code, see [Below](#macro_lack_of_type_check__example). +- If macro parameters are not calculated during macro expansion, unexpected results may be generated. For details, see [Rule 6.1](#r6-1) and [Rule 6.3](#r6-3). +- The macro has no independent scope. When it is used together with control flow statements, unexpected results described in [Rule 6.2](#r6-2) may be generated. +- There are high skill requirements on the proper use of macros (see the following rules), for example, the usage of `#` and parentheses affects readability. +- Extensions of some macros can only be implemented by specific compilers in specific scenarios, such as `statement expression` of `gcc`, reducing the portability. +- After the macro is expanded at the pre-compilation stage, it is invisible during subsequent compilation, linking, and debugging. Macros that contain multiple lines are expanded into a line. Function-like macros are difficult to debug, interrupt, and locate in the case of bugs. +- Macros containing a large number of statements must be expanded at each call point. If there are many call points, the code will be expanded. + +Example code of a function-like macro lacking type check: + +```c +#define MAX(a, b) (((a) < (b)) ? (b) : (a)) + +int Max(int a, int b) +{ + return (a < b) ? b : a; +} + +int TestMacro(void) +{ + unsigned int a = 1; + int b = -1; + + (void)printf("MACRO: max of a(%u) and b(%d) is %d\n", a, b, MAX(a, b)); + (void)printf("FUNC : max of a(%u) and b(%d) is %d\n", a, b, Max(a, b)); + return 0; +} +``` + +Due to the lack of type check, the comparison between `a` and `b` in `MAX` is changed to a comparison ignoring the sign status. The result is **a \< b**. The output is as follows: + +``` +MACRO: max of a(1) and b(-1) is -1 +FUNC : max of a(1) and b(-1) is 1 +``` + +The function does not have the preceding macro disadvantages. However, compared with macros, the biggest disadvantage is that the execution efficiency is not as high (increasing the overhead of function calls and the difficulty of compiler optimization). +Therefore, the C99 standard introduces inline functions (gcc introduces inline functions ahead of this standard). + +The inline function is similar to the macro, as it is also expanded at the call point. The difference is that inline functions are expanded during compilation. +Inline functions have the advantages of both functions and macros: + +- Strict type checking is performed for inline functions and functions. +- The parameter of an inline function/function is calculated only once. +- Inline functions are unfolded in place and there is no overhead for function calls. +- Inline functions are better optimized than standard functions. + +For performance-sensitive code, consider using inline functions instead of function-like macros. +Functions and inline functions cannot completely replace function-like macros, because function-like macros are more suitable for certain scenarios. +For example, in a log scenario, using a function-like macro with variable parameters and default parameters is more convenient. + +```c +int ErrLog(const char *file, unsigned long line, const char *fmt, ...); +#define ERR_LOG(fmt, ...) ErrLog(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +``` + +### Rule 6.1 When a macro is defined, macro parameters must use complete parentheses. + +The macro parameter is replaced by text only when the macro is expanded. The value is calculated when the macro is compiled. After the text replacement, the statements contained in the macro are combined with called code. +The expression after combination may result in a different result than expected, especially when the macro parameter is in an expression. + +The following is an incorrect format: + +```c +#define SUM(a, b) a + b // Bad. +``` + +When the macro is used, the execution result is inconsistent with the expected result. +`100 / SUM(2, 8)` is expanded to `(100 / 2) + 8`. The expected result is `100 / (2 + 8)`. +This problem can be solved by adding parentheses to the entire expression, as shown in the following: + +```c +#define SUM(a, b) (a + b) // Bad. +``` + +However, this method has the following problems: +`SUM(1 << 2, 8)` is extended to `1 << (2 + 8)` (because the priority of `<<` is lower than that of `+`), which is inconsistent with the expected result `(1 << 2) + 8`. + +To solve this problem, add parentheses to each macro parameter, as shown in the following: + +```c +#define SUM(a, b) (a) + (b) // Bad. +``` + +The third scenario is as follows: `SUM(2, 8) * 10` . The result after the extension is `(2) + ((8) * 10)`, which is inconsistent with the expected result `(2 + 8) * 10`. + +In conclusion, the correct format is as follows: + +```c +#define SUM(a, b) ((a) + (b)) // Good. +``` + +But avoid abusing parentheses. As shown in the following, adding parentheses to a single identifier or a positive number is meaningless. + +```c +#define SOME_CONST 100 // Good: No parentheses needed for a single positive number. +#define ANOTHER_CONST (-1) // Good: Parentheses needed for a negative number. + +#define THE_CONST SOME_CONST // Good: No parentheses needed for a single identifier. +``` + +Notes: + +- Macro parameters in '#', '##' operation do not need parentheses. +- Macro parameters which participating in string splicing do not need parentheses. +- If a macro parameter is used as a separate part in one side of an assignment expression (including +=, -=, etc.), parentheses are not required. +- If a macro parameter is used as a separate part in comma expressions, functions or macro call lists, parentheses are not required. + +Example: + +```c +#define MAKE_STR(x) #x // No parentheses for 'x' + +#define HELLO_STR(obj) "Hello, " obj // No parentheses for 'obj' + +#define ADD_3(sum, a, b, c) (sum = (a) + (b) + (c)) // 'a', 'b', and 'c' need parentheses, while 'sum' does not. + +#define FOO(a, b) Bar((a) + 1, b) // 'a' needs parentheses, while 'b' does not. +``` + +### Rule 6.2 Implementation statements of function-like macros that contain multiple statements must be placed in a do-while(0). + +Macros do not have code blocks. Macros do not have code blocks. When a macro is expanded at the call point, the expressions and variables defined in the macro are integrated into the calling code. As a result, variable name conflict and segmentation of macro statements may occur. Use do-while(0) to add a boundary to the macro so that the macro has an independent scope. In addition, a single statement can be formed by combining the macro with a semicolon (;) to avoid this problem. + +The following macro is incorrect: + +```c +// Not Good. +#define FOO(x) \ + (void)printf("arg is %d\n", (x)); \ + DoSomething((x)); +``` + +When the macro is called as shown in the following example code, the for loop only executes the first statement of the macro, and the next statement of the macro is executed only after the loop ends. + +```c +for (i = 1; i < 10; i++) + FOO(i); +``` + +To solve the preceding problem, use braces to enclose the statements defined by `FOO`. + +```c +#define FOO(x) { \ + (void)printf("arg is %d\n", (x)); \ + DoSomething((x)); \ +} +``` + +The brackets are not associated with semicolons (;). The semicolon following the braces is another statement. +In the following code example, the "suspended else' compilation error message is displayed. + +```c +if (condition) + FOO(10); +else + FOO(20); +``` + +The correct format is to wrap the executed body using a do-while(0), as shown in the following: + +```c +// Good. +#define FOO(x) do { \ + (void)printf("arg is %d\n", (x)); \ + DoSomething((x)); \ +} while (0) +``` + +Exception: + +- Macros that contain 'break' or 'continue' statements can be treated as exceptions. Do use these macros carefully. +- An exception can be made when the macro contains an incomplete statement. For example, use a macro to encapsulate the conditional part of the for loop. +- An exception can be a non-multiple statement, or a single if/for/while/switch statement. + +### Rule 6.3 Do not pass expressions with side effects to a function-like macro. + +Since macros are replaced by text, if a function-like macro uses the same macro parameter multiple times and transfers expressions with side effects as macro parameters, unexpected results may occur. +As shown in the following example, the macro `SQUARE` is normal, but the `a++` expression with side effects is passed to the macro. As a result, the value of `a` is different from the expected value after the `SQUARE` macro is executed. + +```c +#define SQUARE(a) ((a) * (a)) + +int a = 5; +int b; +b = SQUARE(a++); // Bad: 'a' is added twice. +``` + +`SQUARE(a++)` is expanded to `((a++) * (a++))` the variable `a` is added twice, and its value is `7` instead of the expected `6`. + +The correct format is as follows: + +```c +b = SQUARE(a); +a++; // Result: a = 6, which is added only once. +``` + +In addition, if the macro parameter contains a function call, the function may be called repeatedly after the macro is expanded. +If the function execution results are the same, it is a waste; if the results are different, the execution result may not meet the expected value. + +### Rec 6.2 Exercise caution when you use the statements such as return, goto, continue, and break in a function-like macro definition. + +In a macro, the process changing statements, such as return, goto, continue, and break. While they can simplify the code, they hide the real process, which hinders understanding and causes resource leakage. + +First, the macro encapsulation of the statement 'return' can easily lead to excessive encapsulation and use. +As shown in the following code, the judgment of `status` is a part of the main process. After being encapsulated in macros, the purpose is not intuitive. The `RETURN_IF` macro is ignored, causing a confused understanding. + +```c +#define LOG_AND_RETURN_IF_FAIL(ret, fmt, ...) do { \ + if ((ret) != OK) { \ + (void)ErrLog(fmt, ##__VA_ARGS__); \ + return (ret); \ + } \ +} while (0) + +#define RETURN_IF(cond, ret) do { \ + if (cond) { \ + return (ret); \ + } \ +} while (0) + +ret = InitModuleA(a, b, &status); +LOG_AND_RETURN_IF_FAIL(ret, "Init module A failed!"); // OK. + +RETURN_IF(status != READY, ERR_NOT_READY); // Bad: The most important logic is not obvious. + +ret = InitModuleB(c); +LOG_AND_RETURN_IF_FAIL(ret, "Init module B failed!"); // OK. +``` + +Second, if return is encapsulated in a macro, it may also cause a memory leak. Example is as follows: + +```c +#define CHECK_PTR(ptr, ret) do { \ + if ((ptr) == NULL) { \ + return (ret); \ + } \ +} while (0) + +... + +mem1 = MemAlloc(...); +CHECK_PTR(mem1, ERR_CODE_XXX); + +mem2 = MemAlloc(...); +CHECK_PTR(mem2, ERR_CODE_XXX); // Wrong: Memory leak. +``` + +If `mem2` fails to apply for memory, the `CHECK_PTR` will return a message instead of releasing `mem1`. +Besides, the name of the `CHECK_PTR` macro is not good. The macro name only reflects the check action and does not specify the result. Readers can see that a failure is returned when the pointer is null only after viewing the macro implementation. It's not inherently obvious. + +In summary: It is not recommended to encapsulate statements that change control flow, such as return, goto, continue, and break in macro definitions. +However, these macros can be used such as return value judgment as an exception. + +Note: **Macro names must contain descriptive keywords if statements that will change control flow such as return, goto, continue, and break are used.** + +### Rec 6.3 Ensure that function-like macros contain no more than 10 lines (not including blank lines and comments). + +A big problem of the function-like macro is that it is more difficult to debug and locate than the function, especially when the macro is too long. +Macro expansion will also lead to more compiled code. It is recommended that function-like macros contain no more than 10 lines. + +# 7 Variables + +In C language coding, variables are the most important except for functions. +When using a variable, you should always follow the principle of "single responsibility". +By scope, variables can be classified into global variables and local variables. + +## Global Variables + +Do not use or avoid using global variables. +In program design, global variables are variables that are accessible to all scopes. Using unnecessary global variables is generally considered a bad habit. + +Disadvantages of global variables: + +- Destroys the independence and portability of a function. Functions can be dependent on global variables and increased coupling results. +- Reduces readability and maintainability. When multiple functions read and write to global variables, their values may not be determinate, which is unfavorable for comprehension and maintenance. +- In a concurrent programming environment, global variables will hinder reentry of functions. Additional synchronization protection must be added to ensure data security. + +If unavoidable, the read and write of global variables should be encapsulated in a centralized manner. + +### Rule 7.1 Do not use global variables as interfaces between modules. + +Global variables are for the internal implementation of modules and should not be exposed as interfaces. +Global variables should be as centralized as possible. If the data of this module needs to be disclosed to external modules, a function as an interface to this data should be provided. + +## Local Variables + +### Rule 7.2 Do not use uninitialized variables as rvalues. + +The variable here refers to a local dynamic variable, and also includes a memory block obtained on a memory heap. +Because their initial values are unpredictable, it is prohibited to use them directly as rvalues without effective initialization. + +```c +void Foo(...) +{ + int data; + Bar(data); // Bad: Uninitialized variables are used as rvalues. + ... +} +``` + +If there are different branches, ensure that all branches are initialized before being used as rvalues. + +```c +void Foo(...) +{ + int data; + if (...) { + data = 100; + } + Bar(data); // Bad: This value is not initialized in some branches. + ... +} +``` + +Uninitialized rvalues can be found by generic static check tools. +For example, the PCLint tool reports a warning for the following two examples. + +> Warning 530: Symbol 'data' (line ...) not initialized Warning 644: Variable 'data' (line ...) may not have been initialized + +### Rule 7.3 Forbid invalid and redundant variable initialization. + +If the initial value is not determined before initialization is performed, it is not concise but not secure, which may introduce problems that are more difficult to discover. + +Common redundant initialization: + +```c +int cnt = 0; // Bad: Redundant initialization. It will be overwritten by later initialization. +... +cnt = GetXxxCnt(); +... +``` + +Variables with conditional values can be initialized to default values during definition. + +```c +char *buf = NULL; // Good: NULL as the default value +if (condition) { + buf = malloc(MEM_SIZE); +} +... +if (buf != NULL) { // Check whether memory has been allocated. + free(buf); +} +``` + +Even worse, redundant clearing for arrays may affect the performance. + +```c +char buf[VERY_BIG_SIZE] = {0}; +memset(buf, 0, sizeof(buf)); // Bad: Redundant clearing +``` + +Invalid initialization, which hides a more severe problem: + +```c +void Foo(...) +{ + int data = 0; // Bad: regular initialization + + UseData(data); // UseData should be placed after GetData. + data = GetData(...); // Get data. + ... +} +``` + +In the preceding code, if 0 is not assigned before initialization, the static check tool can help find the problem of "direct use without being initialized". +However, due to invalid initialization, the defect of placing "UseData" before "GetData" cannot be easily found. + +Therefore, simple code should be written to initialize variables or memory blocks as required. + +The C99 does not limit the definition position of local variables to before the first statement in a function. That is, a variable can now be defined close to a variable. +This concise approach not only limits the scope of the variable scope, but also solves the problem of how to initialize the variable when it is defined. +If this compilation environment is supported, you are advised to define local variables in this way. + +**Exception:** +**As 'Secure Coding Standard' required, pointers, resource variables, and boolean variables can be treated as exceptions of this rule.** + +### Rule 7.4 Do not use magic numbers. + +The so-called magic numbers are the numbers that are unintelligible and difficult to understand. +The magic number is not a concept that can be defined literally. It varies depending on context and service knowledge. + +For example, the number 12 varies in different contexts. +`type = 12;` is not intelligible, but `month = year * 12;` can be understood. +The number 0 is sometimes seen as a magic number. For example, the `status = 0;` cannot truly express any status information. + +Solution: +Comments can be added for numbers that are used only once. +For numbers that are used multiple times, macro or const variables must be defined and self-commented with symbolic naming. + +The following cases are forbidden: +The name is not used to explain the meaning of a number, for example, `#define ZERO 0`. +The value is limited by the name, for example, `#define XX_TIMER_INTERVAL_300MS 300`. + +# 8 Programming Practice + +## Expressions + +### Rec 8.1 When comparing expressions, follow the principle that the left side tends to change and the right side tends to remain unchanged. + +When a variable is compared with a constant, placing the constant on the left, for example, `if (MAX == v)` does not read naturally, and `if (MAX > v)` is more difficult to understand. +The constant should be placed on the right according to the normal reading order and habit. The expression is written as follows: + +```c +if (v == MAX) ... +if (v < MAX) ... +``` + +There are special cases: for example, the expression `if (MIN < v && v < MAX)` is used to check for a range. This first constant should be placed on the left. + +You do not need to worry about accidentally writing '==' as '=' because a compilation alarm will be generated for `if (v = MAX)` and an error will be reported by other static check tools. Use these tools to solve such writing errors and ensure that that code is readable. + +### A full expression containing an increment (++) or decrement (--) operator should have no other using of the variable. + +In an expression containing a variable increment or decrement operation, if the variable is referenced again, the result is not explicitly defined in the C standard, which may vary between compilers or different versions of the same compiler. +For better portability, you should not make any assumptions about the operation sequence not defined in any standards. + +Note that this problem cannot be solved by using parentheses because it is not a problem of priority. + +Example: + +```c +x = b[i] + i++; // Bad: b[i] operation and i++, the order is not clear. +``` + +The correct way is to add a separate line of increment or decrement: + +```c +x = b[i] + i; +i++; // Good: Single line. +``` + +Function parameter: + +```c +Func(i++, i); // Bad: When passing the second parameter, it is not sure whether the increment operation has occurred. +``` + +The correct way: + +```c +i++; // Good: Single line. +x = Func(i, i); +``` + +### Rec 8.2 Use parentheses to specify the sequence of expressions, instead of using the default priority. + +Parentheses can be used to better emphasize the purpose of used operators. This will prevent program errors due to the inconsistency between default priority and the intended design. +However, too many parentheses muddy the code, reducing readability. Use them moderately. + +Parentheses are recommended when expressions contain operators that are not commonly used and are confusing, such as bitwise operators. + +```c +c = (a & 0xFF) + b; /* Parentheses are required while using bit operators. */ +``` + +## Statements + +### Rule 8.2 The switch statement must have a 'default' branch. + +In most cases, the 'default' branch exists in the switch statement to ensure that there is a default processing action when the case tag is missing. + +Exception: +If the switch condition variable is of the enumeration type and the case branches cover all values, then the default branch is redundant. +A modern compiler can check whether the case branch of some enumerated values is missing in the switch statement. A warning will be displayed. + +```c +enum Color { + RED, + BLUE +}; + +// The switch condition variable is an enumerated value. Therefore, you do not need to add the 'default' processing branch. +switch (color) { + case RED: + DoRedThing(); + break; + case BLUE: + DoBlueThing(); + ... + break; +} +``` + +### Rec 8.3 Exercise caution when using the goto statement. + +The goto statement destroys the structure of the program, so you'd better not use the goto statement unless you really need it. You can only jump to statements following the goto statement, and only within the one function. + +The goto statement is used to implement function return to a single point within a function. +If a function has a large number of identical logics that cannot be encapsulated, for example, repeated file execution, the processed part of code after the file operation failure (for example, closing the file handle and releasing memory that is dynamically applied for) is usually placed in the last part of the function body. And the goto statement is placed right before these. In this way, the code becomes clear and concise. It can also be encapsulated in functions or macros, but doing so makes code less straightforward. + +Example: + +```c +// Good: Use a goto statement to implement function return at a single point. +int SomeInitFunc(void) +{ + void *p1; + void *p2 = NULL; + void *p3 = NULL; + + p1 = malloc(MEM_LEN); + if (p1 == NULL) { + goto EXIT; + } + + p2 = malloc(MEM_LEN); + if (p2 == NULL) { + goto EXIT; + } + + p3 = malloc(MEM_LEN); + if (p3 == NULL) { + goto EXIT; + } + + DoSomething(p1, p2, p3); + return 0; // OK. + +EXIT: + if (p3 != NULL) { + free(p3); + } + if (p2 != NULL) { + free(p2); + } + if (p1 != NULL) { + free(p1); + } + return -1; // Failed! +} +``` + +## Type Conversion + +### Rec 8.4 Minimize unnecessary type conversion and forced conversion. + +When the data type is forcibly changed, the meaning of the data and the value after conversion may change. If details are not considered, potential risks may be generated. + +In the following assignment, most compilers do not generate warnings, but the values are slightly changed. + +```c +char ch; +unsigned short int exam; + +ch = -1; +exam = ch; // Bad: Compilers does not generate any warnings. In this case, the value of exam is 0xFFFF. +``` \ No newline at end of file diff --git a/docs-en/contribute/OpenHarmony-cpp-coding-style-guide.md b/docs-en/contribute/OpenHarmony-cpp-coding-style-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..8a7ecbad0ca50c514f6b14380fca47613f505468 --- /dev/null +++ b/docs-en/contribute/OpenHarmony-cpp-coding-style-guide.md @@ -0,0 +1,2821 @@ +# C++ Coding Style Guide + +## Purpose +Rules are not perfect. Disabling useful features in specific situations may affect code implementation. However, the rules are formulated "to help most programmers to get more benefits". If a rule is found unhelpful or difficult to follow in team coding, please send your feedback to us so we can improve the rule accordingly. +Before referring to this guide, you are expected to have the following basic capabilities for C++. It is not for a beginner that wants to learn about C++. +1. Have a general knowledge of ISO standards for C++. +2. Be familiar with the basic features of C++, including those of C++ 03/11/14/17. +3. Have a general knowledge of the C++ Standard Library. + +## General Principles +Code must meet the requirements for readability, maintainability, security, reliability, testability, efficiency, and portability while ensuring functionality correctness. + +## Key Points +1. C++ programming style, such as naming and typesetting. +2. C++ modular design, including how to design header files, classes, interfaces, and functions. +3. Best practices of C++ features, including constants, type casting, resource management, and templates. +4. Best practices of modern C++, including conventions that can improve code maintainability and reliability in C++ 11/14/17. + + +## Conventions +**Rule**: a regulating principle that must be followed during programming. + +**Recommendation**: a guideline that must be considered during programming. + +This document is applicable to standard C++ versions (C++ 03/11/14/17) unless otherwise specified in the rule. + +## Exceptions +It is necessary to understand the reason for each rule or recommendation and to try and comply with them. +However, some rules and recommendations have exceptions. + +The only acceptable exceptions are those that do not violate the general principles and provide appropriate reasons for the exception. +Try to avoid exceptions because they affect the code consistency. Exceptions to 'Rules' should be very rare. + +The style consistency principle is preferred in the following case: +When you modify external open source or third-party code, the existing code specifications prevail. + +# 2 Naming +## General Naming Rules +__CamelCase__ +CamelCase is the practice of writing compound words or phrases so that each word or abbreviation in the phrase begins with a capital letter, with no intervening spaces or punctuation. +There are two conventions: UpperCamelCase and lowerCamelCase. + + +| Type | Naming Style | +| ---------------------------------------- | ---------------------------------------- | +| Class Type, Struct Type, Enumeration Type, and Union Type Definitions, Scope Name | UpperCamelCase | +| Functions (Including Global Functions, Scope Functions, and Member Functions) | UpperCamelCase | +| Global Variables (Including Variables of the Global and Namespace Scopes, Namespace Variables, and Class Static Variables), Local Variables, Function Parameters, and Class, Struct, and Union Member Variables | lowerCamelCase | +| Macro, Constant, Enumerated Value, goto Tag | All caps, separated with underscores (`_`) | + +Note: +**Constant** indicates the variables of the basic, enumeration, or character string type modified by const or constexpr in the global scope, the namespace scope, and the scope of a static member of a class. +**Variable** indicates the variables excluding those defined in **Constant**. These variables use the lowerCamelCase style. + +## File Names +### Recommendation 2.2.1 Use .cpp as the C++ file name extension and .h as the header file name extension. +It is recommended that you use .h as the name extension of a header file so that the header file can be directly compatible with C and C++. +It is recommended that you use .cpp as the name extension of an implementation file. In this way, you can directly distinguish C++ code from C code. + +At present, there are some other file name extensions used by programmers: + +- Header files: .hh, .hpp, .hxx +- Implementation files: .cc, .cxx, .C + +If your project team uses a specific file name extension, you can continue to use it and keep the style consistent. +This document uses .h and .cpp extensions. + + +### Recommendation 2.2.2 Keep C++ file names the same as the class name. +The names of the C++ header file and the C++ implementation file must be the same as the class name. Use the CamelCase or Kernel style and keep the style consistent. + +For example, if there is a class named DatabaseConnection, the corresponding file names are as follows: +- database_connection.h +- database_connection.cpp + +The naming rules of struct, namespace, and enumeration definition files are similar to the rules above. + +## Function Names +Functions are named in UpperCamelCase. Generally, the verb or verb-object structure is used. +```cpp +class List { +public: + void AddElement(const Element& element); + Element GetElement(const unsigned int index) const; + bool IsEmpty() const; +}; + +namespace Utils { + void DeleteUser(); +} +``` + +## Type Names + +Types are named in the UpperCamelCase style. +All types, such as classes, structs, unions, typedefs, and enumerations, use the same conventions. For example: +```cpp +// classes, structs and unions +class UrlTable { ... +class UrlTableTester { ... +struct UrlTableProperties { ... +union Packet { ... + +// typedefs +typedef std::map PropertiesMap; + +// enums +enum UrlTableErrors { ... +``` + +For namespace naming, UpperCamelCase is recommended. +```cpp +// namespace +namespace OsUtils { + +namespace FileUtils { + +} + +} +``` + + +### Recommendation 2.4.1 Do not abuse typedef or #define to alias basic types. +Unless otherwise specified, do not use typedef or #define to redefine a basic value type. +The basic types found in the `` header file are preferable. + +| Signed Type | Unsigned Type | Description | +| ----------- | ------------- | ---------------------------------------- | +| int8_t | uint8_t | The signed or unsigned 8-bit integer type. | +| int16_t | uint16_t | The signed or unsigned 16-bit integer type. | +| int32_t | uint32_t | The signed or unsigned 32-bit integer type. | +| int64_t | uint64_t | The signed or unsigned 64-bit integer type. | +| intptr_t | uintptr_t | The signed or unsigned integer type large enough to hold a pointer. | + + +## Variable Names +General variables are named in lowerCamelCase, including global variables, function parameters, local variables, and member variables. +```cpp +std::string tableName; // Good: Recommended style. +std::string tablename; // Bad: Forbidden style. +std::string path; // Good: When there is only one word, lowerCamelCase (all lowercase) is used. +``` + +### Rule 2.5.1 Add the prefix 'g_' to global variables. Do not add a prefix to a static variable. +Global variables should be used as little as possible, and special attention should be paid to the use of global variables. This prefix highlights global variables so that developers can be more careful when handling them. +- Global static variables and global variables are named in the same way. +- Static variables and common local variables in functions are named in the same way. +- Static member variables and common member variables of classes are named in the same way. + +```cpp +int g_activeConnectCount; + +void Func() +{ + static int packetCount = 0; + ... +} +``` + +### Rule 2.5.2 Name member variables in classes based on the three styles of the lowerCamelCase and maintain a uniform coding style for a product or project. + +```cpp +class Foo { +private: + std::string fileName_; // Add the _ postfix, similar to the K&R naming style. +}; +``` +Use the lowerCamelCase style and do not add prefixes or suffixes to name a member variable of the struct or union type. Keep the naming style consistent with that for a local variable. + +## Macro, Constant, and Enumeration Names +For macros and enumerated values, use all caps separated with underscores (`_`). +In the global scope, constants of named and unnamed namespaces and static member constants should be capitalized and separated with underscores (`_`). Local constants and ordinary const member variables use the lowerCamelCase naming style. + +```cpp +#define MAX(a, b) (((a) < (b))? (b): (a)) // Though examples of Macro names are made, you are not advised to use macros to implement this function. + +enum BaseColor { // Note: Enumerated types are named in the UpperCamelCase style, while their values are all capitalized and separated with underscores (_). + RED, + DARK_RED, + GREEN, + LIGHT_GREEN +}; + +int Func(...) +{ + const unsigned int bufferSize = 100; // Local variable + char *p = new char[bufferSize]; + ... +} + +namespace Utils { + const unsigned int DEFAULT_FILE_SIZE_KB = 200; // Global variable +} + +``` + +# 3 Formatting + +## Line Length + +### Recommendation 3.1.1 Each line of code should contain a maximum of 120 characters. +** Note: **It is recommended that the number of characters in each line not exceed 120. It is recommended that the number of characters in each line not exceed 120. If the line of code exceeds the permitted length, wrap the line appropriately. + +Exception: +- If a one-line comment contains a command or URL of more than 120 characters, you can keep the line for ease in using copy, paste, and search using the grep command. +- The length of an `#include` statement can contain a long path exceeding 120 characters, but this should be avoided if possible. +- The error information in preprocessor directives can exceed the permitted length. + Put the error information of preprocessor directives in one line to facilitate reading and understanding even if the line contains more than 120 characters. +```cpp +#ifndef XXX_YYY_ZZZ +#error Header aaaa/bbbb/cccc/abc.h must only be included after xxxx/yyyy/zzzz/xyz.h, because xxxxxxxxxxxxxxxxxxxxxxxxxxxxx +#endif +``` + +## Indentation + +### Rule 3.2.1 Use spaces to indent and indent 4 spaces at a time. +Only spaces can be used for indentation. Four spaces are indented each time. Do not use the Tab character to indent. +Currently, almost all IDEs support automatic expansion of a Tab to 4 spaces upon pressing the tab key. Please configure your IDE to do so. + +## Braces +### Rule 3.3.1 Use the K&R indentation writing style. +__K&R style__ +When a line break is required, the left brace of a function (excluding the lambda statement) starts a new line. One space should be placed between the statement and the brace. +The right brace starts a new line and nothing else is placed on the line, unless it is followed by the remaining part of the same statement, for example, "while" in the do statement, "else" or "else if" in the if statement, a comma, and a semicolon. + +For example: +```cpp +struct MyType { // Follow the statement to the end, and indent one space. + ... +}; + +int Foo(int a) +{ // The left brace of the function starts a new line, nothing else is placed on the line. + if (...) { + ... + } else { + ... + } +} +``` +The reasons for recommending this style are as follows: + +- Code is more compact. +- Placing the brace at the end of the statement makes the code more continuous in reading rhythm than starting a new line. +- This style complies with mainstream norms and habits of programming languages. +- Most modern IDEs have an automatic code indentation, alignment and display. Placing the brace at the end of a line does not impact understanding. + + +If no function body is inside the braces, the braces can be put on the same line. +```cpp +class MyClass { +public: + MyClass() : value_(0) {} + +private: + int value_; +}; +``` + +## Function Declarations and Definitions + +### Rule 3.4.1 The return type and the function name of a function declaration or definition must be on the same line. When the length of the function parameter list exceeds the permitted length, a line break is required and parameters must be aligned appropriately. +When a function is declared or defined, the return value type of the function should be on the same line as the function name. If the line length permits, the function parameters should be placed on the same line. Otherwise, the function parameters should be wrapped and properly aligned. +The left parenthesis of a parameter list should always be on the same line as the function name. The right parenthesis always follows the last parameter. + +The following is an example of line breaks: +```cpp +ReturnType FunctionName(ArgType paramName1, ArgType paramName2) // Good: All are in the same line. +{ + ... +} + +ReturnType VeryVeryVeryLongFunctionName(ArgType paramName1, // Each added parameter starts on a new line because the line length limit is exceeded. + ArgType paramName2, // Good: aligned with the previous parameter. + ArgType paramName3) +{ + ... +} + +ReturnType LongFunctionName(ArgType paramName1, ArgType paramName2, // Parameters are wrapped because the line length limit is exceeded. + ArgType paramName3, ArgType paramName4, ArgType paramName5) // Good: After the line break, 4 spaces are used for indentation. +{ + ... +} + +ReturnType ReallyReallyReallyReallyLongFunctionName( // The line length cannot accommodate even the first parameter, and a line break is required. + ArgType paramName1, ArgType paramName2, ArgType paramName3) // Good: After the line break, 4 spaces are used for indentation. +{ + ... +} +``` + +## Function Calls +### Rule 3.5.1 A function call parameter list should be placed on one line. When the parameter list exceeds the line length and requires a line break, the parameters should be properly aligned. +A function call parameter list should be placed on one line. When the parameter list exceeds the line length and requires a line break, the parameters should be properly aligned. +The left parenthesis always follows the function name, and the right parenthesis always follows the last parameter. + +The following are examples of proper line breaks: +```cpp +ReturnType result = FunctionName(paramName1, paramName2); // Good: All function parameters are on one line. + +ReturnType result = FunctionName(paramName1, + paramName2, // Good: aligned with the previous parameter + paramName3); + +ReturnType result = FunctionName(paramName1, paramName2, + paramName3, paramName4, paramName5); // Good: Parameters are wrapped. After the line break, 4 spaces are used for indentation. + +ReturnType result = VeryVeryVeryLongFunctionName( // The line length cannot accommodate even the first parameter, and a line break is required. + paramName1, paramName2, paramName3); // After the line break, 4 spaces are used for indentation. +``` + +If some of the parameters called by a function are associated with each other, you can group them for better understanding. +```cpp +// Good: The parameters in each line represent a group of data structures with strong correlation. They are placed on a line for ease of understanding. +int result = DealWithStructureLikeParams(left.x, left.y, // A group of related parameters. + right.x, right.y); // Another group of related parameters. +``` + +## if Statements + +### Rule 3.6.1 Use braces to include an if statement. +We require that all if statements use braces, even if there is only one statement. + +Reasons: +- The logic is intuitive and easy to read. +- It is less prone to mistakes when new code is added to the existing if statement. +- If function-like macros are used in a conditional statement, it is less prone to mistakes (in case the braces are missing when macros are defined). + +```cpp +if (objectIsNotExist) { // Good: Braces are added to a single-line conditional statement. + return CreateNewObject(); +} +``` +### Rule 3.6.2 Place if, else, and else if keywords on separate lines. +If there are multiple branches in a conditional statement, they should be placed on separate lines. + +Good example: + +```cpp +if (someConditions) { + DoSomething(); + ... +} else { // Good: Put the if and else keywords on separate lines. + ... +} +``` + +Bad example: + +```cpp +if (someConditions) { ... } else { ... } // Bad: The if and else keywords are put on the same line. +``` + +## Loop Statements +### Rule 3.7.1 Use braces after loop statements. +Similar to if statements, we require that the for and while loop statements contain braces, even if the loop body is empty or there is only one loop statement. +```cpp +for (int i = 0; i < someRange; i++) { // Good: Braces are used. + DoSomething(); +} +``` +```cpp +while (condition) {} // Good: The while loop body is empty. Braces should be used. +``` +```cpp +while (condition) { + continue; // Good: The continue keyword highlights the end of the empty loop. Braces should be used. +} +``` + +Bad example: +```cpp +for (int i = 0; i < someRange; i++) + DoSomething(); // Bad: Braces are mandatory. +``` +```cpp +while (someCondition) ; // Bad: Using a semicolon here will make people misunderstand that it is a part of the while statement and not the end to it. +``` + +## Switch Statements +### Rule 3.8.1 Indent case and default in a switch statement with four spaces. +The indentation style of the switch statement is as follows: +```cpp +switch (var) { + case 0: // Good: Indented + DoSomething1(); // Good: Indented + break; + case 1: { // Good: Braces are added. + DoSomething2(); + break; + } + default: + break; +} +``` + +```cpp +switch (var) { +case 0: // Bad: case is not indented. + DoSomething(); + break; +default: // Bad: default is not indented. + break; +} +``` + +## Expressions + +### Recommendation 3.9.1 Keep a consistent line break style for expressions and ensure that operators are placed at the end of a line. +A long expression that does not meet the line length requirement must be wrapped appropriately. Generally, the expression is wrapped at an operator of a lower priority or a connector, and the operator or connector is placed at the end of the line. +Placing these at the end of a line indicates that the operation is to be continued on the next line. +For example: + +// Assume that the first line exceeds the length limit. +```cpp +if (currentValue > threshold && // Good: After the line break, the logical-AND operators are placed at the end of the line. + someConditionsion) { + DoSomething(); + ... +} + +int result = reallyReallyLongVariableName1 + // Good + reallyReallyLongVariableName2; +``` +After an expression is wrapped, ensure that the lines are aligned appropriately or indented with 4 spaces. See the following example. + +```cpp +int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + + longVaribleName4 + longVaribleName5 + longVaribleName6; // Good: indented with 4 spaces + +int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 + + longVaribleName4 + longVaribleName5 + longVaribleName6; // Good: The lines are aligned. +``` +## Variable Assignment + +### Rule 3.10.1 Multiple variable definitions and assignment statements cannot be written on one line. +Each line should have only one variable initialization statement. It is easier to read and understand. + +```cpp +int maxCount = 10; +bool isCompleted = false; +``` + +Bad example: + +```cpp +int maxCount = 10; bool isCompleted = false; // Bad: Multiple variable initialization statements must be separated on different lines. Each variable initialization statement occupies one line. +int x, y = 0; // Bad: Multiple variable definitions must be separated on different lines. Each definition occupies one line. + +int pointX; +int pointY; +... +pointX = 1; pointY = 2; // Bad: Multiple variable assignment statements must be separated on different lines. +``` +Exception: Multiple variables can be declared and initialized in the for loop header, if initialization statement (C++17), and structured binding statement (C++17). Multiple variable declarations in these statements have strong associations. Forcible division into multiple lines may cause problems such as scope inconsistency and separation of declaration from initialization. + +## Initialization +Initialization is applicable to structs, unions, and arrays. + +### Rule 3.11.1 When an initialization list is wrapped, ensure that the line after the break is indented and aligned properly. +If a structure or array initialization list is wrapped, the line after the break is indented with four spaces. +Choose the wrap location and alignment style for best comprehension. + +```cpp +const int rank[] = { + 16, 16, 16, 16, 32, 32, 32, 32, + 64, 64, 64, 64, 32, 32, 32, 32 +}; +``` + +## Pointers and References +### Recommendation 3.12.1 The pointer type `*` follows a variable name or type. There can be only one space to the side of it. +Pointer naming: There can be only one space next to `*`. +```cpp +int* p = NULL; // Good +int *p = NULL; // Good + +int*p = NULL; // Bad +int * p = NULL; // Bad +``` + +Exception: When a variable is modified by const or restrict, `*` cannot follow the variable or type. +```cpp +const char * const VERSION = "V100"; +``` + +### Recommendation 3.12.2 The reference type `&` follows a variable name or type. There can be only one space to the side of it. +Reference naming: There can be only one space around `&`. +```cpp +int i = 8; + +int& p = i; // Good +int &p = i; // Good +int*& rp = pi; // Good: The reference type `*&` follows the type. +int *&rp = pi; // Good: The reference type `*&` follows the variable name. +int* &rp = pi; // Good: The pointer type `*` follows the type and the eference type `&` follows the variable name. + +int & p = i; // Bad +int&p = i; // Bad +``` + +## Preprocessor Directives +### Rule 3.13.1 The number sign (#) that starts a preprocessor directive must be at the beginning of the line and can be indented in nested preprocessor directives. +The number sign (#) that starts a preprocessor directive must be at the beginning of the line even through the preprocessor directive is inside a function. + +```cpp +#if defined(__x86_64__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) // Good: "#" is at the beginning of the line. +#define ATOMIC_X86_HAS_CMPXCHG16B 1 // Good: "#" is at the beginning of the line. +#else +#define ATOMIC_X86_HAS_CMPXCHG16B 0 +#endif + + +int FunctionName() +{ + if (someThingError) { + ... +#ifdef HAS_SYSLOG // Good: Even in the function body, "#" is at the beginning of the line. + WriteToSysLog(); +#else + WriteToFileLog(); +#endif + } +} +``` +The nested preprocessor directives starting with `#` can be indented and aligned based on a standardized style. + +```cpp +#if defined(__x86_64__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) + #define ATOMIC_X86_HAS_CMPXCHG16B 1 // Good: wrapped for easier comprehension +#else + #define ATOMIC_X86_HAS_CMPXCHG16B 0 +#endif +``` + +## Whitespace +### Rule 3.14.1 Ensure that horizontal spaces are used to highlight keywords and important information, and avoid unnecessary whitespace. +Horizontal spaces are used to highlight keywords and important information. Spaces are not allowed at the end of each code line. The general rules are as follows: + +- Add spaces after keywords such as if, switch, case, do, while, and for. +- Do not add spaces after the left parenthesis or before the right parenthesis. +- For expressions enclosed by braces, either add a space on either side or avoid a space on either side. +- Do not add spaces after any unary operator (& * + - ~ !). +- Add a space to the left and right sides of each binary operator (= + -< > * /% | & ^ <= >= == !=). +- Add spaces to the left and right sides of a ternary operator (? :). +- Do not add spaces between a prefix or suffix increment (++) or decrement (--) operator and a variable. +- Do not add spaces before or after a struct member operator (. ->). +- Do not add spaces before commas. Add spaces after commas. +- Do not add spaces between a template or type conversion operator (<>) and a type. +- Do not add spaces before or after a domain operator (::). +- Determine whether to add spaces before and after a colon (:) based on the actual situation. + +In normal cases: +```cpp +void Foo(int b) { // Good: A space is added before the left brace. + +int i = 0; // Good: During variable initialization, there should be spaces before and after =. Do not leave a space before the semicolon. + +int buf[BUF_SIZE] = {0}; // Good: Spaces are not added in braces. +``` + +Function definition and call: +```cpp +int result = Foo(arg1,arg2); + ^ // Bad: A space should be added after the comma. + +int result = Foo( arg1, arg2 ); + ^ ^ // Bad: Spaces should not be added after the left parenthesis or before the right parenthesis. +``` + +Pointer and Address Operator +```cpp +x = *p; // Good: There is no space between the operator * and the pointer p. +p = &x; // Good: There is no space between the operator & and the variable x. +x = r.y; // Good: When a member variable is accessed through the operator (.), no space is added. +x = r->y; // Good: When a member variable is accessed through the operator (->), no space is added. +``` + +Other Operators: +```cpp +x = 0; // Good: There is a space before and after the assignment operator (=). +x = -5; // Good: There is no space between the minus sign (–) and the number. +++x; //Good: Do not add spaces between a prefix or suffix increment (++) or decrement (--) operator and a variable.. +x--; + +if (x && !y) // Good: There is a space before and after the Boolean operator. There is no space between the ! operator and the variable. +v = w * x + y / z; // Good: There is a space before and after the binary operator. +v = w * (x + z); // Good: There is no space before or after the expression in the parentheses. + +int a = (x < y) ? x : y; // Good: Ternary operator. There is a space before and after ? and : +``` + +Loops and Conditional Statements: +```cpp +if (condition) { // Good: There is a space between the if keyword and the left parenthesis, and no space before or after the conditional statement in the parentheses. + ... +} else { // Good: There is a space between the else keyword and the left brace. + ... +} + +while (conditions) {} // Good: There is a space between the while keyword and the left parenthesis. There is no space before or after the conditional statement in the parentheses. + +for (int i = 0; i < someRange; ++i) { // Good: There is a space between the for keyword and the left parenthesis, and after the semicolon. + ... +} + +switch (condition) { // Good: There is a space after the switch keyword. + case 0: // Good: There is no space between the case condition and the colon. + ... + break; + ... + default: + ... + break; +} +``` + +Templates and Conversions +```cpp +// Angle brackets (< and >) are not adjacent to space. There is no space before < or between > and (. +vector x; +y = static_cast(x); + +// There can be a space between the type and the pointer operator. Keep the spacing style consistent. +vector x; +``` + +Scope Operators +```cpp +std::cout; // Good: Namespace access. Do not leave spaces. + +int MyClass::GetValue() const {} // Good: Do not leave spaces in the definition of member functions. +``` + +Colons +```cpp +// Scenarios when space is required + +// Good: // Add a space before or after the colon in a derived class definition. +class Sub : public Base { + +}; + +// Add a space before or after the colon in the initialization list of a constructor function. +MyClass::MyClass(int var) : someVar_(var) +{ + DoSomething(); +} + +// Add a space before or after the colon in a bit-field. +struct XX { + char a : 4; + char b : 5; + char c : 4; +}; +``` + +```cpp +// Scenarios when space is not required + +// Good: // No space is added before or after the colon next to a class access permission (public or private). +class MyClass { +public: + MyClass(int var); +private: + int someVar_; +}; + +// No space is added before or after the colon in a switch statement. +switch (value) +{ + case 1: + DoSomething(); + break; + default: + break; +} +``` + +Note: Currently, all IDEs support automatic deletion of spaces at the end of a line. Please configure your IDE correctly. + +### Recommendation 3.14.1 Use blank lines only when necessary to keep code compact. + +There must be as few blank lines as possible so that more code can be displayed for easy reading. Recommendations: +- Add blank lines according to the correlation between lines. +- Consecutive blank lines are not allowed inside functions, type definitions, macros, and initialization expressions. +- A maximum of **two** consecutive blank lines can be used. +- Do not add blank lines on the first and last lines of a code block in braces. This recommendation is not applicable to code block in braces of a namespace. + +```cpp +int Foo() +{ + ... +} + + + +int bar() {// Bad: More than one blank lines are used between two function definitions. +{ + ... +} + + +if (...) { + // Bad: Do not add blank lines on the first and last lines of a code block. + ... + // Bad: Do not add blank lines on the first and last lines of a code block. +} + +int Foo(...) +{ + // Bad: Do not add blank lines before the first statement in a function body. + ... +} +``` + +## Classes +### Rule 3.15.1 Class access specifier declarations are in the sequence: public, protected, private. Indent these specifiers to the same level as the class keyword. +```cpp +class MyClass : public BaseClass { +public: // Not indented. + MyClass(); // Indented with 4 spaces. + explicit MyClass(int var); + ~MyClass() {} + + void SomeFunction(); + void SomeFunctionThatDoesNothing() + { + } + + void SetVar(int var) { someVar_ = var; } + int GetVar() const { return someVar_; } + +private: + bool SomeInternalFunction(); + + int someVar_; + int someOtherVar_; +}; +``` + +In each part, it is recommended that similar statements be put together and in the following order: Type (including typedef, using, nested structs and classes), Constant, Factory Function, Constructor, Assignment Operator, Destructor, Other Member Function, and Data Member + + +### Rule 3.15.2 The constructor initialization list must be on the same line or wrapped and aligned with four spaces of indentation. +```cpp +// If all variables can be placed on the same line +MyClass::MyClass(int var) : someVar_(var) +{ + DoSomething(); +} + +// If the variables cannot be placed on the same line +// Wrapped at the colon and indented with four spaces +MyClass::MyClass(int var) + : someVar_(var), someOtherVar_(var + 1) // Good: Add a space after the comma. +{ + DoSomething(); +} + +// If an initialization list needs to be placed in multiple lines, put each member on a separate line and align between lines. +MyClass::MyClass(int var) + someVar(var), // Four spaces of indentation. + someOtherVar_(var + 1) +{ + DoSomething(); +} +``` + +# 4 Comments +Generally, clear architecture and good naming are recommended to improve code readability, and comments are provided only when necessary. +Comments are used to help readers quickly understand code. Therefore, **comments should be provided for the sake of readers**. + +Comments must be concise, clear, and unambiguous, ensuring that information is complete and not redundant. + +**Comments are as important as code**. +When writing a comment, you need to step into the reader's shoes and use comments to express what the reader really needs. Comments are used to express the function and intention of code, rather than repeating code. +When modifying the code, ensure that the comments are consistent with the modified code. It is not polite to modify only code and keep the old comments, which will undermine the consistency between code and comments, and may confuse or even mislead readers. + +Comments should be made in English. + +## Comment Style + +In C++ code, both ` /* */` and ` // ` can be used for commenting. +Comments can be classified into different types, such as file header comments, function header comments, and code comments. This is based on their purposes and positions. +Comments of the same type must keep a consistent style. + +Note: Example code in this document uses comments in the `//` format only to better describe the rules and recommendations. This does not mean this comment format is better. + +## File Header Comments +### Rule 4.2.1 File header comments must contain the copyright notice. + +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +## Function Header Comments +### Rule 4.3.1 Function header comments with no content are forbidden. +Not all functions need function header comments. +For information that cannot be described by function signatures, add function header comments. + +Function header comments are placed above the function declaration or definition. Use one of the following styles: +Use '//' to start the function header. + +```cpp +// Single-line function header +int Func1(void); + +// Multi-line function header +// Second line +int Func2(void); +``` + +Use `/* */` to start the function header. +```cpp +/* Single-line function header */ +int Func1(void); + +/* + * Another single-line function header + */ +int Func2(void); + +/* + * Multi-line function header + * Second line + */ +int Func3(void); +``` +Use function names to describe functions, and add function header comments if necessary. +Do not write useless or redundant function headers. Do not write empty function headers with no content. + +The function header comment content will depend on the function and includes but is not limited to: a function description, return value, performance constraints, usage comments, memory conventions, algorithm implementation, reentering requirements. +In the function interface declaration in the external header file, the function header comment should clearly describe important and useful information. + +Good example: + +```cpp +/* + * The number of written bytes is returned. If -1 is returned, the write operation failed. + * Note that the memory buffer should be released by the caller. + */ +int WriteString(const char *buf, int len); +``` + +Bad example: +```cpp +/* + * Function name: WriteString + * Function: Write a character string. + * Parameters: + * Return value: + */ +int WriteString(const char *buf, int len); +``` +Problems: + +- The 'Parameters' and 'Return value' have no content. +- The function name is redundant. +- The most import thing, that is, who needs to release the buffer, is not clearly stated. + +## Code Comments +### Rule 4.4.1 Code comments are placed above or on the right of the corresponding code. +### Rule 4.4.2 There must be a space between the comment character and the comment content. At least one space is required between the comment and code if the comment is placed to the right of code. +Comments placed above code should be indented the same as that of the code. +Use one of the following styles: +Use "//". +```cpp + +// Single-line comment +DoSomething(); + +// Multi-line comment +// Second line +DoSomething(); +``` + +Use `/* */`. +```cpp +/* Single-line comment */ +DoSomething(); + +/* + * Multi-line comment in another mode + * Second line + */ +DoSomething(); +``` +Leave at least one space between the code and the comment on the right. It is recommended that no more than four spaces be left. +You can use the Tab key to indent 1–4 spaces. + +Select and use one of the following styles: + +```cpp +int foo = 100; // Comment on the right +int bar = 200; /* Comment on the right */ +``` +It is more appealing sometimes when the comment is placed on the right of code and the comments and code are aligned vertically. +After the alignment, ensure that the comment is 1–4 spaces away from the widest line of code. +For example: + +```cpp +const int A_CONST = 100; /* Related comments of the same type can be aligned vertically. */ +const int ANOTHER_CONST = 200; /* Leave spaces after code to align comments vertically. */ +``` +When the comment on the right exceeds the line width, consider placing the comment above the code. + +### Rule 4.4.3 Delete unused code segments. Do not comment them out. +Code that is commented out cannot be maintained. If you attempt to restore the code, it is very likely to introduce ignorable defects. +The correct method is to delete unnecessary code directly. If necessary, consider porting or rewriting the code. + +Here, commenting out refers to the removal of code from compilation without actually deleting it. This is done by using /* */, //, #if 0, #ifdef NEVER_DEFINED, and so on. + +### Recommendation 4.4.1 Delivered code cannot contain a TODO/TBD/FIXME comment. +TODO/TBD comments are used to describe required improvements and supplements. +FIXME comments are used to describe defects that need fixing. +They should have a standardized style, which facilitates text search. For example: + +```cpp +// TODO(): XX +// FIXME: XX +``` + + +# 5 Header Files +## Header File Responsibility +A header file is an external interface of a module or file. The design of a header file shows most of the system design. +The interface declaration for most functions is more suitable placed in the header file, but implementation (except inline functions) cannot be placed in the header file. Functions, macros, enumerations, and structure definitions that need to be used in .cpp files cannot be placed in the header file. +The header responsibility should be simple. An overly complex header file will make dependencies complex and cause long compilation times. + +### Recommendation 5.1.1 Each .cpp file should have a .h file with the same name. It should be used to declare the classes and interfaces that need to be exposed externally. +Generally, each .cpp file has a corresponding .h file. This .cpp file is used to store the function declarations, macro definitions, and class definitions that are to be exposed. +If a .cpp file does not need to open any interface externally, it should not exist. +Exception: __An entry point (for example, the file where the main function is located), unit tests, and dynamic libraries __ + +For example: +```cpp +// Foo.h + +#ifndef FOO_H +#define FOO_H + +class Foo { +public: + Foo(); + void Fun(); + +private: + int value_; +}; + +#endif +``` + +```cpp +// Foo.cpp +#include "Foo.h" + +namespace { // Good: The declaration of the internal function is placed in the header of the .cpp file, and has been limited to the unnamed namespace or static scope. + void Bar() + { + } +} + +... + +void Foo::Fun() +{ + Bar(); +} +``` + +## Header File Dependency +### Rule 5.2.1 Header file cyclic dependency is forbidden. +An example of cyclic dependency (also known as circular dependency) is: a.h contains b.h, b.h contains c.h, and c.h contains a.h. If any of these header files is modified, all code containing a.h, b.h, and c.h needs to be recompiled. +For a unidirectional dependency, for example if: a.h contains b.h, b.h contains c.h, and c.h does not contain any header file, modifying a.h does not mean that we need to recompile the source code for b.h or c.h. + +The cyclic dependency of header files reflects an obviously unreasonable architecture design, which can be avoided through optimization. + + +### Rule 5.2.2 Header files should have #define guards to prevent multiple inclusion. +To prevent header files from being included multiple times, all header files should be protected by #define. Do not use #pragma once. + +When defining a protection character, comply with the following rules: +1) The protection character uses a unique name. +2) Do not place code or comments (except for file header comments) before or after the protected part. + +Example: Assume that the timer.h file of the timer module is in the timer/include/timer.h directory. Perform the following operations to safeguard the timer.h file: + +```cpp +#ifndef TIMER_INCLUDE_TIMER_H +#define TIMER_INCLUDE_TIMER_H +... +#endif +``` + +### Rule 5.2.3 It is prohibited to reference external function interfaces and variables in extern declaration mode. +Interfaces provided by other modules or files can be used only by including header files. +Using external function interfaces and variables in extern declaration mode may cause inconsistency between declarations and definitions when external interfaces are changed. +In addition, this kind of implicit dependency may cause architecture corruption. + +Cases that do not comply with specifications: + +// a.cpp content +```cpp +extern int Fun(); // Bad: Use external functions in extern mode. + +void Bar() +{ + int i = Fun(); + ... +} +``` + +// b.cpp content +```cpp +int Fun() +{ + // Do something +} +``` +Should be changed to: + +// a.cpp content +```cpp +#include "b.h" // Good: Use the interface provided by other .cpp by including its corresponding header file. + +void Bar() +{ + int i = Fun(); + ... +} +``` + +// b.h content +```cpp +int Fun(); +``` + +// b.cpp content +```cpp +int Fun() +{ + // Do something +} +``` +In some scenarios, if internal functions need to be referenced with no intrusion to the code, the extern declaration mode can be used. +For example: +When performing unit testing on an internal function, you can use the extern declaration to reference the tested function. +When a function needs to be stubbed or patched, the function can be declared using extern. + +### Rule 5.2.4 Do not include header files in extern "C". +If a header file is included in extern "C", extern "C" may be nested. Some compilers restrict the nesting of extern "C". If there are too many nested layers, compilation errors may occur. + +When C and C++ programmings are used together and if extern "C" includes a header file, the original intent behind the header file may be hindered. For example, when the link specifications are modified incorrectly. + +For example, assume that there are two header files a.h and b.h. + +// a.h content +```cpp +... +#ifdef __cplusplus +void Foo(int); +#define A(value) Foo(value) +#else +void A(int) +#endif +``` +// b.h content +```cpp +... +#ifdef __cplusplus +extern "C" { +#endif + +#include "a.h" +void B(); + +#ifdef __cplusplus +} +#endif +``` + +Using the C++ preprocessor to expand b.h, the following information is displayed: +```cpp +extern "C" { + void Foo(int); + void B(); +} +``` + +According to the author of a.h, the function Foo is a C++ free function following the "C++" link specification. +However, because `#include "a.h"` is placed inside `extern "C"` in b.h, the link specification of function Foo is changed incorrectly. + +Exception: +In the C++ compilation environment, if you want to reference the header file of pure C, the C header files should not contain `extern "C"`. The non-intrusive approach is to include the C header file in `extern "C"`. + +### Recommendation 5.2.1 Use `#include` instead of a forward declaration to include header files. +A forward declaration is for the declaration of classes, functions, and templates and is not meant for its definition. + +- Advantages: + 1. Forward declarations can save compilation time. Redundant `#include `statements force the compiler to expand more files and process more input. + 2. Forward declarations can save unnecessary recompilation time. The use of #include will force your code to be recompiled multiple times due to unrelated changes in header files. +- Disadvantages: + 1. Forward declarations hide dependency relationship. When a header file is modified, user code will skip the necessary recompilation process. + 2. A forward declaration may be broken by subsequent changes to the library. Forward declarations of functions and templates sometimes prevent header file developers from changing APIs. For example, widening a formal parameter type, adding a template parameter with a default value, and so on. + 3. Forward declaration of symbols from the namespace `std::` is seen as undefined behavior (as specified in the C++ 11 standard specification). + 4. Forward declaration of multiple symbols from a header file can be more verbose than simply including (#include) the header. + 5. Structuring code only for forward declaration (for example, using pointer members instead of object members) can make the code more complex and slower. + 6. It is difficult to determine whether a forward declaration or `#include` is needed. In some scenarios, replacing `#include` with a forward declaration may cause unexpected results. + +Therefore, we should avoid using forward declarations as much as possible. Instead, we use the #include statement to include a header file and ensure dependency. + +# 6 Scopes + +## Namespaces + +### Recommendation 6.1.1 For code that does not need to be exported from the .cpp file, you are advised to use an unnamed namespace for encapsulation or use static to modify the variables, constants, or functions that need hiding. +In the C++ 2003 standard, using static to modify the external availability of functions and variables was marked as deprecated. Therefore, unnamed namespaces are the recommended method. + +Main Reasons: +1. There are too many meanings for static in C++: static function member variable, static member function, static global variable, and static function local variable. Each of them has special processing. +2. Static can only be used to define variables, constants, and functions that are not referenced outside the current .cpp file, while namespaces can also be used to encapsulate types. +3. Use a namespace to control the scope instead of using both static and namespaces. +4. Unnamed namespaces can be used to instantiate templates rather than functions modified by the static keyword. + +Do not use unnamed namespaces or static in header files. + +```cpp +// Foo.cpp + +namespace { + const int MAX_COUNT = 20; + void InternalFun(){}; +} + +void Foo::Fun() +{ + int i = MAX_COUNT; + + InternalFun(); +} + +``` + +### Rule 6.1.1 Do not use "using" to import namespace in a header file or before #include statements. +Note: Using "using" to import namespace will affect any subsequent code and may cause symbol conflicts. +Example: + +```cpp +// Header file a.h +namespace NamespaceA { + int Fun(int); +} +``` + +```cpp +// Header file b.h +namespace NamespaceB { + int Fun(int); +} + +using namespace NamespaceB; + +void G() +{ + Fun(1); +} +``` + +```cpp +// Source code a.cpp +#include "a.h" +using namespace NamespaceA; +#include "b.h" + +void main() +{ + G(); // "using namespace NamespaceA" before #include "b.h", will cause conflicts when calling NamespaceA::Fun and NamespaceB::Fun. +} +``` + +Using "using" to import a symbol or define an alias in a header file is allowed in customized namespaces of modules, but is prohibited in the global namespace. +```cpp +// foo.h + +#include +using fancy::string; // Bad: It is prohibited to import symbols to the global namespace. + +namespace Foo { + using fancy::string; // Good: Symbols can be imported in customized namespaces of modules. + using MyVector = fancy::vector; // Good: In C++11, aliases can be defined in customized namespaces. +} +``` + + +## Global Functions and Static Member Functions + +### Recommendation 6.2.1 Use namespaces to manage global functions. If global functions are closely tied to a class, you can use static member functions. +Note: Placing non-member functions in a namespace avoids polluting the global scope. Do not use "class + static member function" to simply manage global functions. If a global function is closely tied to a class, it can be used as a static member function of the class. + +If you need to define some global functions for a .cpp file, use unnamed namespaces for management. +```cpp +namespace MyNamespace { + int Add(int a, int b); +} + +class File { +public: + static File CreateTempFile(const std::string& fileName); +}; +``` + +## Global Constants and Static Member Constants + +### Recommendation 6.3.1 Use namespaces to manage global constants. If global constants are closely tied to a class, you can use static member constants. +Note: Placing global constants in a namespace avoids polluting the global scope. Do not use "class + static member constant" to simply manage global constants. If a global constant is closely tied to a class, it can be used as a static member constant of the class. + +If you need to define some global constants only for a .cpp file, use unnamed namespaces for management. +```cpp +namespace MyNamespace { + const int MAX_SIZE = 100; +} + +class File { +public: + static const std::string SEPARATOR; +}; +``` + +## Global Variables + +### Recommendation 6.4.1 Do not use global variables. Use the singleton pattern instead. +Note: Global variables can be modified and read, which results in data coupling between production code and the global variables. +```cpp +int g_counter = 0; + +// a.cpp +g_counter++; + +// b.cpp +g_counter++; + +// c.cpp +cout << g_counter << endl; +``` + +Singleton +```cpp +class Counter { +public: + static Counter& GetInstance() + { + static Counter counter; + return counter; + } // Simple example of a singleton implementation + + void Increase() + { + value_++; + } + + void Print() const + { + std::cout << value_ << std::endl; + } + +private: + Counter() : value_(0) {} + +private: + int value_; +}; + +// a.cpp +Counter::GetInstance().Increase(); + +// b.cpp +Counter::GetInstance().Increase(); + +// c.cpp +Counter::GetInstance().Print(); +``` + +After the singleton is implemented, there is a unique global instance, which can functions as a global variable. However, the singleton provides better encapsulation. + +Exception: In some cases, the scope of a global variable is contained inside a module. Multiple instances of the same global variable may exist, and each module holds one copy. In this case, a singleton cannot be used as it is limited to one instance. + +# 7 Classes + +## Constructors, Copy/Move Constructors, Copy/Move Assignment Operators, and Destructors +Constructors, copy/move constructors, copy/move assignment operators, and destructors provide lifetime management methods for objects. +- Constructor: `X()` +- Copy constructor: `X(const X&)` +- Copy assignment operator: `operator=(const X&)` +- Move constructor: `X (X&&)` *Provided in versions later than C++ 11*. +- Move assignment operator: `operator=(X&&)` *Provided in versions later than C++ 11*. +- Destructor: `~X()` + +### Rule 7.1.1 The member variables of a class must be initialized explicitly. +Note: If a class has members but no constructor and a default constructor is defined, the compiler will automatically generate a constructor, but it will not initialize member variables. The content of each object is uncertain. + +Exception: +- If the member variables in a class have a default constructor, explicit initialization is not required. + +Example: The following code has no constructor, and private data members cannot be initialized: +```cpp +class Message { +public: + void ProcessOutMsg() + { + //… + } + +private: + unsigned int msgID_; + unsigned int msgLength_; + unsigned char* msgBuffer_; + std::string someIdentifier_; +}; + +Message message; // The message member is not initialized. +message.ProcessOutMsg(); // Potential risks exist in subsequent use. + +// Therefore, it is necessary to define a default constructor as follows: +class Message { +public: + Message() : msgID_(0), msgLength_(0), msgBuffer_(NULL) + { + } + + void ProcessOutMsg() + { + // … + } + +private: + unsigned int msgID_; + unsigned int msgLength_; + unsigned char* msgBuffer_; + std::string someIdentifier; // The member variable has a default constructor. Therefore, explicit initialization is not required. +}; +``` + +### Recommendation 7.1.1 Initialization during declaration (C++ 11) and initialization using the constructor initialization list are preferred for member variables. +Note: Initialization during declaration (C++11) is preferred because initialized values of member variables can be easily understood. If initialized values of certain member variables are relevant to constructors, or C++ 11 is not supported, the constructor initialization list is used preferentially to initialize these member variables. Compared with the assignment statements in constructors, code of the constructor initialization list is simpler and has higher performance, and can be used to initialize constant and reference members. + +```cpp +class Message { +public: + Message() : msgLength(0) { // Good: The constructor initialization list is preferred. + { + msgBuffer = NULL; // Bad: Values cannot be assigned in constructors. + } + +private: + unsigned int msgID{0}; // Good: Used in C++11. + unsigned int msgLength_; + unsigned char* msgBuffer_; +}; +``` + +### Rule 7.1.2 Declare single-parameter constructors as explicit to prevent implicit conversion. +Note: If a single-parameter constructor is not declared as explicit, it will become an implicit conversion function. +Example: + +```cpp +class Foo { +public: + explicit Foo(const string& name): name_(name) + { + } +private: + string name_; +}; + + +void ProcessFoo(const Foo& foo){} + +int main(void) +{ + std::string test = "test"; + ProcessFoo(test); // Compiling failed. + return 0; +} +``` + +The preceding code fails to be compiled because the parameter required by `ProcessFoo` is of the Foo type, which mismatch with the input string type. + +If the explicit keyword of the Foo constructor is removed, implicit conversion is triggered and a temporary Foo object is generated when `ProcessFoo` is called with the string parameter. Usually, this implicit conversion is confusing and bugs are apt to be hidden, due to unexpected type conversion. Therefore, single-parameter constructors require explicit declaration. + +### Rule 7.1.3 If copy/move constructors and copy/move assignment operators are not needed, clearly prohibit them. +Note: If users do not define it, the compiler will generate copy constructors and copy assignment operators, move constructors and move assignment operators (move semantic functions will be available in versions later than C++ 11). +If we do not use copy constructors or copy assignment operators, explicitly delete them. + +1. Set copy constructors or copy assignment operators to private and do not implement them. +```cpp +class Foo { +private: + Foo(const Foo&); + Foo& operator=(const Foo&); +}; +``` +2. Use delete provided by C++ 11. For details, see Rule 10.1.3 in chapter 10 Modern C++ Features. + +### Rule 7.1.4 Copy constructors and copy assignment operators should be implemented or forbidden together. +Both copy constructors and copy assignment operators provide copy semantics. They should be implemented or hidden together. + +```cpp +// The copy constructor and the copy assignment operator are implemented together. +class Foo { +public: + ... + Foo(const Foo&); + Foo& operator=(const Foo&); + ... +}; + +// The copy constructor and the copy assignment operator are both set to default, as supported by C++ 11. +class Foo { +public: + Foo(const Foo&) = default; + Foo& operator=(const Foo&) = default; +}; + +// The copy constructor and the copy assignment operator are hidden together. You should use the delete keyword if C++11 features are available. +class Foo { +private: + Foo(const Foo&); + Foo& operator=(const Foo&); +}; +``` + +### Rule 7.1.5 Move constructors and move assignment operators should be implemented or hidden together. +The move operation is added in C++ 11. If a class is required to support the move operation, move constructors and move assignment operators need to be implemented. + +Both move constructors and move assignment operators provide move semantics. They should be implemented or hidden together. +```cpp +// The copy constructor and the copy assignment operator are implemented together. +class Foo { +public: + ... + Foo(Foo&&); + Foo& operator=(Foo&&); + ... +}; + +// The copy constructor and the copy assignment operator are both set to default, as supported by C++ 11. +class Foo { +public: + Foo(Foo&&) = default; + Foo& operator=(Foo&&) = default; +}; + +// The copy constructor and the copy assignment operator are hidden together. You should use the delete keyword if C++11 features are available. +class Foo { +public: + Foo(Foo&&) = delete; + Foo& operator=(Foo&&) = delete; +}; +``` + +### Rule 7.1.6 It is prohibited to call virtual functions in constructors and destructors. +Note: Calling a virtual function of the current object in a constructor or destructor will cause behaviors of non-polymorphism. +In C++, a base class constructs only one complete object at a time. + +Example: Base indicates the base class, and Sub indicates the derived class. +```cpp +class Base { +public: + Base(); + virtual void Log() = 0; // Different derived classes call different log files. +}; + +Base::Base() // Base class constructor +{ + Log(); // Call the virtual function log. +} + +class Sub : public Base { +public: + virtual void Log(); +}; +``` + +When running the following statement: +`Sub sub;` +The constructor of the derived class is executed first. However, the constructor of the base class is called first. Because the constructor of the base class calls the virtual function log, the log is in the base class version. The derived class is constructed only after the base class is constructed. As a result, behaviors of non-polymorphism are caused. +This also applies to destructors. + + +## Inheritance + +### Rule 7.2.1 Destructors of a base class should be declared as virtual. +Note: Destructors of the derived class can be called during polymorphism invocation only when destructors of the base class are virtual. + +Example: There will be memory leak if destructors of the base class are not declared as virtual. +```cpp +class Base { +public: + virtual std::string getVersion() = 0; + + ~Base() + { + std::cout << "~Base" << std::endl; + } +}; +``` + +```cpp +class Sub : public Base { +public: + Sub() : numbers_(NULL) + { + } + + ~Sub() + { + delete[] numbers_; + std::cout << "~Sub" << std::endl; + } + + int Init() + { + const size_t numberCount = 100; + numbers_ = new (std::nothrow) int[numberCount]; + if (numbers_ == NULL) { + return -1; + } + + ... + } + + std::string getVersion() + { + return std::string("hello!"); + } +private: + int* numbers_; +}; +``` + +```cpp +int main(int argc, char* args[]) +{ + Base* b = new Sub(); + + delete b; + return 0; +} +``` +Because destructors of the base class are not declared as virtual, only destructors of the base class are called when an object is destroyed. Destructors of the derived class Sub are not called. As a result, a memory leak occurs. + + +### Rule 7.2.2 Do not use default parameter values for virtual functions. +Note: In C++, virtual functions are dynamically bound, but the default parameters of functions are statically bound during compilation. This means that the function you finally execute is a virtual function that is defined in the derived class but uses the default parameter value in the base class. To avoid confusion and other problems caused by inconsistent default parameter declarations during overriding of virtual functions, it is prohibited to declare default parameter values for all virtual functions. +Example: The default value of parameter "text" of the virtual function "Display" is determined at compilation time instead of runtime, which does not fit with polymorphism. +```cpp +class Base { +public: + virtual void Display(const std::string& text = "Base!") + { + std::cout << text << std::endl; + } + + virtual ~Base(){} +}; + +class Sub : public Base { +public: + virtual void Display(const std::string& text = "Sub!") + { + std::cout << text << std::endl; + } + + virtual ~Sub(){} +}; + +int main() +{ + Base* base = new Sub(); + Sub* sub = new Sub(); + + ... + + base->Display(); // The program output is as follows: Base! The expected output is as follows: Sub! + sub->Display(); // The program output is as follows: Sub! + + delete base; + delete sub; + return 0; +}; +``` + +### Rule 7.2.3 Do not redefine inherited non-virtual functions. +Note: Non-virtual functions cannot be dynamically bound (only virtual functions can be dynamically bound). You can obtain the correct result by operating on the pointer of the base class. + +Example: +```cpp +class Base { +public: + void Fun(); +}; + +class Sub : public Base { +public: + void Fun(); +}; + +Sub* sub = new Sub(); +Base* base = sub; + +sub->Fun(); // Call Fun of the derived class. +base->Fun(); // Call Fun of the base class. +//... + +``` + +## Multiple Inheritance +In the actual development process, multiple inheritance is seldom used because the following typical problems may occur: +1. Data duplication and name ambiguity caused by "diamond" inheritance. C++ introduces virtual inheritance to solve these problems. +2. In addition to "diamond" inheritance, names of multiple base classes may also conflict with each other, resulting in name ambiguity. +3. If a derived class needs to be extended or needs to override methods of multiple base classes, the responsibilities of the derived classes are unclear and semantics are muddled. +4. Compared with delegation, inheritance is seen as white box reuse, that is, a derived class can access the protected members of the base class, which leads to more coupling. Multiple inheritance, due to the coupling of multiple base classes, leads to even more coupling. + +Multiple inheritance has the following advantages: +Multiple inheritance provides a simpler method for assembling and reusing multiple interfaces or classes. + +Therefore, multiple inheritance can be used only in the following cases: + +### Recommendation 7.3.1 Use multiple inheritance to implement interface separation and multi-role combination. +If a class requires multiple interfaces, combine multiple separated interfaces by using multiple inheritance. This is similar to the Traits mixin of the Scala language. + +```cpp +class Role1 {}; +class Role2 {}; +class Role3 {}; + +class Object1 : public Role1, public Role2 { + // ... +}; + +class Object2 : public Role2, public Role3 { + // ... +}; + +``` + +The C++ standard library has a similar implementation example: +```cpp +class basic_istream {}; +class basic_ostream {}; + +class basic_iostream : public basic_istream, public basic_ostream { + +}; +``` + +## Overloading + +Overload operators should be used when there are sufficient reasons, and they do not change the original perception of the operators. For example, do not use the plus sign (+) to perform subtraction. +Operator overloading can make code more intuitive but has some disadvantages: +- It is often mistaken that the operation is as fast as a built-in operator, which has no performance degradation. +- There is no naming to aid debugging. It is more convenient to search by function name than by operator. +- Overloading operators can cause confusion if behavior definitions are not intuitive (for example, if the "+" operator is used for subtraction). +- The implicit conversion caused by the overloading of assignment operators may lead to entrenched bugs. Functions such as Equals () and CopyFrom () can be defined to replace the = and == operators. + + + +# 8 Functions +## Function Design +### Rule 8.1.1 Avoid long functions and ensure that each function contains no more than 50 lines (non-null and non-comment). +A function should be displayed on one screen (no longer than 50 lines). It should do only one thing, and do it well. + +Long functions often mean that the functions are too complex to implement in more than one function, or overly detailed but not further abstracted. + +Exception: Some algorithms may be longer than 50 lines due to algorithm convergence and functional comprehensiveness. + +Even if a long function works very well now, once someone modifies it, new problems may occur. It might even cause bugs that are difficult to find. +It is recommended that you split a long function into several functions that are simpler and easier to manage, facilitating comprehension and modification. + +## Inline Functions + +### Recommendation 8.2.1 An inline function cannot exceed 10 lines. +**Note**: An inline function has the same characteristics of a normal function. The difference between an inline function and a normal function lies in the processing of function calls. When a general function is called, the program execution right is transferred to the called function, and then returned to the function that calls it. When an inline function is called, the invocation expression is replaced with an inline function body. + +Inline functions are only suitable for small functions with only 1-10 lines. For a large function that contains many statements, the function call and return overheads are relatively trivial and do not need the help of an inline function. Most compilers may abandon the inline mode and use the common method to call the function. + +If an inline function contains complex control structures, such as loop, branch (switch), and try-catch statements, the compiler may regard the function as a common function. +**Virtual functions and recursive functions cannot be used as inline functions**. + +## Function Parameters + +### Recommendation 8.3.1 Use a reference instead of a pointer for function parameters. + +**Note**: A reference is more secure than a pointer because it is not empty and does not point to other targets. Using a reference stops the need to check for illegal null pointers. + +If a product is being developed for an older platform, the processing used by the old platform is preferred. +Use const to avoid parameter modification, so that readers can clearly know that a parameter is not going to be modified. This greatly enhances code readability. + +Exception: When the input parameter is an array with an unknown compile-time length, you can use a pointer instead of a reference. + +### Recommendation 8.3.2 Use strongly typed parameters. Do not use void*. +While different languages have their own views on strong typing and weak typing, it is generally believed that C and C++ are strongly typed languages. Since we use such a strongly typed language, we should keep to this style. +An advantage of this is the compiler can find type mismatch problems at the compilation stage. + +Using strong typing helps the compiler find more errors for us. Pay attention to the usage of the FooListAddNode function in the following code: +```cpp +struct FooNode { + struct List link; + int foo; +}; + +struct BarNode { + struct List link; + int bar; +} + +void FooListAddNode(void *node) // Bad: Here, the void * type is used to transfer parameters. +{ + FooNode *foo = (FooNode *)node; + ListAppend(&g_FooList, &foo->link); +} + +void MakeTheList() +{ + FooNode *foo = NULL; + BarNode *bar = NULL; + ... + + FooListAddNode(bar); // Wrong: In this example, the foo parameter was supposed to be transferred, but the bar parameter is accidentally transferred instead. However, no error is reported. +} +``` + +1. You can use a template function to change the parameter type. +2. A base class pointer can be used to implement this according to polymorphism. + +### Recommendation 8.3.3 A function can have a maximum of five parameters. +If a function has too many parameters, it is apt to be affected by external changes, and therefore maintenance is affected. Too many function parameters will also increase the testing workload. + +If a function has more than five parameters, you can: +- Split the function. +- Combine related parameters into a struct. + +# 9 Other C++ Features + +## Constants and Initialization + +Unchanged values are easier to understand, trace, and analyze. Therefore, use constants instead of variables as much as possible. When defining values, use const as a default. + +### Rule 9.1.1 Do not use macros to replace constants. + +**Note**: Macros are a simple text replacement that is completed in the preprocessing phase. When an error is reported, the corresponding value is reported. During tracing and debugging, the value is also displayed instead of the macro name. A macro does not support type checking and is insecure. A macro has no scope. + +```cpp +#define MAX_MSISDN_LEN 20 // Bad + +// Use const in C++. +const int MAX_MSISDN_LEN = 20; // Good + +// In versions later than C++ 11, constexpr can be used. +constexpr int MAX_MSISDN_LEN = 20; +``` + +### Recommendation 9.1.1 A group of related integer constants must be defined as an enumeration. + +**Note**: Enumerations are more secure than `#define` or `const int`. The compiler checks whether a parameter value is within the enumerated value range to avoid errors. + +```cpp +// Good example: +enum Week { + SUNDAY, + MONDAY, + TUESDAY, + WEDNESDAY, + THURSDAY, + FRIDAY, + SATURDAY +}; + +enum Color { + RED, + BLACK, + BLUE +}; + +void ColorizeCalendar(Week today, Color color); + +ColorizeCalendar(BLUE, SUNDAY); // Compilation error. The parameter type is incorrect. + +// Bad example: +const int SUNDAY = 0; +const int MONDAY = 1; + +const int BLACK = 0; +const int BLUE = 1; + +bool ColorizeCalendar(int today, int color); +ColorizeCalendar(BLUE, SUNDAY); // No error is reported. +``` + +When an enumeration value needs to correspond to a specific value, explicit value assignment is required during declaration. Otherwise, do not assign explicit values. This will prevent repeated assignment and reduce the maintenance workload (when adding and deleting members). + +```cpp +// Good example: Device ID defined in the S protocol. It is used to identify a device type. +enum DeviceType { + DEV_UNKNOWN = -1, + DEV_DSMP = 0, + DEV_ISMG = 1, + DEV_WAPPORTAL = 2 +}; +``` + +Do not assign explicit values when enumeration is used internally, and only for classification. + +```cpp +// Good example: Enumeration definition is used to identify session status in a program. +enum SessionState { + INIT, + CLOSED, + WAITING_FOR_RESPONSE +}; +``` + +Try to avoid repeating enumeration values. If it is required, use the already defined enumeration values instead. + +```cpp +enum RTCPType { + RTCP_SR = 200, + RTCP_MIN_TYPE = RTCP_SR, + RTCP_RR = 201, + RTCP_SDES = 202, + RTCP_BYE = 203, + RTCP_APP = 204, + RTCP_RTPFB = 205, + RTCP_PSFB = 206, + RTCP_XR = 207, + RTCP_RSI = 208, + RTCP_PUBPORTS = 209, + RTCP_MAX_TYPE = RTCP_PUBPORTS +}; +``` + +### Rule 9.1.2 Magic numbers cannot be used. +So-called magic numbers are numbers that are unintelligible and difficult to understand. + +Some numbers can be understood based on context. +For example, the number 12 varies in different contexts. +type = 12; is not intelligible (and a magic number), but `month = year * 12`; can be understood, so we wouldn't really class this as a magic number. +The number 0 is often seen as a magic number. For example, `status = 0`; cannot truly express any status information. + +Solution: +Comments can be added for numbers that are used locally. +For the numbers that are used multiple times, you must define them as constants and give them descriptive names. + +The following cases are forbidden: +No symbol is used to explain the meaning of a number, for example, ` const int ZERO = 0`. +The symbol name limits the value. For example, for example, `const int XX_TIMER_INTERVAL_300MS = 300`. Use `XX_TIMER_INTERVAL_MS` instead. + +### Rule 9.1.3 Ensure that a constant has only one responsibility. + +**Note**: A constant is used for only one specific function, that is, a constant cannot be used for multiple purposes. + +```cpp +// Good example: For protocol A and protocol B, the length of the MSISDN is 20. +const unsigned int A_MAX_MSISDN_LEN = 20; +const unsigned int B_MAX_MSISDN_LEN = 20; + +// Using different namespaces: +namespace Namespace1 { + const unsigned int MAX_MSISDN_LEN = 20; +} + +namespace Namespace2 { + const unsigned int MAX_MSISDN_LEN = 20; +} +``` + +### Rule 9.1.4 Do not use memcpy_s or memset_s to initialize non-POD objects. + +**Note**: `POD` is short for `Plain Old Data`, which is a concept introduced in the C++ 98 standard (ISO/IEC 14882, first edition, 1998-09-01). The `POD` types include the original types and aggregate types such as `int`, `char`, `float`, `double`, `enumeration`, `void`, and pointer. Encapsulation and object-oriented features cannot be used (for example, user-defined constructors, assignment operators, destructors, base classes, and virtual functions). + +For non-POD classes, such as class objects of non-aggregate types, virtual functions may exist. Memory layout is uncertain, and is related to the compiler. Misuse of memory copies may cause serious problems. + +Even if a class of the aggregate type is directly copied and compared, and any functions hiding information or protecting data are destroyed, the `memcpy_s` and `memset_s` operations are not recommended. + +For details about the POD type, see the appendix. + +### Recommendation 9.1.2 Declare and initialize variables only when they are used. + +**Note**: It is a common low-level programming error that a variable is not assigned an initial value before being used. Declaring and initializing a variable just before using it will prevent this. + +If all variables are declared at the beginning of a function before they are used, their scope covers the entire function, which may lead to the following problems: +* The program may become difficult to understand and maintain. The definition and use of variables are separated. +* These variables are difficult to initialize properly. At the beginning of a function, there is often insufficient information for variable initialization, and a default null value (such as 0) is often assigned as the initial value. If a variable is used before it is assigned a valid value, it will also cause errors. + +Following the minimization principle of variable scopes and the principle of proximity declaration will make it easier to read code and understand variable types and initial values. In particular, use initialization to replace declaration and then assign values. + +```cpp +// Bad example: Declaration is separated from initialization. +string name; // The variable is not initialized in the declaration, and a default constructor is called. +name = "zhangsan"; // An assignment operator is called again. Declaration is separate from definition, which is difficult to understand. + +// Good example: Declaration and initialization are together, and easy to understand. +string name("zhangsan"); // Invoke a constructor. +``` + + +## Expressions +### Rule 9.2.1 A variable cannot be referenced again if it is contained in an increment or decrement operation in an expression. +In an expression where the increment or decrement operations are performed on a variable, the variable cannot be referenced again. The result of a second referencing is not explicitly defined in C++ standards. The results in different compilers or different versions of a compiler may be different. +Therefore, it is recommended that an undefined operation sequence not be assumed. + +Note that the problem of operation sequence cannot be solved by using parentheses because this is not a priority problem. + +Example: +```cpp +x = b[i] + i++; // Bad: Whether the position of b[i] is before or after the i++ is unclear. +``` +The increment or decrement operation should be placed in a single line: +```cpp +x = b[i] + i; +i++; // Good: i++ is placed in a single line. +``` + +Function parameter +```cpp +Func(i++, i); // Bad: Whether the increment operation happens for the second parameter is unclear +``` + +Good example: +```cpp +i++; // Good: i++ is placed in a single line. +x = Func(i, i); +``` + +### Rule 9.2.2 A switch statement must have a default branch. +In most cases, a switch statement requires a default branch to ensure that there is a default action when the case tag is missing for a processed value. + +Exception: +If the switch condition variables are enumerated and the case branch covers all values, the default branch is redundant. +Because modern compilers can check which case branches are missing in the switch statement and provide an advanced warning. + +```cpp +enum Color { + RED = 0, + BLUE +}; + +// The switch condition variables are enumerated. Therefore, you do not need to add a default branch. +switch (color) { + case RED: + DoRedThing(); + break; + case BLUE: + DoBlueThing(); + ... + break; +} +``` + +### Recommendation 9.2.1 When comparing expressions, follow the principle that the left side tends to change and the right side tends to remain unchanged. +When a variable is compared with a constant, placing the constant on the left, for example, if (MAX == v), does not comply with standard reading habits and is more difficult to understand. +The constant should be placed on the right. The expression is written as follows: +```cpp +if (value == MAX) { + +} + +if (value < MAX) { + +} +``` +There are special cases: for example, if the expression `if (MIN < value && value < MAX)` is used to describe a range, the first half, as a constant, should be placed on the left. + +You do not need to worry about writing '==' as '=' because a compilation alarm will be generated for `if (value = MAX)` and an error will be reported by other static check tools. Use these tools to solve such writing errors and ensure that that code is readable. + +### Recommendation 9.2.2 Use parentheses to specify the operator precedence. +Use parentheses to specify the operator precedence. This will prevent program errors due to the inconsistency between default priority and the intended design. At the same time, it makes the code clearer and more readable. However, too many parentheses muddy the code, reducing readability. The following is a recommendation on their correct usage. + +- For binary and ternary operators, if multiple operators are involved, parentheses should be used. +```cpp +x = a + b + c; /* The operator does not change, and thus parentheses are not required. */ +x = Foo(a + b, c); /* The operator does not change, and thus parentheses are not required. */ +x = 1 << (2 + 3); /* More than one operator is used and thus parentheses are required. */ +x = a + (b / 5); /* More than one operator is used and thus parentheses are required. */ +x = (a == b) ? a : (a – b); /* More than one operator is used and thus parentheses are required. */ +``` + + +## Type Casting + +Do not use type branches to customize behaviors. Type branch customization behavior is prone to errors and is an obvious sign of attempting to compile C code using C++. This is very inflexible technology. If you forget to modify all branches when adding a new type to a compiler, you will not be notified. Use templates and virtual functions to let the type define itself rather than letting the calling side determine behavior. + +It is recommended that type casting be avoided. We should consider the data type in the code design instead of overusing type casting to solve type conflicts. When designing a basic type, consider the following: +- Whether it is unsigned or signed. +- Is it suitable for float or double? +- Should you use int8, int16, int32, or int64 bit lengths? + +However, we cannot prohibit the use of type casting because the C++ language is a machine-oriented programming language, involving pointer addresses, and we interact with various third-party or underlying APIs. Their type design may not be reasonable and type casting tends to occur in the adaptation process. + +Exception: When calling a function, if we do not want to process the result of the function, first consider whether this is your best choice. If you do not want to process the return value of the function, cast it to void. + +### Rule 9.3.1 If type casting is required, use the type casting provided by the C++ instead of the C style. + +**Note**: + +The type casting provided by C++ is more targeted, easy to read, and more secure than the C style. C++ provides the following types of casting: +- Type casting: +1. `dynamic_cast`: Used to inherit the downstream transformation of the system. `dynamic_cast` has the type check function. Design the base class and derived class to avoid using dynamic_cast for casting. +2. `static_cast`: Similar to the C style casting, which can be used to convert a value, or to convert the pointer or reference of a derived class into a base class pointer or reference. This casting is often used to eliminate type ambiguity brought on by multiple inheritance, which is relatively safe. If it is a pure arithmetic conversion, use the braces as stated in the following text. +3. `reinterpret_cast`: Used to convert irrelevant types. `reinterpret_cast` forces the compiler to reinterpret the memory of a certain type of objects into another type, which is an unsafe conversion. It is recommended that `reinterpret_cast` be used as little as possible. +4. `const_cast`: Used to remove the `const` attribute of an object so that the object can be modified. You are advised to use `const_cast` as little as possible. + +- Arithmetic conversion: (Supported by C++ 11 and later versions) + If the type information is not lost, for example, the casting from float to double, or from int32 to int64, the braces syntax is recommended. +```cpp + double d{ someFloat }; + int64_t i{ someInt32 }; +``` + +### Recommendation 9.3.1 Avoid using `dynamic_cast`. +1. `dynamic_cast` depends on the RTTI of C++ so that the programmer can identify the type of the object in C++ at run time. +2. `dynamic_cast` indicates that a problem has occurred in the design of the base class and derived class.The derived class destroys the contract of the base class and it is necessary to use `dynamic_cast` to convert the class to a subclass for special processing. In this case, it is more desirable to improve the design of the class, instead of using `dynamic_cast` to solve the problem. + +### Recommendation 9.3.2 Avoid using `reinterpret_cast`. + +**Note**: `reinterpret_cast` is used to convert irrelevant types. Trying to use `reinterpret_cast` to force a type to another type destroys the security and reliability of the type and is an insecure casting method. Avoid casting between completely different types. + +### Recommendation 9.3.3 Avoid using `const_cast`. + +**Note**: The `const_cast` command is used to remove the `const` and `volatile` properties of an object. + +The action of using a pointer or reference after the const_cast conversion to modify the const property of an object is undefined. + +```cpp +// Bad example: +const int i = 1024; +int* p = const_cast(&i); +*p = 2048; // The action is undefined. +``` + +```cpp +// Bad example: +class Foo { +public: + Foo() : i(3) {} + + void Fun(int v) + { + i = v; + } + +private: + int i; +}; + +int main(void) +{ + const Foo f; + Foo* p = const_cast(&f); + p->Fun(8); // The action is undefined. +} + +``` + + +## Resource Allocation and Release + +### Rule 9.4.1 When a single object is released, delete is used. When an array object is released, delete [] is used. +Note: To delete a single object, use delete; to delete an array object, use delete []. The reasons are as follows: + +- new: Apply for memory from the system and call the corresponding constructor to initialize an object. +- new[n]: Apply for memory for n objects and call the constructor n times for each object to initialize them. +- delete: Call the corresponding destructor first and release the memory of an object. +- delete[]: Call the corresponding destructor for each object and release their memory. + +If the usage of new and delete does not match this format, the results are unknown. For a non-class type, new and delete will not call the constructor or destructor. + +Bad example: +```cpp +const int MAX_ARRAY_SIZE = 100; +int* numberArray = new int[MAX_ARRAY_SIZE]; +... +delete numberArray; +numberArray = NULL; +``` + +Good example: +```cpp +const int MAX_ARRAY_SIZE = 100; +int* numberArray = new int[MAX_ARRAY_SIZE]; +... +delete[] numberArray; +numberArray = NULL; +``` + +### Recommendation 9.4.1 Use the RAII feature to trace dynamic allocation. + +Note: RAII is an acronym for Resource Acquisition Is Initialization. It is a simple technology that controls program resources (such as memory, file handle, network connections, and mutexes) by using the object lifecycle. + +The common practice is as follows: When the object is constructed, the resource is obtained, and the access to the resource is controlled so that the resource is always valid in the life cycle of the object. Finally, the resource is released when the object is destructed. This approach has two advantages: +- We do not need to explicitly release resources. +- The resources required by the object are always valid throughout the lifecycle of the object. This way, you do not need to check the validity of the resources, which simplifies logic and improves efficiency. + + +In the following example, RAII removes the need for explicit release of mutex resources. + +```cpp +class LockGuard { +public: + LockGuard(const LockType& lockType): lock_(lockType) + { + lock_.Aquire(); + } + + ~LockGuard() + { + lock_.Relase(); + } + +private: + LockType lock_; +}; + + +bool Update() +{ + LockGuard lockGuard(mutex); + if (...) { + return false; + } else { + // Data operations + } + + return true; +} +``` + +## Standard Template Library + +The standard template library (STL) varies between products. The following table lists some basic rules and suggestions for each team. + +### Rule 9.5.1 Do not save the pointer returned by c_str () of std::string. + +Note: The C++ standard does not specify that the string::c_str () pointer is permanently valid. Therefore, the STL implementation used can return a temporary storage area and release it quickly when calling string::c_str (). Therefore, to ensure the portability of the program, do not save the result of string::c_str (). Instead, call it directly. + +Example: + +```cpp +void Fun1() +{ + std::string name = "demo"; + const char* text = name.c_str(); // After the expression ends, the life cycle of name is still in use and the pointer is valid. + + // If a non-const member function (such as operator[] and begin()) of the string type is invoked and the string is modified, + // The text may become unavailable or may not be the original string. + name = "test"; + name[1] = '2'; + + // When the text pointer is used next time, the string is no longer "demo". +} + +void Fun2() +{ + std::string name = "demo"; + std::string test = "test"; + const char* text = (name + test).c_str(); // After the expression ends, the temporary object generated by the + operator may be destroyed, and the pointer may be invalid. + + // When the text pointer is used next time, it no longer points to the valid memory space. +} +``` +Exception: In rare cases where high performance coding is required , you can temporarily save the pointer returned by string::c_str() to match the existing functions which support only the input parameters of the const char* type. However, you should ensure that the lifecycle of the string object is longer than that of the saved pointer, and that the string object is not modified within the lifecycle of the saved pointer. + + +### Recommendation 9.5.1 Use std::string instead of char*. + +Note: Using string instead of `char*` has the following advantages: +1. There is no need to consider the null character ’\0’at the end. +2. You can directly use operators such as `+`, `=`, and `==`, and other character and string operation functions. +3. There is no need to consider memory allocation operations.This helps avoid explicit usage of `new` and `delete` and the resulting errors. + +Note that in some STL implementations, string is based on the copy-on-write policy, which causes two problems. One is that the copy-on-write policy of some versions does not implement thread security, and the program breaks down in multi-threaded environments. Second, dangling pointers may be caused when a dynamic link library transfers the string based on the copy-on-write policy, due to the fact that reference count cannot be reduced when the library is unloaded. Therefore, it is important to select a reliable STL implementation to ensure the stability of the program. + +Exception: +When an API of a system or other third-party library is called, only `char*` can be used for defined interfaces. However, before calling the interfaces, you can use string. When calling the interfaces, you can use `string::c_str()` to obtain the character pointer. +When a character array is allocated as a buffer on the stack, you can directly define the character array without using string or containers such as `vector`. + +### Rule 9.5.2 Do not use auto_ptr. +Note: The `std::auto_ptr` in the STL library has an implicit ownership transfer behavior. The code is as follows: +```cpp +auto_ptr p1(new T); +auto_ptr p2 = p1; +``` +After the second line of statements is executed, p1 does not point to the object allocated in line 1 and becomes `NULL`. Therefore, `auto_ptr` cannot be placed in any standard containers. +This ownership transfer behavior is not expected. In scenarios where ownership must be transferred, implicit transfer should not be used. This often requires the programmer to keep extra attention on code that uses `auto_ptr`, otherwise access to a null pointer will occur. +There are two common scenarios for using auto_ptr . One is to transfer it as a smart pointer to outside the function that generates the auto_ptr , and the other is to use auto_ptr as the RAII management class. Resources are automatically released when the lifecycle of auto_ptr expires. +In the first scenario, you can use std::shared_ptr instead. +In the second scenario, you can use std::unique_ptr in the C++ 11 standard. std::unique_ptr is a substitute for std::auto_ptr and supports explicit ownership transfer. + +Exception: +Before the C++ 11 standard is widely used, std::auto_ptr can be used in scenarios where ownership needs to be transferred. However, it is recommended that std::auto_ptr be encapsulated. The copy constructor and assignment operator of the encapsulation class should not be used in a standard container. + + +### Recommendation 9.5.2 Use the new standard header files. + +Note: +When using the standard header file of C++, use `` instead of ``. + +## Usage of const +Add the keyword const before the declared variable or parameter (example: `const int foo`) to prevent the variable from being tampered with. Add the const qualifier to the function in the class (example: `class Foo {int Bar (char c) const;} ;`) to make sure the function does not modify the status of the class member variable. const variables, data members, functions, and parameters ensure that the type detection during compilation is accurate and errors are found as soon as possible. Therefore, we strongly recommend that const be used in any possible case. +Sometimes it is better to use constexpr from C++ 11 to define real constants. + +### Rule 9.6.1 For formal parameters of pointer and reference types, if the parameters do not need to be modified, use const. +Unchanging values are easier to understand, trace, and analyze. `const` is used as the default option and is checked during compilation to make the code more secure and reliable. +```cpp +class Foo; + +void PrintFoo(const Foo& foo); +``` + +### Rule 9.6.2 For member functions that do not modify member variables, use const. +Declare the member function as `const` whenever possible. The access function should always be const. So long as the function of a member is not modified, the function is declared with const. +When you need to modify data members in a virtual function, take all classes in the inheritance chain into account instead of only focusing on the implementation of a single class. +```cpp +class Foo { +public: + + // ... + + int PrintValue() const // const modifies member functions and does not modify member variables. + { + std::cout << value_ << std::endl; + } + + int GetValue() const // const modifies member functions and does not modify member variables. + { + return value_; + } + +private: + int value_; +}; +``` + +### Recommendation 9.6.1 Member variables that will not be modified after initialization should be defined as constants. + +```cpp +class Foo { +public: + Foo(int length) : dataLength_(length) {} +private: + const int dataLength_; +}; +``` + +## Exceptions + +### Recommendation 9.7.1 If the function does not throw an exception, the declaration is `noexcept`. +**Reasons:** +1. If the function does not throw an exception, the declaration is `noexcept`, which enables the compiler to optimize the function to the maximum extent, for example, reducing the execution paths and improving the efficiency of exiting when an error occurs. +2. For STL containers such as `vector`, to ensure the interface robustness, if the `move ` constructor of saved items is not declared as `noexcept`, the `copy machanism` instead of the `move machanism` is used when the items are removed from the container. This would cause performance loss risks. If the function does not throw an exception, or a program does not intercept and process an exception thrown by the function, new `noexcept` keywords can be used to modify the function, indicating that the function does not throw an exception or the thrown exception is not intercepted or processed. For example: + +```cpp +extern "C" double sqrt(double) noexcept; // No exceptions are thrown. + +// noexcept can still be used when exceptions may be thrown. +// The exception of memory exhaustion is not processed. The function is simply declared as noexcept. +std::vector MyComputation(const std::vector& v) noexcept +{ + std::vector res = v; // Exceptions may be thrown. + // do something + return res; +} +``` + +**Example:** + +```cpp +RetType Function(Type params) noexcept; // Maximized optimization +RetType Function(Type params) noexcept; // No optimization + +// Declaration as noexcept for the move operation of std::vector is needed. +class Foo1 { +public: + Foo1(Foo1&& other); // no noexcept +}; + +std::vector a1; +a1.push_back(Foo1()); +a1.push_back(Foo1()); // The copy constructor is called to enable the container expansion and removal of existing items. + +class Foo2 { +public: + Foo2(Foo2&& other) noexcept; +}; + +std::vector a2; +a2.push_back(Foo2()); +a2.push_back(Foo2()); //Triggers container expansion and invokes the move constructor to move existing elements. +``` + +**Note** +The default constructor, destructor, `swap` function, and `move` operator should not throw an exception. + +## Templates + +Template programming allows for extremely flexible interfaces that are type safe and high performance, enabling reuse of code of different types but with the same behavior. + +The disadvantages of template proramming are as follows: + +1. The techniques used in template programming are often obscure to anyone but language experts. Code that uses templates in complicated ways is often unreadable, and is hard to debug or maintain. +2. Template programming often leads to extremely poor compiler time error messages: even if an interface is simple, complicated implementation details become visible when the user does something wrong. +3. If the template is not properly used, the code will be over expanded during runtime. +4. It is difficult to modify or refactor template code. The template code is expanded in multiple contexts, and it is hard to verify that the transformation makes sense in all of them. + +Therefore, it is recommended that __ template programming be used only in a small number of basic components and basic data structure__. When using the template programming, minimize the __ complexity as much as possible, and __ avoid exposing the template__. It is better to hide programming as an implementation detail whenever possible, so that user-facing headers are readable. And you should write sufficiently detailed comments for code that uses templates. + + +## Macros +In the C++ language, it is strongly recommended that complex macros be used as little as possible. +- For constant definitions, use `const` or `enum` as stated in the preceding sections. +- For macro functions, try to be as simple as possible, comply with the following principles, and use inline functions and template functions for replacement. + +```cpp +// The macro function is not recommended. +#define SQUARE(a, b) ((a) * (b)) + +// Use the template function and inline function as a replacement. +template T Square(T a, T b) { return a * b; } +``` + +For details about how to use macros, see the related chapters about the C language specifications. +**Exception**: For some common and mature applications, for example, encapsulation for new and delete, the use of macros can be retained. + +# 10 Modern C++ Features + +As the ISO released the C++ 11 language standard in 2011 and released the C++ 17 in March 2017, the modern C++ (C++ 11/14/17) adds a large number of new language features and standard libraries that improve programming efficiency and code quality. +This chapter describes some guidelines for modern C++ use, to avoid language pitfalls. + +## Code Simplicity and Security Improvement +### Recommendation 10.1.1 Use `auto` properly. +**Reasons** + +* `auto` can help you avoid writing verbose, repeated type names, and can also ensure initialization when variables are defined. +* The `auto` type deduction rules are complex and need to be read carefully. +* If using `auto` makes the code clearer, use a specific type of it and use it only for local variables. + +**Example** + +```cpp +// Avoid verbose type names. +std::map::iterator iter = m.find(val); +auto iter = m.find(val); + +// Avoid duplicate type names. +class Foo {...}; +Foo* p = new Foo; +auto p = new Foo; + +// Ensure that the initialization is successful. +int x; // The compilation is correct but the variable is not initialized. +auto x; // The compilation failed. Initialization is needed. +``` + +`auto` type deduction may cause the following problems: + +```cpp +auto a = 3; // int +const auto ca = a; // const int +const auto& ra = a; // const int& +auto aa = ca; // int, const and reference are neglected. +auto ila1 = { 10 }; // std::initializer_list +auto ila2{ 10 }; // std::initializer_list + +auto&& ura1 = x; // int& +auto&& ura2 = ca; // const int& +auto&& ura3 = 10; // int&& + +const int b[10]; +auto arr1 = b; // const int* +auto& arr2 = b; // const int(&)[10] +``` + +If you do not pay attention to `auto` type deduction and ignore the reference, hard-to-find performance problems may be created. + +```cpp +std::vector v; +auto s1 = v[0]; // auto deduction changes s1 to std::string in order to copy v[0]. +``` + +If `auto` is used to define an interface, such as a constant in a header file, the type may be changed if the developer has modified the value. + +### Rule 10.1.1 Use the keyword `override` when rewriting virtual functions. +**Reason:** +The keyword `override` ensures that the function is a virtual function and an overridden virtual function of the base class. If the subclass function is different from the base class function prototype, a compilation alarm is generated. `final` also ensures that virtual functions are not overridden by subclasses. + +If you modify the prototype of a base class virtual function but forget to modify the virtual function overridden by the subclass, you can find inconsistency during compilation. You can also avoid forgetting to modify the overridden function when there are multiple subclasses. + +**Example** + +```cpp +class Base { +public: + virtual void Foo(); + virtual void Foo(int var); + void Bar(); +}; + +class Derived : public Base { +public: + void Foo() const override; // Compilation failed: derived::Foo is different from that of the prototype of base::Foo and is not overridden. + void Foo() override; // Compilation successful: derived::Foo overrode base::Foo. + void Foo(int var) final; // Compilation successful: Derived::Foo(int) rewrites Base::Foo(int), and the derived class of Derived cannot override this function. + void Bar() override; // Compilation failed: base::Bar is not a virtual function. +}; +``` + +**Summary** +1. When defining the virtual function for the first time based on the base class, use the keyword `virtual`. +2. When overriding the virtual function by a subclass in a base class, including destructors, use the keyword `override` or `final` instead of `virtual`. +3. For the non-virtual function, do not use `virtual` or `override`. + +### Rule: 10.1.2 Use the keyword `delete` to delete functions. +**Reason** +The `delete` keyword is clearer and the application scope is wider than a class member function that is declared as private and not implemented. + +**Example:** + +```cpp +class Foo { +private: + // Whether the copy structure is deleted or not is unknown because usually only the header file is checked. + Foo(const Foo&); +}; + +class Foo { +public: + // Explicitly delete the copy assignment operator. + Foo& operator=(const Foo&) = delete; +}; +``` + +The `delete` keyword can also be used to delete non-member functions. + +```cpp +template +void Process(T value); + +template<> +void Process(void) = delete; +``` + +### Rule 10.1.3 Use `nullptr` instead of `NULL` or `0`. +**Reason:** +For a long time, C++ has not had a keyword that represents a null pointer, which is embarrassing: + +```cpp +#define NULL ((void *)0) + +char* str = NULL; // Error: void* cannot be automatically converted to char*. + +void(C::*pmf)() = &C::Func; +if (pmf == NULL) {} // Error: void* cannot be automatically converted to the pointer that points to the member function. +``` + +If `NULL` is defined as `0` or `0L`, the above problems can be solved. + +Alternatively, use `0` directly in places where null pointers are required. However, another problem occurs. The code is not clear, especially when `auto` is used for automatic deduction. + +```cpp +auto result = Find(id); +if (result == 0) { // Does Find() return a pointer or an integer? + // do something +} +``` + +Literally `0` is of the `int` type (`0L` is the `long` type). Therefore, neither `NULL` nor `0` is a pointer type. +When a function of the pointer or integer type is overloaded, `NULL` or `0` calls only the overloaded pointer function. + +```cpp +void F(int); +void F(int*); + +F(0); // Call F(int) instead of F(int*). +F(NULL); // Call F(int) instead of F(int*). +``` + +In addition, `sizeof(NULL) == sizeof(void*)` does not always make sense, which is a potential risk. + +Summary: If `0` or `0L` is directly used, the code is not clear and type security cannot be ensured. If `NULL` is used, the type security cannot be ensured. These are all potential risks. + +`nullptr` has many advantages. It literally represents the null pointer and makes the code clearer. More to the point, it is no longer an integer type. + +`nullptr` is of the `std::nullptr_t` type. `std::nullptr_t` can be implicitly converted into all original pointer types, so that `nullptr` can represent a null pointer that points to any type. + +```cpp +void F(int); +void F(int*); +F(nullptr); // Call F(int*). + +auto result = Find(id); +if (result == nullptr) { // Find() returns a pointer. + // do something +} +``` + +### Rule 10.1.4 Use `using` instead of `typedef`. +For versions earlier than `C++11`, you can define the alias of the type by using `typedef`. No one wants to repeat code like `std::map>`. + +```cpp +typedef std::map> SomeType; +``` + +Using alias for the type is actually encapsulating the type. This encapsulation makes the code clearer, and to a large extent avoids the bulk modification caused by the type change. +For versions supporting C++ 11 features, `using` is provided to implement `alias declarations`: + +```cpp +using SomeType = std::map>; +``` + +Compare the two formats: + +```cpp +typedef Type Alias; // It cannot be told whether the original Type or Alias is at the front. +using Alias = Type; // The format confirms to the assignment rule. It is easy to understand and helps reduce errors. +``` + +If this is not enough to prove the advantages of `using`, the alias template may be a better example: + +```cpp +//: Only one line of code is need to define an alias for a template. +template +using MyAllocatorVector = std::vector>; + +MyAllocatorVector data; // An alias for a template defined with "using". + +template +class MyClass { +private: + MyAllocatorVector data_; // Another. +}; +``` + +`typedef` does not support alias templates and they have to be hacked in. + +```cpp +// A template is used for packaging typedef. Therefore, a template class is needed. +template +struct MyAllocatorVector { + typedef std::vector> type; +}; + +MyAllocatorVector::type data; // ::type needs to be added when using typedef to define an alias. + +template +class MyClass { +private: + typename MyAllocatorVector::type data_; // For a template class, typename is also needed in addition to ::type. +}; +``` + +### Rule 10.1.5 Do not use std::move to operate the const object. +Literally, `std::move` means moving an object. The const object cannot be modified and cannot be moved. Therefore, using `std::move` to operate the const object may confuse code readers. +Regarding actual functions, `std::move` converts an object to the rvalue reference type. It can convert the const object to the rvalue reference of const. Because few types define the move constructor and the move assignment operator that use the const rvalue reference as the parameter, the actual function of code is often degraded to object copy instead of object movement, which brings performance loss. + +**Bad example:** +```cpp +std::string g_string; +std::vector g_stringList; + +void func() +{ + const std::string myString = "String content"; + g_string = std::move(myString); // Bad: myString is not moved. Instead, it is copied. + const std::string anotherString = "Another string content"; + g_stringList.push_back(std::move(anotherString)); // Bad: anotherString is not moved. Instead, it is copied. +} +``` + +## Smart Pointers +### Rule 10.2.1 Use smart pointers instead of a raw pointer to manage resources. +**Reason:** +Avoid resource leakage. + +**Example:** + +```cpp +void Use(int i) +{ + auto p = new int {7}; // Bad: Initializing local pointers with new. + auto q = std::make_unique(9); // Good: Guarantee that memory is released. + if (i > 0) { + return; // Return and possible leak. + } + delete p; // Too late to salvage. +} +``` + +**Exception:** +Raw pointers can be used in scenarios requiring high performance and compatibility. + +### Rule 10.2.2 Use `unique_ptr` instead of `shared_ptr`. +**Reasons:** +1. Using `shared_ptr` a lot has an overhead (atomic operations on the `shared_ptr`s reference count have a measurable cost). +2. Shared ownership in some cases (such as circular dependency) may create objects that can never be released. +3. Shared ownership can be an attractive alternative to careful ownership design but it may obfuscate the design of a system. + +### Rule 10.2.3 Use `std::make_unique` instead of `new` to create a `unique_ptr`. +**Reasons:** +1. `make_unique` provides a simpler creation method. +2. `make_unique` ensures the exception safety of complex expressions. + +**Example:** + +```cpp +// Bad: MyClass appears twice, which carries a risk of inconsistency. +std::unique_ptr ptr(new MyClass(0, 1)); +// Good: MyClass appears once and there is no possibility of inconsistency. +auto ptr = std::make_unique(0, 1); +``` + +Recurrence of types may cause serious problems, and it is difficult to find them: + +```cpp +// The code compiles fine, but new and delete usage does not match. +std::unique_ptr ptr(new uint8_t[10]); +std::unique_ptr ptr(new uint8_t); +// No exception safety: The compiler may calculate parameters in the following order: +// 1. Allocate the memory of Foo. +// 2. Construct Foo. +// 3. Call Bar. +// 4. Construct unique_ptr. +// If Bar throws an exception, Foo is not destroyed and a memory leak occurs. +F(unique_ptr(new Foo()), Bar()); + +// Exception safety: Calling of function is not interrupted. +F(make_unique(), Bar()); +``` + +**Exception:** +`std::make_unique` does not support user-defined `deleter`. +In the scenario where the `deleter` needs to be customized, it is recommended that `make_unique` be implemented in the customized version’s own namespace. +Using `new` to create `unique_ptr` with the user-defined `deleter` is the last choice. + +### Rule 10.2.4 Create `shared_ptr` by using `std::make_shared` instead of `new`. +**Reason:** +In addition to the consistency factor similar to that in `std::make_unique` when using `std::make_shared`, performance is also a factor to consider. +`std::shared_ptr` manages two entities: +* Control block (storing reference count, `deleter`, etc.) +* Managed objects + +When `std::make_shared` creates `std::shared_ptr`, it allocates sufficient memory for storing control blocks and managed objects on the heap at a time. When `std::shared_ptr(new MyClass)`is used to create a `std::shared_ptr`, not only does `new MyClass` trigger heap allocation, but the constructor function of `std::shard_ptr` triggers a second heap allocation, resulting in extra overhead. + +**Exception:** +Similar to `std::make_unique`, `std::make_shared` does not support `deleter` customization. + +## Lambda +### Recommendation 10.3.1 Use `lambda` to capture local variables or write local functions when normal functions do not work. +**Reason:** +Functions cannot capture local variables or be declared at local scope. If you need those things, choose `lambda` instead of handwritten `functor`. +On the other hand, `lambda` and `functor` objects do not support overloading. If overloading is required, use a function. +If both `lambda` and functions work, a function is preferred. Use the simplest tool. + +**Example:** + +```cpp +// Write a function that accepts only an int or string. +// -- Overloading is more natural. +void F(int); +void F(const string&); + +// The local state needs to be captured or appear in the statement or expression range. +// -- A lambda is more natural. +vector v = LotsOfWork(); +for (int taskNum = 0; taskNum < max; ++taskNum) { + pool.Run([=, &v] {...}); +} +pool.Join(); +``` + +### Rule 10.3.1 Avoid capturing by reference in lambdas that will not be used locally. +**Reason:** +Using `lambdas` at a "nonlocal" scope includes returning, storing on the heap, and passing to another thread. Local pointers and references should not outlive their scope. Capturing by reference in `lambdas` indicates storing a reference to a local object. If this leads to a reference that exceeds the lifecycle of a local variable, capturing by reference should not be used. + +**Example:** + +```cpp +// Bad +void Foo() +{ + int local = 42; + // Capture a reference to a local variable. + // After the function returns results, local no longer exists, + // Process() call will have undefined behavior. + threadPool.QueueWork([&]{ Process(local); }); +} + +// Good +void Foo() +{ + int local = 42; + // Capture a copy of local. + // Since a copy of local is made, it will be always available for the call. + threadPool.QueueWork([=]{ Process(local); }); +} +``` + +### Recommendation 10.3.2 All variables are explicitly captured if `this` is captured. +**Reason:** +The `[=]` in the member function seems to indicate capturing by value but actually it is capturing data members by reference because it captures the invisible `this` pointer by value. Generally, it is recommended that capturing by reference be avoided. If it is necessary to do so, write `this` explicitly. + +**Example:** + +```cpp +class MyClass { +public: + void Foo() + { + int i = 0; + + auto Lambda = [=]() { Use(i, data_); }; // Bad: It looks like we are copying or capturing by value but member variables are actually captured by reference. + + data_ = 42; + Lambda(); // Call use(42); + data_ = 43; + Lambda(); // Call use(43); + + auto Lambda2 = [i, this]() { Use(i, data_); }; // Good: the most explicit and least confusing method. + } + +private: + int data_ = 0; +}; +``` + +### Recommendation 10.3.3 Avoid default capture modes. +**Reason:** +The lambda expression provides two default capture modes: by-reference (&) and by-value (=). +By default, the "by-reference" capture mode will implicitly capture the reference of all local variables, which will easily lead to dangling references. By contrast, explicitly writing variables that need to be captured can make it easier to check the lifecycle of an object and reduce the possibility of making a mistake. +By default, the "by-value” capture mode will implicitly capture this pointer, and it is difficult to find out which variables the lambda function depends on. If a static variable exists, the reader mistakenly considers that the lambda has copied a static variable. +Therefore, it is required to clearly state the variables that lambda needs to capture, instead of using the default capture mode. + +**Bad example:** +```cpp +auto func() +{ + int addend = 5; + static int baseValue = 3; + + return [=]() { // Only addend is actually copied. + ++baseValue; // The modification will affect the value of the static variable. + return baseValue + addend; + }; +} +``` + +**Good example:** +```cpp +auto func() +{ + int addend = 5; + static int baseValue = 3; + + return [addend, baseValue = baseValue]() mutable { // Uses the C++14 capture initialization to copy a variable. + ++baseValue; // Modifying the copy of a static variable does not affect the value of the static variable. + return baseValue + addend; + }; +} +``` + +Reference: Effective Modern C++: Item 31: Avoid default capture modes. + +## Interfaces +### Recommendation 10.4.1 Use `T*` or `T&` arguments instead of a smart pointer in scenarios where ownership is not involved. +**Reasons:** +1. Passing a smart pointer to transfer or share ownership should only be used when the ownership mechanism is explicitly required. +2. Passing a smart pointer (for example, passing the `this` smart pointer) restricts the use of a function to callers using smart pointers. +3. Passing a shared smart pointer adds a runtime performance cost. + +**Example:** + +```cpp +// Accept any int*. +void F(int*); + +// Accept only integers for which you want to transfer ownership. +void G(unique_ptr); + +// Accept only integers for which you want to share ownership. +void G(shared_ptr); + +// Does not need to change the ownership but requires ownership of the caller. +void H(const unique_ptr&); + +// Accept any int. +void H(int&); + +// Bad +void F(shared_ptr& w) +{ + // ... + Use(*w); // When only w is used, lifecycle management is not required. + // ... +}; +``` + diff --git a/docs-en/get-code/source-code-acquisition.md b/docs-en/get-code/source-code-acquisition.md index 521b65baad6c6ea749403f604d339a92db82b77c..01991e60354aeb6a83fe881d5fc110911d28e301 100755 --- a/docs-en/get-code/source-code-acquisition.md +++ b/docs-en/get-code/source-code-acquisition.md @@ -47,7 +47,7 @@ You can download the source code or the corresponding solutions from the image l

Site

-

SHA-256 Verification Code

+

SHA-256 Verification Code

Hi3518 solutions (binary)

@@ -234,7 +234,7 @@ Add the bundle \(**@ohos/demo** as an example\) to your project as follows: Method 1 \(recommended\): Use the **repo** tool to download source code. ``` -repo init -u https://gitee.com/openharmony/manifest.git -b master +repo init -u https://gitee.com/openharmony/manifest.git -b master --no-repo-verify repo sync -c ``` diff --git a/docs-en/get-code/tool-acquisition.md b/docs-en/get-code/tool-acquisition.md index 90ed477644c39785b999bd1112b1a638967af480..1b79636ece91988d11c9e4fbf5ea6c3584fa83ed 100755 --- a/docs-en/get-code/tool-acquisition.md +++ b/docs-en/get-code/tool-acquisition.md @@ -2,64 +2,76 @@ ## Acquiring Compilation Toolchain -Download the compilation toolchain from image sites listed in the following table. For details about how to install the compilation toolchain, see the **Environment Setup** section in **Getting Started** of the specific development guide. +The following table lists the compilation toolchain downloaded from an image site. For details about how to download and install all compilation tools, see the **Environment Setup** section in **Getting Started** of the specific development board. **Table 1** Acquiring compilation toolchain -

Content

+ - - - + - + - - - - + - - - - - - - + - + - - - - + - - - @@ -98,7 +110,7 @@ HUAWEI DevEco Device Tool \(DevEco Device Tool for short\) is a one-stop integra ## Tool Introduction -HUAWEI DevEco Studio \(DevEco Studio for short\) is a one-stop IDE oriented to OpenHarmony-based devices in all scenarios. It allows you to create project templates, and develop, build, debug, and release OpenHarmony applications from end to end. DevEco Studio enables you to efficiently develop applications with OpenHarmony distributed capabilities, thereby empowering you to innovate applications. +HUAWEI DevEco Studio \(DevEco Studio for short\) is a one-stop IDE oriented to Huawei devices in all scenarios. It allows you to create project templates, and develop, build, debug, and release OpenHarmony applications from end to end. DevEco Studio enables you to efficiently develop applications with OpenHarmony distributed capabilities, thereby empowering you to innovate applications. ## Website diff --git a/docs-en/guide/Readme-EN.md b/docs-en/guide/Readme-EN.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/adding-pages-4.md b/docs-en/guide/adding-pages-4.md deleted file mode 100755 index a9426b25c99ae8e7c1fa0dc64378e8590431e36c..0000000000000000000000000000000000000000 --- a/docs-en/guide/adding-pages-4.md +++ /dev/null @@ -1,31 +0,0 @@ -# Adding Pages - -## Creating the Home Page \(Creating a Project\) - -After the project is created, the **index** page is generated by default. The following figure shows the project directory. - -**Figure 1** Project directory -![](figures/project-directory-1.png "project-directory-1") - -## Creating Detail and History Pages - -Perform the following steps twice to create the rest two pages: - -1. Right-click **pages** and choose **New** \> **JS Page** from the shortcut menu. - - **Figure 2** Adding a page - ![](figures/adding-a-page-2.png "adding-a-page-2") - -2. Enter the page name. - - **Figure 3** Entering the page name - ![](figures/entering-the-page-name-3.png "entering-the-page-name-3") - -3. Confirm the creation. - - The following figure shows the application project directory after the **detail** page and **history** page are created. Each page contains a **.hml** layout file, a **.css** file, and a **.js** file \(containing service logic code\). - - **Figure 4** Complete project directory - ![](figures/complete-project-directory-4.png "complete-project-directory-4") - - diff --git a/docs-en/guide/adding-pages.md b/docs-en/guide/adding-pages.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/building-the-detail-page-6.md b/docs-en/guide/building-the-detail-page-6.md deleted file mode 100755 index 073539f91faae7de1266f2cd814c62ad1ecd23dd..0000000000000000000000000000000000000000 --- a/docs-en/guide/building-the-detail-page-6.md +++ /dev/null @@ -1,124 +0,0 @@ -# Building the Detail Page - -The **detail** page displays air quality data, such as CO, NO2, PM10, PM2.5, and SO2. Use multiple **** components to display the information, and use the **** component to continuously swipe up and down on the page. Note that **** can have multiple child components, which must be ****. For the **detail** page, you need to develop for the page typesetting and styles. The detailed code is as follows: - -## detail.hml - -``` - - -
- {{location}} -
- -
- CO - {{airData[0]}} -
-
- NO2 - {{airData[1]}} -
-
- PM10 - {{airData[2]}} -
-
- PM2.5 - {{airData[3]}} -
-
- SO2 - {{airData[4]}} -
-
-
- -
- - {{updated}}:{{updateStatus}} - - {{dataSourceName}}:{{dataSource}} -
- -
- - -
-
-
-
-``` - -## detail.css - -``` -.line-div{ - background-color:#f5fffa; - height:2px; - width:454px; -} -.info-div-width-height{ - width:321px; - height:60px; - margin-top: 20px; -} -.gas-name{ - color:#f5fffa; - width:160px; - height:30px; -} -.gas-value{ - text-align:right; - color:#00fa9a; - width:160px; - height:30px; -} -.config-info { - height:40px; - width:321px; - color:#f5fffa; -} -``` - -## detail.js - -``` -import router from '@system.router' -export default { - data:{// Initialization information - location:"HangZhou", - udpateStatus:"1h ago", - dataSource:"tianqi.com", - updateTime:"15:13", - updated:'Updated', - dataSourceName:'Data Source', - sampledOn:'Sampled on', - cityIndex:0, - airData:['100', '90', '120', '50', '150', '40', '25'] - }, - onInit(){ - // Process information in multiple languages. - this.location = this.$t(this.location); - this.updated = this.$t("updated"); - this.dataSourceName = this.$t("dataSourceName"); - this.sampledOn = this.$t("sampledOn"); - this.monitoringStation = this.$t("monitoringStation"); - if(this.selectedCityIndex != null){ // Save city information sent from the home page. - this.cityIndex= this.selectedCityIndex; - } - }, - openHistroy(){// Switch to the history page. - router.replace({ - uri:'pages/history/history' - }); - }, - backMain(){ // Go back to the home page and return the information about the selected city. - router.replace({ - uri:'pages/index/index', - params:{selectedCityIndex:this.cityIndex} - }); - } -} -``` - diff --git a/docs-en/guide/building-the-detail-page.md b/docs-en/guide/building-the-detail-page.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/building-the-history-page-7.md b/docs-en/guide/building-the-history-page-7.md deleted file mode 100755 index bfafd5640717815416dc14a12ba18181dc744d3f..0000000000000000000000000000000000000000 --- a/docs-en/guide/building-the-history-page-7.md +++ /dev/null @@ -1,86 +0,0 @@ -# Building the History Page - -The **history** page displays the air quality data of a week in a chart. On this page, multiple **** components are used to replace the **** component to display the chart. The sample code is as follows: - -## history.hml - -``` - - -
- {{historicalData}} -
-
- - -
-
- - {{$item}} -
- -
-
-
-
-
-
-
-
- - -
-
- - - - -
-``` - -## history.css - -``` -.div-info-location{ - color:#dcdcdc; - width:321px; - height:40px; -} -.div-info-historical-data{ - color:#f5fffa; - width:321px; - height:40px; -} -.gas-name{ - color:#f0ffff; - text-align:right; - width:321px; - height:32px; -} -.info-list-item{ - width:321px; - height:80px; -} -``` - -## history.js - -``` -import router from '@system.router' -module.exports = { - data: { - historicalData:"historicalData", - datasets:["CO","O3","NO2","NO","PM25","SO2"] - }, - onInit(){ - // Process information in multiple languages. - this.historicalData = this.$t(this.historicalData); - }, - backDetail(){ - router.replace({ // Return to the detail page. - uri:'pages/detail/detail' - }); - } -} -``` - diff --git a/docs-en/guide/building-the-history-page.md b/docs-en/guide/building-the-history-page.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/building-the-home-page-5.md b/docs-en/guide/building-the-home-page-5.md deleted file mode 100755 index 146298be5cba629bac28e74486a8a543ad24e064..0000000000000000000000000000000000000000 --- a/docs-en/guide/building-the-home-page-5.md +++ /dev/null @@ -1,271 +0,0 @@ -# Building the Home Page - -The application home page displays air quality information of the current city. There are two screens on the home page. Each screen displays the air quality information of a city, including the AQI and city name. The AQI value can be displayed in the form of a ring progress bar with animation. - -1. The **** component is required to implement switching between the two screens. - - Add a root **** to the **.hml** file. Note that each **.hml** file can contain only one root component. The code snippet is as follows: - - ``` - - - ``` - - - **class="container"** indicates the style used by the component. The **container** is a style class in the **index.css** file. The code snippet is as follows: - - ``` - .container{ - height: 454px; - width: 454px; - } - ``` - - This style class sets the height and width of the component. For device development, the component height and width must be explicitly specified. Otherwise, the component may fail to be displayed. - - - - **index="\{\{swiperPage\}\}" duration="500" onchange="swiperChange"** sets the component attribute and event. **duration="500"** indicates that the duration of the swiping animation is 500 ms. - - **index="\{\{swiperPage\}\}"** specifies the index of the child component of ****. **\{\{swiperPage\}\}** indicates that the index value is dynamically bound to the **swiperPage** variable in the JavaScript code. The index value changes with the **swiperPage** value. - - **onchange="swiperChange"** binds the change event of the **** component to the **swiperChange** function. The JavaScript code is as follows: - - ``` - // Import the router module for page switching. - import router from '@system.router' - export default { - // Define parameters. - data: { - swiperPage:0 // The first page is displayed by default. - }, - onInit () { - }, - // Swiping event, which saves the index value of the current . The index value is saved to the swiperPage variable each time a swiping occurs. - swiperChange (e) { - this.swiperPage = e.index; - } - } - ``` - - -2. Set the information about each city to be displayed on a screen. In each screen, four types of information needs to be displayed using different components. - - Add two child components \(****\) to the ****. Add the ****, ****, and **** components to each **** to display the information. Code example in the **.hml** file is as follows: - - ``` - - - - - ------City - ------Air quality - -----Progress bar - -------A cloud image - --------AQI value - AQI------AQI - - - - - - - - - - - - ``` - -3. Set the style, animation effect, and dynamic data binding for all components. The complete example code is as follows: - - - **index.hml** - - ``` - - - - - - {{airData[0].airQuality}} - {{airData[0].location}} - - - - - {{airData[0].detailData}} - AQI - - - - {{airData[1].airQuality}} - {{airData[1].location}} - - - {{airData[1].detailData}} - AQI - - - ``` - - - **index.css** - - A **.css** file contains many classes. Each class defines the position, size, font, color, and background color of a component. Each child component is added to its parent component, and the style file of the parent component affects how the child component will be displayed. - - ``` - .pm25-value{ - text-align:center; - font-size:38px; - color:#f0ffff; - width:454px; - height:50px; - top:275px; - } - .pm25-name{ - text-align:center; - color:#a2c4a2; - width:454px; - height:50px; - top:335px; - } - .location-text{ - text-align:center; - color:#f0ffff; - width:454px; - height:50px; - top:20px; - } - .container{ - height: 454px; - width: 454px;; - } - .circleProgress{ - centerX:227px; - centerY:250px; - radius:180px; - startAngle:198; - totalAngle:320; - strokeWidth:45; - width:454px; - height:454px; - } - .image{ - top:390px; - left:217px; - width:32px; - height:32px; - } - .airquality{ - top:220px; - text-align: center; - width:454px; - height:40px; - } - - ``` - - - **index.js** - - A **.js** file is used to implement interaction logic of your application. In the **.js** file of the home page, the following features need to be implemented: dynamic changes of the text content and progress bar color based on numbers, multiple languages, page switching, and animation playback. - - ``` - // Import the router module for page switching. - import router from '@system.router' - export default { - // Define parameters. - data: { - textColor1:'#00ff00',// Text color - textColor2:'#00ff00', - bgColor1:'#669966',// Background color - bgColor2:'#669966', - swiperPage:0, - percent1:0,// Progress bar - percent2:0, - src1:'common/cloud_green.png', - src2:'common/cloud_green.png', - airData: [{ - location: 'HangZhou', - airQuality: 'Good', - detailData: 10 - }, { - location: 'ShangHai', - airQuality: 'Unhealth', - detailData:90 - }] - }, - onInit () { - // Multi-language feature. Use the $t function to obtain content in the required language. - this.airData[0].location = this.$t(this.airData[0].location); - this.airData[1].location = this.$t(this.airData[1].location); - this.airData[0].airQuality = this.$t(this.airData[0].airQuality); - this.airData[1].airQuality = this.$t(this.airData[1].airQuality); - if(this.airData[0].detailData > 100){ // Display different colors and images based on indicator values. - this.src1 = "common/cloud_red.png"; - this.textColor1 = '#ff0000';// Display the text in red. - this.bgColor1 = '#9d7462'; - } else if(50 < this.airData[0].detailData && this.airData[0].detailData <= 100){ - this.src1 = "common/cloud_yellow.png"; - this.textColor1 = '#ecf19a';// Display the text in yellow. - this.bgColor1 = '#9d9d62'; - } - if(this.airData[1].detailData > 100){ - this.src2 = "common/cloud_red.png"; - this.textColor2 = '#ff0000'; - this.bgColor2 = '#9d7462'; - } else if(50 < this.airData[1].detailData && this.airData[1].detailData <= 100){ - this.src2 = "common/cloud_yellow.png"; - this.textColor2 = '#ecf19a'; - this.bgColor2 = '#9d9d62'; - } - if(this.selectedCityIndex){ - this.swiperPage = this.selectedCityIndex; - } - }, - onShow () { // Processing logic during page display - var self = this; - var time = 1000/(self.airData[self.swiperPage].detailData);// Complete animation playback in 1s. - if(time == 0){ - time = 100; - } - // Animation effect of the ring progress bar. Start a timer and change the progress bar at a certain interval (calculated based on the AQI value). The animation playback is completed within 1s. - var interval = setInterval(function () { - if ((self.swiperPage==0?self.percent1:self.percent2) >= self.airData[self.swiperPage].detailData) { - clearInterval(interval); - return; - } - if(self.swiperPage == 0){ - self.percent1++; - }else{ - self.percent2++ - } - }, time) - }, - // Switch to the detail page. - openDetail () { - router.replace({ - uri:'pages/detail/detail', - params:{selectedCityIndex:this.swiperPage}// Selected city - }); - }, - // Swiping event, which saves the index value of the current and directly switches to the specified screen from the detail page. - swiperChange (e) { - this.swiperPage = e.index; - var self = this; - var time = 1000/(self.airData[self.swiperPage].detailData); - if(time == 0){ - time = 100; - } - // Play the animation if the user swipes to the page for the first time. - var interval = setInterval(function () { - let percent = (self.swiperPage==0?self.percent1:self.percent2); - if (percent >= self.airData[self.swiperPage].detailData) { - clearInterval(interval); - return; - } - if(self.swiperPage==0){ - self.percent1++; - }else{ - self.percent2++; - } - }, time) - } - } - ``` - - diff --git a/docs-en/guide/building-the-home-page.md b/docs-en/guide/building-the-home-page.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/camera-control.md b/docs-en/guide/camera-control.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/cameras-with-a-screen.md b/docs-en/guide/cameras-with-a-screen.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/debugging-and-verification.md b/docs-en/guide/debugging-and-verification.md deleted file mode 100755 index daa5fa38970a3ff5721d75075c14b9506e3c0557..0000000000000000000000000000000000000000 --- a/docs-en/guide/debugging-and-verification.md +++ /dev/null @@ -1,90 +0,0 @@ -# Debugging and verification - -For details about the burning process, see the Hi3861 Quick Start - Burning. The following describes the verification process of the power distribution network. - -## Non-perceptive network distribution function verification - -1. Start the module with the power distribution sample service to enable the module to enter the to-be-distributed state, as shown in the following figure. - - ``` - sdk ver:Hi3861V100R001C00SPC023 2020-06-09 13:30:00 - FileSystem mount ok. - wifi init success! - app_main test - [sample] main biz. - ap start succ - Nan Init Success - nan state(0)->(1) - wait STA join AP - [sample] main biz. - [sample] main biz. - wait STA join AP - ``` - -2. Start the app on the mobile phone. \(This app is an internal debugging demo. Developers need to develop FAs by themselves. The following describes only the network configuration process.\) Click Login Authorization \> Login Authorization \> NAN Device, and place the mobile phone close to the module, a new device is found. \(In the following figure, the product icon is changed to 1, and the SN is changed to 0123456789012345.\) - - **Figure 1** Discovering a new device - - - ![](figures/en-us_image_0000001054570953.png) - -3. Click the device to enter the configuration page. Select the hotspot SSID, enter the password, and click the button for network configuration \(...\), as shown in the following figure. - - **Figure 2** Wi-Fi network configuration page - - - ![](figures/en-us_image_0000001054850904.png) - -4. Click Next. On the second control page that is displayed, click the power button. The device receives the control message, as shown in the following figure. - - **Figure 3** Control page - - - ![](figures/en-us_image_0000001054370936.png) - -5. On the second control page, click the disconnection button. After you click the button, the device receives a message indicating that it will exit the NAN. - - **Figure 4** Exiting the Control Interface - - - ![](figures/en-us_image_0000001054132279.png) - - -## SoftAP network configuration mode verification - -1. Start the module with the power distribution sample service to enable the module to enter the to-be-distributed state, as shown in the following figure. - - ``` - sdk ver:Hi3861V100R001C00SPC023 2020-06-09 13:30:00 - FileSystem mount ok. - wifi init success! - app_main test - [sample] main biz. - ap start succ - Nan Init Success - nan state(0)->(1) - wait STA join AP - [sample] main biz. - [sample] main biz. - wait STA join AP - ``` - -2. Start the app on the mobile phone. \(This app is an internal debugging demo. Developers need to develop FAs by themselves. The following describes only the network configuration process.\) Choose Authorization Login \> Authorization and Login \> SoftAP Device to discover new devices \(Hi-xxx-Switchs-xxxxx in the following figure\). - - **Figure 5** Discover Device page - - - ![](figures/en-us_image_0000001054132216.png) - -3. Tap the device icon and wait for the app to automatically switch to the page for selecting a Wi-Fi hotspot. Select a hotspot, enter the password, and click.... The device receives the network configuration data, as shown in the following figure. - - **Figure 6** Device configuration page - - - ![](figures/en-us_image_0000001054452241.png) - - -## FAQs - -None - diff --git a/docs-en/guide/developing-the-first-application-program-running-on-the-hi3516-development-board.md b/docs-en/guide/developing-the-first-application-program-running-on-the-hi3516-development-board.md deleted file mode 100755 index d007424c19126493524be71b1fbcc28d770f5a91..0000000000000000000000000000000000000000 --- a/docs-en/guide/developing-the-first-application-program-running-on-the-hi3516-development-board.md +++ /dev/null @@ -1,177 +0,0 @@ -# Developing the First Application Program running on the Hi3516 Development Board - -This section describes how to modify, compile, burn, and run the first application on the board. - -## Obtaining the Source Code - -You need to download a set of source code from the Linux server. For details, see [Source Code Acquisition](en-us_topic_0000001050769927.md). - -## Modifying an application - -The **helloworld.c** code in the **applications/sample/camera/app/src** directory is as following. You can customize the content to be printed. For example, you can change OHOS to World. The current applications can be developed using standard ISO C and C++. - -``` -#include -#include "los_sample.h" - -int main(int argc, char **argv) -{ - printf("\n************************************************\n"); - printf("\n\t\tHello OHOS!\n"); - printf("\n************************************************\n\n"); - - LOS_Sample(g_num); - - return 0; -} -``` - -## Compiling - -On the Linux server, go to the root directory of the source code package. The build.py compilation script is stored in the directory. Run the following script to compile the source code package: - -Run the following script in the root directory of the source code package to compile the source code package. The result file is generated in the out/ipcamera\_hi3516dv300 directory. - -``` -python build.py ipcamera_hi3516dv300 -b debug -``` - -## Burning - -Network Burning Mode - -This method applies only to the board \(such as Hi3516D V300\) that supports the network port. In addition, the PC and the board must be connected by using a network cable and configured on the same network. - ->![](public_sys-resources/icon-notice.gif) **NOTICE:** ->The Visual Studio Code software connects the board to the network. If the board cannot connect to the computer network, check the firewall settings. For details, see FAQ 2. - -1. Install the USB-to-serial adapter driver and obtain the serial port number. - - **Figure 1** Successful driver installation - ![](figures/successful-driver-installation.png "successful-driver-installation") - - 1. Power on the board and connect the serial port cable of the board to the Windows console. - 2. Install the driver and obtain the driver link. - 3. Open Device Manager, then check and record the value of **Prolific USB-to-Serial Comm Port**. - - After the driver is successfully installed, if a warning icon is displayed on the device icon, right-click the device, uninstall the driver, reinstall the driver, and restart the computer as prompted. - - -2. On the Windows console, add the IP address 192.168.1.3 for the interconnection network port of the board. The method is as follows: - - **Figure 2** Adding a Windows host IP address - ![](figures/adding-a-windows-host-ip-address.png "adding-a-windows-host-ip-address") - - 1. Choose Control Panel \> Network and Internet-\> Network Connections, right-click the network adapter connected to the board, and choose Properties from the shortcut menu. - 2. Double-click Internet 协议版本4(TCP/IPv4). - 3. Configure the IP address and gateway according to the preceding figure. - 4. Click OK to save the configuration. - -3. hi3516dv300 is added to the board list. After you choose Board Configure and enable a board, the board configuration table is automatically added. - - **Figure 3** Add a board. - ![](figures/add-a-board.png "add-a-board") - -4. Start the IDE and click the icons in sequence to configure the contents to be burnt over the network. - - **Figure 4** Network configuration diagram of the IDE tool - - - ![](figures/ide.png) - - 1. Select Hi3516 as the board type. - 2. Click Burn. - 3. Set Burning Mode to "network". - 4. Host IP Address: Click to refresh the page and select 192.168.1.3 from the drop-down list box. - -5. Select the chip to be burnt to the flash memory and the burning address. - - **Figure 5** Setting the parameters for burning files - - - ![](figures/ide2.png) - - 1. Select emmc from the Memory Type drop-down list box as the flash memory type. - 2. Click New to add three files. Enter the OHOS\_Image.bin, rootfs.img, and userfs.img files in the file path in sequence. Set the start address and file length as shown in the following figure, obtain the file from the out/ipcamera\_hi3516dv300 directory. - 3. Click Save to save the changes. - 4. Click Burn on the left to start burning. - -6. Select a serial port number from the drop-down list box displayed in the upper part, for example, COM11. - - **Figure 6** Selecting the serial port to be connected to the board - ![](figures/selecting-the-serial-port-to-be-connected-to-the-board.png "selecting-the-serial-port-to-be-connected-to-the-board") - -7. The burning starts. If a message is displayed, you need to manually restart the board \(by powering off and then powering on the board\). - - **Figure 7** The system prompts you to power off the board and power on the board again. - - - ![](figures/reset2.png) - -8. Burning is complete. - - **Figure 8** Successful burning - ![](figures/successful-burning.png "successful-burning") - - -## Running an Image - -1. Connect a serial port cable. - - **Figure 9** Connect the serial port cables. - - - ![](figures/chuankou1.png) - - 1. Click Serial port to enable the serial port. - 2. Enter the serial port number of the "com11" and press Enter until hisillicon is displayed. - 3. If the board is started for the first time or the startup parameters are modified, go to step 2. Otherwise, go to step 3. - -2. \(Mandatory when the board is started for the first time\) Modify the bootcmd and bootargs of the U-boot. This step is a fixed operation. If the parameters are not modified, you need to perform this step only once. The system automatically enters the system each time the board is reset. - - >![](public_sys-resources/icon-notice.gif) **NOTICE:** - >By default, the U-boot boot program waits for two seconds. You can press Enter to interrupt the waiting and the message "hisillicon" is displayed. You can run the reset command to restart the system. - - **Table 1** U-boot startup parameters - - -

Development Board

Version Information

+

Content

Site

+

Version Information

SHA-256 Verification Code

+

Site

+

SHA-256 Verification Code

llvm

+

Hi3861

+

gcc_riscv32

9.0.0-34042

+

7.3.0

Site

+

Site

64a518b50422b6f1ba8f6f56a5e303fb8448a311211ba10c385ad307a1d2546f

+

966fd4fda68d9886b828e6eef3ac3620806a34d3bccba4020a2ef07d9b8b8826

gcc_riscv32

+

Hi3861, Hi3516, and Hi3518

+

gn

7.3.0

+

1523

Site

+

Site

614ee086ead1a4fd7384332b85dd62707801f323de60dfdb61503f473d470a24

+

50a5a5ba5877dd0ec8afcb23d3dd4d966a16403c29cd80a4002230241d32ef34

gn

+

Hi3861, Hi3516, and Hi3518

1523

+

ninja

Site

+

1.9.0

50a5a5ba5877dd0ec8afcb23d3dd4d966a16403c29cd80a4002230241d32ef34

+

Site

+

b4a4ba21e94ff77634e1f88697a00b6f498fdbc0b40d7649df1b246b285874f9

ninja

+

Hi3516 and Hi3518

+

llvm

1.9.0

+

9.0.0-34042

Site

+

Site

b4a4ba21e94ff77634e1f88697a00b6f498fdbc0b40d7649df1b246b285874f9

+

64a518b50422b6f1ba8f6f56a5e303fb8448a311211ba10c385ad307a1d2546f

hc-gen

+

Hi3516 and Hi3518

+

hc-gen

0.65

+

0.65

Site

+

Site

fcfee489371947a464fe41a4b45a897b9a44155891a957f15bad2e157c750162

+

fcfee489371947a464fe41a4b45a897b9a44155891a957f15bad2e157c750162

- - - - - - -

Command

-

setenv bootcmd "sf probe 0;mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

saveenv

-

reset

-

Description

-

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800;go 0x80000000";

-

This command indicates that flash 0 is selected, and the contents with the start address of 0x800 (unit: 512 bytes, that is, 1 MB) and the size of 0x4800 (unit: 512 bytes, that is, 9 MB) are read to the memory address 0x80000000.

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

This command sets the boot parameters to serial port output, baud rate to 115200, data bit to 8, rootfs to be mounted to the eMMC, and file system type to vfat,

-

Enter the start burning position and length of rootfs.img in rootaddr=10M rootsize=15M rw. The size must be the same as that of the rootfs.img file added in the IDE.

-

saveenv indicates that the current configuration is saved.

-

reset indicates that the board is reset.

-

(Optional) go 0x40000000 indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, you can press Enter in the countdown phase of the U-Boot startup to interrupt the automatic startup.

-
- -3. Enter reset and press Enter to restart the board. After the board is restarted successfully, press Enter. OHOS is displayed. - - **Figure 10** Start the system - - - ![](figures/qi1.png) - - -## Run the program. - -In the root directory, run the **./bin/hello\_uart** command line to operate the demo program. The following figure shows the compilation result. - -**Figure 11** Why are apps on my phone launching slowly? - - -![](figures/qidong.png) - diff --git a/docs-en/guide/developing-the-first-driver-program-running-on-the-hi3516-development-board.md b/docs-en/guide/developing-the-first-driver-program-running-on-the-hi3516-development-board.md deleted file mode 100755 index f1b4fd659d6b227d099965e847ff288fab470a5c..0000000000000000000000000000000000000000 --- a/docs-en/guide/developing-the-first-driver-program-running-on-the-hi3516-development-board.md +++ /dev/null @@ -1,560 +0,0 @@ -# Developing the First Driver Program running on the Hi3516 Development Board - -This section describes how to develop a driver program on the board, including introduction, compilation, burning, and operation of the driver. - -## Obtaining the Source Code - -You need to download a set of source code from the Linux server. For details, see [Source Code Acquisition](en-us_topic_0000001050769927.md). - -## Introduction to Driver - -The following example shows how to add configuration files, implement the driver code, and compile the code for interaction between the user-space applications and the driver based on the UART driver. The driver source code is stored in the **vendor/huawei/hdf/sample** directory. - -1. Add Configurations. - - The following example shows how to add the driver configurations to the HDF configuration file \(for example, **vendor/hisi/hi35xx/hi3516dv300/config/uart/uart\_config.hcs**\). - - ``` - root { - platform { - uart_sample { - num = 5; // UART Device No. - base = 0x120a0000; // UART registers - irqNum = 38; - baudrate = 115200; - uartClk = 24000000; - wlen = 0x60; - parity = 0; - stopBit = 0; - match_attr = "sample_uart_5"; - } - } - } - ``` - - This example shows how to add the driver nodes to the HDF configuration file \(for example, **vendor/hisi/hi35xx/hi3516dv300/config/device\_info/device\_info.hcs**\). - - ``` - root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_uart :: device { - device5 :: deviceNode { - policy = 1; - priority = 40; - permission = 0644; - moduleName = "UART_SAMPLE"; - serviceName = "HDF_PLATFORM_UART_5"; - deviceMatchAttr = "sample_uart_5"; - } - } - } - } - } - ``` - - >![](public_sys-resources/icon-note.gif) **NOTE:** - >The configuration file and the source code of the UART driver sample are in the same path. You need to manually add them to the path of the Hi3516D V300 board. - -2. Register a UART driver entry. - - The following example shows how to register the **HdfDriverEntry** entry of the UART driver based on the HDF. - - ``` - // Bind the UART driver interface to the HDF. - static int32_t HdfUartSampleBind(struct HdfDeviceObject *device) - { - if (device == NULL) { - return HDF_ERR_INVALID_OBJECT; - } - HDF_LOGI("Enter %s:", __func__); - return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; - } - - // Obtain configuration information from the HCS of the UART driver. - static uint32_t UartDeviceGetResource( - struct UartDevice *device, const struct DeviceResourceNode *resourceNode) - { - struct UartResource *resource = &device->resource; - struct DeviceResourceIface *dri = NULL; - dri = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (dri == NULL || dri->GetUint32 == NULL) { - HDF_LOGE("DeviceResourceIface is invalid"); - return HDF_FAILURE; - } - - if (dri->GetUint32(resourceNode, "num", &resource->num, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read num fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "base", &resource->base, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read base fail"); - return HDF_FAILURE; - } - resource->physBase = (unsigned long) OsalIoRemap(resource->base, 0x48); - if (resource->physBase == 0) { - HDF_LOGE("uart config fail to remap physBase"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "irqNum", &resource->irqNum, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read irqNum fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "baudrate", &resource->baudrate, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read baudrate fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "wlen", &resource->wlen, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read wlen fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "parity", &resource->parity, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read parity fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "stopBit", &resource->stopBit, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read stopBit fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "uartClk", &resource->uartClk, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read uartClk fail"); - return HDF_FAILURE; - } - return HDF_SUCCESS; - } - - // Attach the configuration and interfaces of the UART driver to the HDF. - static int32_t SampleAttach(struct UartHost *host, struct HdfDeviceObject *device) - { - int32_t ret; - struct UartDevice *uartDevice = NULL; - if (device->property == NULL) { - HDF_LOGE("%s: property is NULL", __func__); - return HDF_FAILURE; - } - uartDevice = (struct UartDevice *) OsalMemCalloc(sizeof(struct UartDevice)); - if (uartDevice == NULL) { - HDF_LOGE("%s: OsalMemCalloc uartDevice error", __func__); - return HDF_ERR_MALLOC_FAIL; - } - ret = UartDeviceGetResource(uartDevice, device->property); - if (ret != HDF_SUCCESS) { - (void) OsalMemFree(uartDevice); - return HDF_FAILURE; - } - host->num = uartDevice->resource.num; - host->priv = uartDevice; - UartSampleAddDev(host); // Add a user-space UART node. For details, see the source code uart_dev_sample. - return UartDeviceInit(uartDevice); // Initialize UART PL011. For details, see the source code uart_pl011_sample. - } - - // Initialize the UART driver. - static int32_t HdfUartSampleInit(struct HdfDeviceObject *device) - { - int32_t ret; - struct UartHost *host = NULL; - - if (device == NULL) { - HDF_LOGE("%s: device is NULL", __func__); - return HDF_ERR_INVALID_OBJECT; - } - HDF_LOGI("Enter %s:", __func__); - host = UartHostFromDevice(device); - if (host == NULL) { - HDF_LOGE("%s: host is NULL", __func__); - return HDF_FAILURE; - } - ret = SampleAttach(host, device); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: attach error", __func__); - return HDF_FAILURE; - } - host->method = &g_uartSampleHostMethod; - return ret; - } - - static void UartDeviceDeinit(struct UartDevice *device) - { - struct UartRegisterMap *regMap = (struct UartRegisterMap *) device->resource.physBase; - /* wait for uart enter idle. */ - while (UartPl011IsBusy(regMap)); - UartPl011ResetRegisters(regMap); - uart_clk_cfg(0, false); - OsalIoUnmap((void *) device->resource.physBase); - device->state = UART_DEVICE_UNINITIALIZED; - } - - // Unbind and detach the UART driver. - static void SampleDetach(struct UartHost *host) - { - struct UartDevice *uartDevice = NULL; - - if (host->priv == NULL) { - HDF_LOGE("%s: invalid parameter", __func__); - return; - } - uartDevice = host->priv; - UartDeviceDeinit(uartDevice); - (void) OsalMemFree(uartDevice); - host->priv = NULL; - } - - // Release the UART driver. - static void HdfUartSampleRelease(struct HdfDeviceObject *device) - { - struct UartHost *host = NULL; - HDF_LOGI("Enter %s:", __func__); - - if (device == NULL) { - HDF_LOGE("%s: device is null", __func__); - return; - } - host = UartHostFromDevice(device); - if (host == NULL) { - HDF_LOGE("%s: host is null", __func__); - return; - } - if (host->priv != NULL) { - SampleDetach(host); - } - UartHostDestroy(host); - } - - struct HdfDriverEntry g_hdfUartSample = { - .moduleVersion = 1, - .moduleName = "UART_SAMPLE", - .Bind = HdfUartSampleBind, - .Init = HdfUartSampleInit, - .Release = HdfUartSampleRelease, - }; - - HDF_INIT(g_hdfUartSample); - ``` - -3. Register a UART driver interface. - - The following example shows how to implement the UART driver interface using the template **UartHostMethod** provided by the HDF. - - ``` - static int32_t SampleInit(struct UartHost *host) - { - HDF_LOGI("%s: Enter", __func__); - if (host == NULL) { - HDF_LOGE("%s: invalid parameter", __func__); - return HDF_ERR_INVALID_PARAM; - } - return HDF_SUCCESS; - } - - static int32_t SampleDeinit(struct UartHost *host) - { - HDF_LOGI("%s: Enter", __func__); - if (host == NULL) { - HDF_LOGE("%s: invalid parameter", __func__); - return HDF_ERR_INVALID_PARAM; - } - return HDF_SUCCESS; - } - - // Write data into the UART. - static int32_t SampleWrite(struct UartHost *host, uint8_t *data, uint32_t size) - { - HDF_LOGI("%s: Enter", __func__); - uint32_t idx; - struct UartRegisterMap *regMap = NULL; - struct UartDevice *device = NULL; - - if (host == NULL || data == NULL || size == 0) { - HDF_LOGE("%s: invalid parameter", __func__); - return HDF_ERR_INVALID_PARAM; - } - device = (struct UartDevice *) host->priv; - if (device == NULL) { - HDF_LOGE("%s: device is NULL", __func__); - return HDF_ERR_INVALID_PARAM; - } - regMap = (struct UartRegisterMap *) device->resource.physBase; - for (idx = 0; idx < size; idx++) { - while (UartPl011IsBusy(regMap)); - UartPl011Write(regMap, data[idx]); - } - return HDF_SUCCESS; - } - - // Set the baud rate of the UART. - static int32_t SampleSetBaud(struct UartHost *host, uint32_t baudRate) - { - HDF_LOGI("%s: Enter", __func__); - struct UartDevice *device = NULL; - struct UartRegisterMap *regMap = NULL; - UartPl011Error err; - - if (host == NULL) { - HDF_LOGE("%s: invalid parameter", __func__); - return HDF_ERR_INVALID_PARAM; - } - device = (struct UartDevice *) host->priv; - if (device == NULL) { - HDF_LOGE("%s: device is NULL", __func__); - return HDF_ERR_INVALID_PARAM; - } - regMap = (struct UartRegisterMap *) device->resource.physBase; - if (device->state != UART_DEVICE_INITIALIZED) { - return UART_PL011_ERR_NOT_INIT; - } - if (baudRate == 0) { - return UART_PL011_ERR_INVALID_BAUD; - } - err = UartPl011SetBaudrate(regMap, device->uartClk, baudRate); - if (err == UART_PL011_ERR_NONE) { - device->baudrate = baudRate; - } - return err; - } - - // Obtain the baud rate of the UART. - static int32_t SampleGetBaud(struct UartHost *host, uint32_t *baudRate) - { - HDF_LOGI("%s: Enter", __func__); - struct UartDevice *device = NULL; - - if (host == NULL) { - HDF_LOGE("%s: invalid parameter", __func__); - return HDF_ERR_INVALID_PARAM; - } - device = (struct UartDevice *) host->priv; - if (device == NULL) { - HDF_LOGE("%s: device is NULL", __func__); - return HDF_ERR_INVALID_PARAM; - } - *baudRate = device->baudrate; - return HDF_SUCCESS; - } - - // Bind the UART using HdfUartSampleInit. - struct UartHostMethod g_uartSampleHostMethod = { - .Init = SampleInit, - .Deinit = SampleDeinit, - .Read = NULL, - .Write = SampleWrite, - .SetBaud = SampleSetBaud, - .GetBaud = SampleGetBaud, - .SetAttribute = NULL, - .GetAttribute = NULL, - .SetTransMode = NULL, - }; - ``` - - The following sample code shows how to add the module of the UART driver to the compilation script **vendor/huawei/hdf/hdf\_vendor.mk**. - - ``` - LITEOS_BASELIB += -lhdf_uart_sample - LIB_SUBDIRS += $(VENDOR_HDF_DRIVERS_ROOT)/sample/platform/uart - ``` - -4. Compile the code for interaction between the user-space applications and driver. - - Create the **/dev/uartdev-5** node after the UART driver is initialized successfully. The following example shows how to interact with the UART driver through the node. - - ``` - #include - #include - #include - #include "hdf_log.h" - - #define HDF_LOG_TAG "hello_uart" - #define INFO_SIZE 16 - - int main(void) - { - int ret; - int fd; - const char info[INFO_SIZE] = {" HELLO UART! "}; - - fd = open("/dev/uartdev-5", O_RDWR); - if (fd < 0) { - HDF_LOGE("hello_uart uartdev-5 open failed %d", fd); - return -1; - } - ret = write(fd, info, INFO_SIZE); - if (ret != 0) { - HDF_LOGE("hello_uart write uartdev-5 ret is %d", ret); - } - ret = close(fd); - if (ret != 0) { - HDF_LOGE("hello_uart uartdev-5 close failed %d", fd); - return -1; - } - return ret; - } - ``` - - The following example shows how to add the **hello\_uart\_sample** component to the **hdf** subsystem in the **build/lite/product/ipcamera\_hi3516dv300.json** file. - - ``` - { - "subsystem": [ - { - "name": "hdf", - "component": [ - { "name": "hdf_sample", "dir": "//vendor/huawei/hdf/sample/platform/uart:hello_uart_sample", "feature":[] } - ] - } - ] - } - ``` - - >![](public_sys-resources/icon-note.gif) **NOTE:** - >The preceding code is only a sample code. You can view the complete code in **vendor/huawei/hdf/sample**. - >The sample code is not automatically compiled by default. You need to manually add it to the compilation script. - - -## Compiling - -On the Linux server, go to the root directory of the source code package. The build.py compilation script is stored in the directory. Run the following script to compile the source code package: - -Run the following script in the root directory of the source code package to compile the source code package. The result file is generated in the out/ipcamera\_hi3516dv300 directory. - -``` -python build.py ipcamera_hi3516dv300 -b debug -``` - -## Burning - -Network Burning Mode - -This method applies only to the board \(such as Hi3516D V300\) that supports the network port. In addition, the PC and the board must be connected by using a network cable and configured on the same network. - ->![](public_sys-resources/icon-notice.gif) **NOTICE:** ->The Visual Studio Code software connects the board to the network. If the board cannot connect to the computer network, check the firewall settings. For details, see FAQ 2. - -1. Install the USB-to-serial adapter driver and obtain the serial port number. - - **Figure 1** Successful driver installation - ![](figures/successful-driver-installation.png "successful-driver-installation") - - 1. Power on the board and connect the serial port cable of the board to the Windows console. - 2. Install the driver and obtain the driver link. - 3. Open Device Manager, then check and record the value of **Prolific USB-to-Serial Comm Port**. - - After the driver is successfully installed, if a warning icon is displayed on the device icon, right-click the device, uninstall the driver, reinstall the driver, and restart the computer as prompted. - - -2. On the Windows console, add the IP address 192.168.1.3 for the interconnection network port of the board. The method is as follows: - - **Figure 2** Adding a Windows host IP address - ![](figures/adding-a-windows-host-ip-address.png "adding-a-windows-host-ip-address") - - 1. Choose Control Panel \> Network and Internet-\> Network Connections, right-click the network adapter connected to the board, and choose Properties from the shortcut menu. - 2. Double-click Internet 协议版本4(TCP/IPv4). - 3. Configure the IP address and gateway according to the preceding figure. - 4. Click OK to save the configuration. - -3. hi3516dv300 is added to the board list. After you choose Board Configure and enable a board, the board configuration table is automatically added. - - **Figure 3** Add a board. - ![](figures/add-a-board.png "add-a-board") - -4. Start the IDE and click the icons in sequence to configure the contents to be burnt over the network. - - **Figure 4** Network configuration diagram of the IDE tool - - - ![](figures/ide.png) - - 1. Select Hi3516 as the board type. - 2. Click Burn. - 3. Set Burning Mode to "network". - 4. Host IP Address: Click to refresh the page and select 192.168.1.3 from the drop-down list box. - -5. Select the chip to be burnt to the flash memory and the burning address. - - **Figure 5** Setting the parameters for burning files - - - ![](figures/ide2.png) - - 1. Select emmc from the Memory Type drop-down list box as the flash memory type. - 2. Click New to add three files. Enter the OHOS\_Image.bin, rootfs.img, and userfs.img files in the file path in sequence. Set the start address and file length as shown in the following figure, obtain the file from the out/ipcamera\_hi3516dv300 directory. - 3. Click Save to save the changes. - 4. Click Burn on the left to start burning. - -6. Select a serial port number from the drop-down list box displayed in the upper part, for example, COM11. - - **Figure 6** Selecting the serial port to be connected to the board - ![](figures/selecting-the-serial-port-to-be-connected-to-the-board.png "selecting-the-serial-port-to-be-connected-to-the-board") - -7. The burning starts. If a message is displayed, you need to manually restart the board \(by powering off and then powering on the board\). - - **Figure 7** The system prompts you to power off the board and power on the board again. - - - ![](figures/reset2.png) - -8. Burning is complete. - - **Figure 8** Successful burning - ![](figures/successful-burning.png "successful-burning") - - -## Running an Image - -1. Connect a serial port cable. - - **Figure 9** Connect the serial port cables. - - - ![](figures/chuankou1.png) - - 1. Click Serial port to enable the serial port. - 2. Enter the serial port number of the "com11" and press Enter until hisillicon is displayed. - 3. If the board is started for the first time or the startup parameters are modified, go to step 2. Otherwise, go to step 3. - -2. \(Mandatory when the board is started for the first time\) Modify the bootcmd and bootargs of the U-boot. This step is a fixed operation. If the parameters are not modified, you need to perform this step only once. The system automatically enters the system each time the board is reset. - - >![](public_sys-resources/icon-notice.gif) **NOTICE:** - >By default, the U-boot boot program waits for two seconds. You can press Enter to interrupt the waiting and the message "hisillicon" is displayed. You can run the reset command to restart the system. - - **Table 1** U-boot startup parameters - - - - - - - - - -

Command

-

setenv bootcmd "sf probe 0;mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

saveenv

-

reset

-

Description

-

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800;go 0x80000000";

-

This command indicates that flash 0 is selected, and the contents with the start address of 0x800 (unit: 512 bytes, that is, 1 MB) and the size of 0x4800 (unit: 512 bytes, that is, 9 MB) are read to the memory address 0x80000000.

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

This command sets the boot parameters to serial port output, baud rate to 115200, data bit to 8, rootfs to be mounted to the eMMC, and file system type to vfat,

-

Enter the start burning position and length of rootfs.img in rootaddr=10M rootsize=15M rw. The size must be the same as that of the rootfs.img file added in the IDE.

-

saveenv indicates that the current configuration is saved.

-

reset indicates that the board is reset.

-

(Optional) go 0x40000000 indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, you can press Enter in the countdown phase of the U-Boot startup to interrupt the automatic startup.

-
- -3. Enter reset and press Enter to restart the board. After the board is restarted successfully, press Enter. OHOS is displayed. - - **Figure 10** Start the system - - - ![](figures/qi1.png) - -4. In the root directory, run the **./bin/hello\_uart** command line to operate the demo program. The following figure shows the compilation result. - - ``` - OHOS # ./bin/hello_uart - OHOS # HELLO UART! - ``` - - diff --git a/docs-en/guide/development-and-compilation.md b/docs-en/guide/development-and-compilation.md deleted file mode 100755 index f912a324c8bed4852988f1efd3b3d772f101e51f..0000000000000000000000000000000000000000 --- a/docs-en/guide/development-and-compilation.md +++ /dev/null @@ -1,272 +0,0 @@ -# Development and Compilation - -The examples of the insensitive network configuration are stored in the applications/sample/wifi-iot/app/network\_config/network\_config\_sample.c file. According to, the service process of the non-sense distribution network can be divided into two parts: distribution network service life cycle management and distribution network service callback event processing. - -**Figure 1** Sample flowchart of the non-sensitive power distribution network - - -![](figures/en-us_image_0000001054690925.png) - -**Table 1** Event Function Description - - - - - - - - - - - - - - - - - - - - - - -

Event Type

-

Function

-

GetPinCode

-

This interface is used to establish a secure channel between a device and a mobile phone. It is used for callback during encryption key negotiation. The security PIN must be preset.

-

ParseNetCfgData

-

After a connection is set up between the device and mobile phone, the mobile app can send the SSID password to the device. After receiving an SSID password message from a mobile phone, the device invokes this interface to notify the device application.

-

OnDisconnect

-

The device is disconnected from the mobile phone in the following scenarios:

-
  1. After sending the SSID password, the mobile app sends a disconnection command to the device.
  2. The heartbeat between the device and the NAN channel of the mobile phone times out, and the device proactively disconnects from the NAN channel.
-

RecvRawData

-

After the device establishes a connection to the mobile phone, the mobile phone app may send a custom message (for example, a control message) to the device. After receiving such a message, the device transfers the message to the device application through this interface. The device application calls the SendRawData API to send a response to the application.

-

NotifyNetCfgStatus

-

After a device is successfully connected to a mobile phone, this interface is used to instruct the device application to blink or light the indicator to notify the device that the device is in the network configuration state.

-
- -## Distribution network service lifecycle management - -Distribution network service life cycle management includes creation and release of distribution network services. - -1. Before the network distribution starts, complete network distribution event registration \(configure the callback function\), set the SoftAp hotspot parameters, and start the network distribution service. - - The network configuration callback event consists of four callback functions: GetPinCode, ParseNetCfgData, OnDisconnect, RecvRawData and NotifyNetCfgStatus. This interface is invoked when the network configuration service receives the SSID message, connection release message, second control message, and connection setup message from the mobile phone. You are advised to register all event callback interfaces. If you do not need to pay attention to an event callback interface, you can implement an empty function or set it to NULL, and then register the event by using the RegNetCfgCallback function. - - When setting the hotspot parameters of the SoftAP, pay attention to the input parameters of the int SetSoftAPParameter\(const struct SoftAPParam \*param\) function. For details, see the API description of the NetcfgService module. Note: If the SoftAP does not need to be enabled for the network configuration service, set all the values of the ssid field to 0. - - When starting the network configuration service, pay attention to the three input parameters of the int StartNetCfg\(const struct DevInfo \*devInfoList, unsigned int listSize, enum NetCfgType mode\) function. - - **Table 2** Parameter structure of the int StartNetCfg type - - - - - - - - - - - - - - - - -

Input Parameter

-

Description

-

devInfoList

-

Indicates the device information list. Currently, the device information to be configured includes productId and sn. The device information is configured in the format of key|value. Example:

-

struct DevInfo devInfo[2];

-

memset(&devInfo, 0, sizeof(devInfo));

-

devInfo[0].key = "productId";

-

devInfo[0].value = "1";

-

devInfo[1].key = "sn";

-

devInfo[1].value = "01234567890123456";

-

listSize

-

Number of parameters in the device information list, that is, the size of the devInfo array. Ensure that the size of the devInfo array is correct.

-

mode

-

Indicates the network configuration mode. Currently, two network configuration modes are provided: single SoftAP network configuration mode and both SoftAP and non-sensitive network configuration mode. Device application developers can select a network configuration mode as required.

-
- - >![](public_sys-resources/icon-caution.gif) **CAUTION:** - >To ensure the normal use of the perception-free network configuration mode, ensure that the enabled AP interface works on channel 6, which complies with the NAN protocol. - - Set SoftAp parameters, register the network configuration event, and start the network configuration service. The following is the code for starting the network configuration service \(using the simultaneous NAN and SoftAp network configuration mode as an example\): - - ``` - static void *SampleBizTask(const char *arg) - { - (void)arg; - int ret; - LedInit(); - /*Set the transmit power for the NAN safe distance.*/ - ret = SetSafeDistancePower(POWER_NUM); - if (ret != 0) { - printf("[sample] Set safe distance power failed\n"); - return NULL; - } - /* Set the SoftAP hotspot configuration parameters. In the example, the SSID and encryption type are set to open. */ - struct SoftAPParam config = {0}; - memset_s(&config, sizeof(struct SoftAPParam), 0, sizeof(struct SoftAPParam)); - strncpy_s(config.ssid, sizeof(config.ssid), g_ssid, strlen(g_ssid)); - config.authType = WIFI_SECURITY_OPEN; - ret = SetSoftAPParameter(&config); - if (ret != 0) { - printf("[sample] Set softAP parameters failed\n"); - return NULL; - } - /* Register the network configuration event callback function. */ - NetCfgCallback hook; - memset_s(&hook, sizeof(NetCfgCallback), 0, sizeof(NetCfgCallback)); - hook.GetPinCode = GetPinCode; - hook.ParseNetCfgData = ParseNetCfgData; - hook.RecvRawData = RecvRawData; - hook.NotifyNetCfgStatus = NotifyNetCfgStatus; - ret = RegNetCfgCallback(&hook); - if (ret != 0) { - printf("[sample] Regist config callback failed\n"); - return NULL; - } - /* Construct device information parameters. */ - struct DevInfo devInfo[DEVICE_INFO_NUM]; - memset_s(&devInfo, sizeof(devInfo), 0, sizeof(devInfo)); - devInfo[0].key = "productId"; - devInfo[1].key = "sn"; - devInfo[0].value = g_productId; - devInfo[1].value = g_sn; - /*Start the distribution network service.*/ - ret = StartNetCfg(devInfo, DEVICE_INFO_NUM, NETCFG_SOFTAP_NAN); - if (ret != 0) { - printf("[sample] Start config wifi fail.\n"); - return NULL; - } - while (1) { - printf("[sample] main biz.\n"); - usleep(SAMPLE_BIZ_SLEEP_TIME_US * SAMPLE_TIME_COUNT); - } - return NULL; - } - ``` - -2. After the network configuration is complete, stop the network configuration service. - - When a user does not need to configure the network, the int StopNetCfg\(void\) interface is used to release services and resources. - - ``` - StopNetCfg(); - ``` - - -## Processing of distribution network service callback events - -After the distribution network service is successfully started in the previous step, the user program needs to wait for the event callback of the distribution network service. - -1. ParseNetCfgData event callback indicates that after receiving the SSID and password information from the mobile phone, the network configuration service notifies the user program by using the function pointer. The following figure shows the process of processing SSID information in the sample. - - ``` - int ParseNetCfgData(const struct WifiInfo *wifiInfo, const unsigned char *vendorData, unsigned int len) - { - printf("[NetCfgService] ParseWifiData vendorData len:%d\n", len); - if (wifiInfo == NULL) { - printf("[NetCfgService] wifiInfo is NULL\n"); - return -1; - } - WifiDeviceConfig netConfig; - memset_s(&netConfig, sizeof(netConfig), 0, sizeof(netConfig)); - if (memcpy_s(netConfig.ssid, WIFI_MAX_SSID_LEN, wifiInfo->ssid, strlen(wifiInfo->ssid)) != 0) { - printf("[NetCfgService] copy ssid failed\n"); - return -1; - } - if (memcpy_s(netConfig.preSharedKey, WIFI_MAX_KEY_LEN, wifiInfo->pwd, strlen(wifiInfo->pwd)) != 0) { - printf("[NetCfgService] copy pwd failed\\n"); - return -1; - } - if (vendorData != NULL) { - /* Developer-defined data, which is parsed and processed by the developer */ - } - DealSsidPwd(&netConfig); - return 0; - } - ``` - -2. OnDisconnect indicates that the application is notified that the mobile phone is disconnected from the device. This sample is not processed. You can plan it based on your service requirements. - - ``` - static void DisconnectEventProc(void) - { - /* In the sample, only printing is performed. You can add service logic as required. */ - printf("[sample] oncall disconnect event.\n"); - } - ``` - -3. RecvRawData: The mobile phone sends a customized message to the device. The message content is defined by developers. The distribution network service is responsible for end-to-end message transmission and secure encryption and decryption. - - ``` - int RecvRawData(const char *data) - { - if (data == NULL) { - return -1; - } - printf("RecvRawData data : %s \n", data); - return 0; - } - ``` - -4. NotifyNetCfgStatus indicates that the mobile phone is successfully connected to the network configuration service on the device. - - ``` - void NotifyNetCfgStatus (enum NetCfgStatus status) - { - printf("[sample]Recv NetCfg Status : %d \n", status); - return; - } - ``` - - -## Compilation construction - -1. The service macro HARMONY\_SUPPORT is defined in the /vendor/hisi/wifi-iot/hi3861/app/demo/src/app\_main.c file. At the end of the app\_main\(\) function, the entry function NetCfgSampleBiz\(\) of the non-sense distribution network sample is called to start the service of the non-sense distribution network sample. - - ``` - #define HARMONY_SUPPORT - hi_void app_main(hi_void) - { - ... - ... - extern void NetCfgSampleBiz(void); - NetCfgSampleBiz(); - } - ``` - -2. Open the encryption and decryption module TEEHUKS, run the sh build.sh menuconfig command in the code directory /vendor/hisi/wifi-iot/hi3861/, and select and save the TEEHUKS support configuration under Security Settings, as shown in the following figure. - - ``` - (Top) - Main menu - Target Chip ---> - Security Settings ---> - Factory Test Settings ---> - BSP Settings ---> - WiFi Settings ---> - Third Party library ---> - Lwip Settings ---> - OTA Settings ---> - Link Settings ---> - Debug Log Settings ---> - ``` - - ``` - (Top) → Security Settings - Main menu - Signature Algorithm for bootloader and upgrade file (SHA256) ---> - (0) boot ver(value form 0 to 16) - (0) kernel ver(value form 0 to 48) - [*] TEE HUKS support - [ ] TEE HUKS demo support - [ ] FLASH ENCRYPT support(only apply in WIFI CHIP_PKT_32K) - ``` - -3. For details about how to compile the source code, see the Hi3861 Quick Start - Compilation. - diff --git a/docs-en/guide/development-guidelines-2.md b/docs-en/guide/development-guidelines-2.md old mode 100755 new mode 100644 index 1f4fce1db4ff86c8ec92e028878163d0997bf4f0..6b5c9a629e1720512db8fa5d6f64388b80fb33fa --- a/docs-en/guide/development-guidelines-2.md +++ b/docs-en/guide/development-guidelines-2.md @@ -1,9 +1,5 @@ # Development Guidelines -For details about basic concepts for camera development, see the camera development [Overview](en-us_topic_0000001051690589.md). - -If you want to view the development sample effect first, skip this section and see [Use Case](use-case-5.md) for video preview. To customize application behavior, modify the sample code by referring to APIs described in this section. - - **[Photographing](photographing-3.md)** - **[Video Recording](video-recording-4.md)** diff --git a/docs-en/guide/development-guidelines.md b/docs-en/guide/development-guidelines.md old mode 100755 new mode 100644 index f22ed45bc54ca38299199ae5ecfd892ae4b3c2b3..fd34356bfaf087978ff851847b5339180b529037 --- a/docs-en/guide/development-guidelines.md +++ b/docs-en/guide/development-guidelines.md @@ -1,9 +1,5 @@ # Development Guidelines -For details about basic concepts for camera development, see the camera development [Overview](en-us_topic_0000001051690589.md). - -If you want to view the development sample effect first, skip this section and see [Use Case](use-case.md). To customize application behavior, modify the sample code by referring to APIs described in this section. - - **[Photographing](photographing.md)** - **[Video Recording](video-recording.md)** diff --git a/docs-en/guide/development.md b/docs-en/guide/development.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/faqs-1.md b/docs-en/guide/faqs-1.md deleted file mode 100755 index d4db7a09f815d9e680dbf21675f00e7c4635c500..0000000000000000000000000000000000000000 --- a/docs-en/guide/faqs-1.md +++ /dev/null @@ -1,132 +0,0 @@ -# FAQs - -1. Is there a global variable that can be accessed by all pages? - - No such global variable is available for all pages. - -2. How do I obtain DOM elements? - - You can obtain DOM elements via the **ref** attribute. You can only use methods of the obtained elements and cannot change their attributes. The following figure shows an example. - - ``` - -
- . - -
- - /* index.js */ - export default { - data: { - images:[ - {src:"common/frame1.png"}, - {src:"common/frame2.png"}, - {src:"common/frame3.png"} - ] - }, - handleClick(){ - // Obtain the component through the $refs attribute. (The ref attribute of the component has been set to animator in the HML file.) - const animator = this.$refs.animator; - const state = animator.getState(); - if(state == "paused"){ - animator.resume(); - }else if(state == "stopped"){ - animator.start(); - }else{ - animator.pause(); - } - } - } - ``` - -3. How do I pass values between pages? - - You can pass values through **params** of the **router.replace** method. The sample code is as follows: - - Set **params** to the values to be passed on a page. - - ``` - router.replace({ - uri:'pages/detail/detail', // URI of the page to be redirected to. - params:{transferData:this.data} // Data to be transferred. You need to define the data amount and name. - }); - ``` - - Receive the passed values on another page. - - ``` - onInit(){ - const data = this.transferData; // Receive the transferred data by the onInit function. - } - ``` - -4. How do I scroll a list to an item? - - Call the **scrollTo** method of the list. The input parameter of this method is the index of the target item. You can specify an item index, or obtain the index through the **scrollend** event. - -5. Does the **** component support multiple lines? - - Yes. You can use the Enter key to start a new line. Alternatively, you can choose not to set the height attribute of the text, and the component automatically starts a new line based on the content. - -6. Why is a component not displayed? - - **Description** - - The component added to the **.hml** file cannot be displayed. - - **Possible Causes** - - - The width and height of the component may not be set. - - The style setting may be incorrect. - - **Solution** - - \(1\) Check whether the width and height values are set explicitly. - - \(2\) Check whether the style of the component is set correctly. - -7. How do I implement scrolling on a page? - - There are three ways to implement page scrolling: **scroll**, ****, or ****. For a root component with **scroll** set, the scrolling effect is automatically implemented when the component size exceeds the screen size. For details, see the _HarmonyOS Development Specifications_. - -8. Why do not the **left** and **top** attributes take effect? - - In addition to the root component, **left** and **top** attributes must work with the **** component. - -9. Why does dynamic binding not take effect? - - The object or its attributes are not defined before dynamic binding. - -10. How do I implement relative and absolute positioning? - - You can use the **** and **** \(with **top** and **left** attributes\) components. - -11. How do I set component transparency? - - For the **** component, set the **opacity** attribute to adjust the transparency. For other components, set the alpha value \(in RGBA color channels\) of a component. - -12. How do I display or hide a component? - - You can use **display**, **show**, or **if**. When an **if** clause evaluates to **false**, the corresponding component will be removed from the VDOM. When **show** is set to **false**, the component will be invisible during rendering, but will not be removed from the VDOM. - -13. What are the precautions for using the **margin** attribute? - - The **margin** attribute cannot be set for child components of the **** component. - -14. What are the precautions for event subscription? - - Only one page exists when the application is running. Therefore, the **router.replace** function destroys the previous page and then creates a new one. For event subscription pages, the event should be subscribed every time a page is created, and unsubscribed before a page switch. - -15. What are the precautions for using dynamic binding? - - Do not use too many dynamic bindings because they consume too much memory. - -16. How does the **loop** attribute take effect for ****? - - If the total length of the child components, except for the first and last ones, is greater than the length of ****, the **loop** attribute takes effect. - -17. What are the precautions for using an array? - - Do not include too many elements in an array. Avoid frequent operations on a large array. - - diff --git a/docs-en/guide/faqs-9.md b/docs-en/guide/faqs-9.md deleted file mode 100755 index 842cab95966b47796ef0df7b608ae40397b5b5e3..0000000000000000000000000000000000000000 --- a/docs-en/guide/faqs-9.md +++ /dev/null @@ -1,121 +0,0 @@ -# FAQs - -1. Is there a global variable that can be accessed by all pages? - - There is no such a global variable. - -2. How do I obtain DOM elements? - - You can obtain DOM elements via the **ref** attribute. You can use methods of the obtained elements but cannot change their attributes. The sample code is as follows: - - ``` - -
- . - -
- - /* index.js */ - export default { - data: { - images:[ - {src:"common/frame1.png"}, - {src:"common/frame2.png"}, - {src:"common/frame3.png"} - ] - }, - handleClick(){ - // Obtain the component through the $refs attribute. (The ref attribute of the component has been set to animator in the HML file.) - const animator = this.$refs.animator; - const state = animator.getState(); - if(state == "paused"){ - animator.resume(); - }else if(state == "stopped"){ - animator.start(); - }else{ - animator.pause(); - } - } - } - ``` - -3. How do I pass values between pages? - - You can pass values through **params** of the **router.replace** method. The sample code is as follows: - - Set **params** to the values to be passed on a page. - - ``` - router.replace({ - uri:'pages/detail/detail', // URI of the page to switch to. - params:{transferData:this.data} // Data to be transferred. You need to define the data amount and name. - }); - ``` - - Receive the passed values on another page. - - ``` - onInit(){ - const data = this.transferData; // Receive the transferred data by the onInit function. - } - ``` - -4. How do I scroll a list to an item? - - Call the **scrollTo** method of the list. The input parameter of this method is the index of the target item. You can specify an item index, or obtain the index through the **scrollend** event. - -5. Does the **** component support multiple lines? - - Yes. You can use the Enter key to start a new line. Alternatively, the component automatically starts a new line based on the content, without the need to set the height attribute of the text. - -6. Why is the component added to the **.hml** file not displayed? - - The width and height of the component may not be set, or the style setting may be incorrect. - - Verify that the width and height values are set explicitly and the style of the component is set correctly. - -7. How do I implement scrolling on a page? - - There are three ways to implement page scrolling: **scroll**, ****, or ****. For a root component with **scroll** set, the scrolling effect is automatically implemented when the component size exceeds the screen size. For details, see the development specifications. - -8. Why do not the **left** and **top** attributes take effect? - - **left** and **top** attributes must work with the **** component in addition to the root component. - -9. Why does not dynamic binding take effect? - - The object or its attributes are not defined before dynamic binding. - -10. How do I implement relative and absolute positioning? - - You can use the **** and **** \(with **top** and **left** attributes\) components. - -11. How do I set component opacity? - - For the **** component, set the **opacity** attribute. For other components, set the alpha value \(in RGBA color channels\). - -12. How do I display or hide a component? - - You can use **display**, **show**, or **if**. When an **if** clause evaluates to **false**, the corresponding component will be removed from the VDOM. When **show** is set to **false**, the component will be invisible during rendering, but will not be removed from the VDOM. - -13. What are the precautions for using the **margin** attribute? - - The **margin** attribute cannot be set for child components of the **** component. - -14. What are the precautions for event subscription? - - Only one page exists when the application is running. Therefore, the **router.replace** function destroys the previous page and then creates a new one. For event subscription pages, the event should be subscribed every time a page is created, and unsubscribed before page switching. - -15. What are the precautions for using dynamic binding? - - Do not use too many dynamic bindings because they consume too much memory. - -16. How does the **loop** attribute take effect for ****? - - If the total length of child components, except for the first and last ones, is greater than the length of ****, the **loop** attribute takes effect. - -17. What are the precautions for using an array? - - Do not include too many elements in an array. Avoid frequent operations on a large array. - - diff --git a/docs-en/guide/faqs.md b/docs-en/guide/faqs.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/add-a-board.png b/docs-en/guide/figures/add-a-board.png deleted file mode 100755 index 81a2b496aacb056d6003d0fe440e9d1dfe8f96e8..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/add-a-board.png and /dev/null differ diff --git a/docs-en/guide/figures/adding-a-page-2.png b/docs-en/guide/figures/adding-a-page-2.png deleted file mode 100755 index 76cbe1ecac3bf4e88340c0479010ace8500eaa73..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/adding-a-page-2.png and /dev/null differ diff --git a/docs-en/guide/figures/adding-a-page.png b/docs-en/guide/figures/adding-a-page.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/adding-a-windows-host-ip-address.png b/docs-en/guide/figures/adding-a-windows-host-ip-address.png deleted file mode 100755 index 55cbac151897a29072411148afb76fae70c84d41..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/adding-a-windows-host-ip-address.png and /dev/null differ diff --git a/docs-en/guide/figures/allowing-the-visual-studio-code-application-to-access-the-network.png b/docs-en/guide/figures/allowing-the-visual-studio-code-application-to-access-the-network.png deleted file mode 100755 index afc9028fbb61db82e6f1384032bb32f56ed2ec35..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/allowing-the-visual-studio-code-application-to-access-the-network.png and /dev/null differ diff --git a/docs-en/guide/figures/checking-whether-the-serial-port-is-used-by-a-terminal.png b/docs-en/guide/figures/checking-whether-the-serial-port-is-used-by-a-terminal.png deleted file mode 100755 index cfa0ceb21f5a11d459b93721f512309c9d6da2ac..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/checking-whether-the-serial-port-is-used-by-a-terminal.png and /dev/null differ diff --git a/docs-en/guide/figures/chuankou1.png b/docs-en/guide/figures/chuankou1.png deleted file mode 100755 index 485cb3054c0c08edd6738256f27ad629ce65e1d7..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/chuankou1.png and /dev/null differ diff --git a/docs-en/guide/figures/complete-project-directory-4.png b/docs-en/guide/figures/complete-project-directory-4.png deleted file mode 100755 index df9568e7bb9c43ca7d804fb31000c28269f554c9..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/complete-project-directory-4.png and /dev/null differ diff --git a/docs-en/guide/figures/complete-project-directory.png b/docs-en/guide/figures/complete-project-directory.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/disabling-the-terminal-running-the-serial-port.png b/docs-en/guide/figures/disabling-the-terminal-running-the-serial-port.png deleted file mode 100755 index 0c1f60638087d0fe56127f2f842244355afad85f..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/disabling-the-terminal-running-the-serial-port.png and /dev/null differ diff --git a/docs-en/guide/figures/en-us_image_0000001054132216.png b/docs-en/guide/figures/en-us_image_0000001054132216.png deleted file mode 100755 index 787b53d0e24f499f8d065ecd789410d386cf4167..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/en-us_image_0000001054132216.png and /dev/null differ diff --git a/docs-en/guide/figures/en-us_image_0000001054132279.png b/docs-en/guide/figures/en-us_image_0000001054132279.png deleted file mode 100755 index a760ce71eafb1cf2a2d394a7a05f2c86a731f501..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/en-us_image_0000001054132279.png and /dev/null differ diff --git a/docs-en/guide/figures/en-us_image_0000001054250955.png b/docs-en/guide/figures/en-us_image_0000001054250955.png deleted file mode 100755 index e64b56571cbc3242f83ca5977b5952ebbc514b60..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/en-us_image_0000001054250955.png and /dev/null differ diff --git a/docs-en/guide/figures/en-us_image_0000001054370936.png b/docs-en/guide/figures/en-us_image_0000001054370936.png deleted file mode 100755 index 548e3ead8163f50b15c7acda7c7b7ab8a8e9a704..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/en-us_image_0000001054370936.png and /dev/null differ diff --git a/docs-en/guide/figures/en-us_image_0000001054385319.png b/docs-en/guide/figures/en-us_image_0000001054385319.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/en-us_image_0000001054452241.png b/docs-en/guide/figures/en-us_image_0000001054452241.png deleted file mode 100755 index 0537ddc49dc9276af68137811471f8c79ad209b8..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/en-us_image_0000001054452241.png and /dev/null differ diff --git a/docs-en/guide/figures/en-us_image_0000001054570953.png b/docs-en/guide/figures/en-us_image_0000001054570953.png deleted file mode 100755 index 881670568dad29a673835d55fb32fef9e8035306..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/en-us_image_0000001054570953.png and /dev/null differ diff --git a/docs-en/guide/figures/en-us_image_0000001054690925.png b/docs-en/guide/figures/en-us_image_0000001054690925.png deleted file mode 100755 index 5cabdae2d5ebac2468360c9220545035a8841e47..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/en-us_image_0000001054690925.png and /dev/null differ diff --git a/docs-en/guide/figures/en-us_image_0000001054850904.png b/docs-en/guide/figures/en-us_image_0000001054850904.png deleted file mode 100755 index 09f1fc0a13e26214c2b83dffc09d9c54a2d5fd2c..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/en-us_image_0000001054850904.png and /dev/null differ diff --git a/docs-en/guide/figures/entering-the-page-name-3.png b/docs-en/guide/figures/entering-the-page-name-3.png deleted file mode 100755 index 026f0c61d55680baf7873b421ef9c76685f2f222..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/entering-the-page-name-3.png and /dev/null differ diff --git a/docs-en/guide/figures/entering-the-page-name.png b/docs-en/guide/figures/entering-the-page-name.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/failed-to-obtain-the-file-due-to-unavailable-connection.png b/docs-en/guide/figures/failed-to-obtain-the-file-due-to-unavailable-connection.png deleted file mode 100755 index 548e03da4b76123cb67d41cbd1de4a0f33f5ef4b..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/failed-to-obtain-the-file-due-to-unavailable-connection.png and /dev/null differ diff --git a/docs-en/guide/figures/failed-to-open-the-serial-port.png b/docs-en/guide/figures/failed-to-open-the-serial-port.png deleted file mode 100755 index 8fd80e5c67d804c3e03623a257d6ef3601fef81b..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/failed-to-open-the-serial-port.png and /dev/null differ diff --git a/docs-en/guide/figures/firewall-and-network-protection.png b/docs-en/guide/figures/firewall-and-network-protection.png deleted file mode 100755 index 775ce6fe99d4894b39f2bdd613097dcaf11a37b2..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/firewall-and-network-protection.png and /dev/null differ diff --git a/docs-en/guide/figures/front-view-of-the-hi3516-board.png b/docs-en/guide/figures/front-view-of-the-hi3516-board.png deleted file mode 100755 index f5837a7e36f69edf31bc3a3dfc2322249384ab22..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/front-view-of-the-hi3516-board.png and /dev/null differ diff --git a/docs-en/guide/figures/hardware-connections.png b/docs-en/guide/figures/hardware-connections.png deleted file mode 100755 index 5f513e3ca7d08da1cb0e5af7fc598aa959a897eb..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/hardware-connections.png and /dev/null differ diff --git a/docs-en/guide/figures/home-screen.png b/docs-en/guide/figures/home-screen.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/ide.png b/docs-en/guide/figures/ide.png deleted file mode 100755 index e30519f5e051961c391d67e53bb648916184cf63..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/ide.png and /dev/null differ diff --git a/docs-en/guide/figures/ide2.png b/docs-en/guide/figures/ide2.png deleted file mode 100755 index eb7e6970c46416e8d4d5706ce1c10de7dc19fde4..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/ide2.png and /dev/null differ diff --git a/docs-en/guide/figures/led-blinking.gif b/docs-en/guide/figures/led-blinking.gif old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/login-page.png b/docs-en/guide/figures/login-page.png deleted file mode 100755 index 74c853ec188b0f36b2122d07abe74f3b546c0d1d..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/login-page.png and /dev/null differ diff --git a/docs-en/guide/figures/modifying-the-template.png b/docs-en/guide/figures/modifying-the-template.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/network-and-firewall-setting.png b/docs-en/guide/figures/network-and-firewall-setting.png deleted file mode 100755 index 88cba0537b5431aa266364abbe19162130f4e3ca..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/network-and-firewall-setting.png and /dev/null differ diff --git a/docs-en/guide/figures/preview-effect-5.jpg b/docs-en/guide/figures/preview-effect-5.jpg deleted file mode 100755 index cce3217d3793a18521540e1a8e6ba349949ad765..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/preview-effect-5.jpg and /dev/null differ diff --git a/docs-en/guide/figures/preview-effect.jpg b/docs-en/guide/figures/preview-effect.jpg old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/project-directory-1.png b/docs-en/guide/figures/project-directory-1.png deleted file mode 100755 index 31fc4ae71504eab0a405ac39041db41feabcf95f..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/project-directory-1.png and /dev/null differ diff --git a/docs-en/guide/figures/project-directory.png b/docs-en/guide/figures/project-directory.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/putty-configuration-page.png b/docs-en/guide/figures/putty-configuration-page.png deleted file mode 100755 index 2200a705d03c1c910fe14a2f0b39407b95c830e1..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/putty-configuration-page.png and /dev/null differ diff --git a/docs-en/guide/figures/putty-security-alert-dialog.png b/docs-en/guide/figures/putty-security-alert-dialog.png deleted file mode 100755 index 283a1d1afc63077a302df0288ae686ae2a54be87..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/putty-security-alert-dialog.png and /dev/null differ diff --git a/docs-en/guide/figures/qi1.png b/docs-en/guide/figures/qi1.png deleted file mode 100755 index 105e9a83292ff333cafb813ecd6647dd256a9443..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/qi1.png and /dev/null differ diff --git a/docs-en/guide/figures/qidong.png b/docs-en/guide/figures/qidong.png deleted file mode 100755 index d8eb0956bcb630fe89454c10e77289c6449a818d..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/qidong.png and /dev/null differ diff --git a/docs-en/guide/figures/rear-view-of-the-hi3516-board.png b/docs-en/guide/figures/rear-view-of-the-hi3516-board.png deleted file mode 100755 index 2e2dc3b4ae6c08c044068545b23062955d8272f8..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/rear-view-of-the-hi3516-board.png and /dev/null differ diff --git a/docs-en/guide/figures/reset2.png b/docs-en/guide/figures/reset2.png deleted file mode 100755 index 6d8e191822c58c6eee84c7e069938f89df83456e..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/reset2.png and /dev/null differ diff --git a/docs-en/guide/figures/restarting-a-burning-task.png b/docs-en/guide/figures/restarting-a-burning-task.png deleted file mode 100755 index f2f78d789e8e4d19b7db435d8a013187847deea0..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/restarting-a-burning-task.png and /dev/null differ diff --git a/docs-en/guide/figures/saved-files-2.png b/docs-en/guide/figures/saved-files-2.png deleted file mode 100755 index 9d0d42f82c0e2a877c01397f038561bea07430e7..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/saved-files-2.png and /dev/null differ diff --git a/docs-en/guide/figures/saved-files.png b/docs-en/guide/figures/saved-files.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/searching-for-the-visual-studio-code-application.png b/docs-en/guide/figures/searching-for-the-visual-studio-code-application.png deleted file mode 100755 index c735ae362e184083329cdf710289a169ad5625d4..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/searching-for-the-visual-studio-code-application.png and /dev/null differ diff --git a/docs-en/guide/figures/selecting-project-template.png b/docs-en/guide/figures/selecting-project-template.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/selecting-the-serial-port-to-be-connected-to-the-board.png b/docs-en/guide/figures/selecting-the-serial-port-to-be-connected-to-the-board.png deleted file mode 100755 index bf0c01278e517224774c228f27e5a8e2a2e58dbc..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/selecting-the-serial-port-to-be-connected-to-the-board.png and /dev/null differ diff --git a/docs-en/guide/figures/serial-port-logs-displayed-after-the-exit-command-is-executed-6.png b/docs-en/guide/figures/serial-port-logs-displayed-after-the-exit-command-is-executed-6.png deleted file mode 100755 index 8a4415351ff8f04e33bcd2d5dbb2b78a213bcbf2..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/serial-port-logs-displayed-after-the-exit-command-is-executed-6.png and /dev/null differ diff --git a/docs-en/guide/figures/serial-port-logs-displayed-after-the-exit-command-is-executed.png b/docs-en/guide/figures/serial-port-logs-displayed-after-the-exit-command-is-executed.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/serial-port-logs-displayed-after-the-photographing-command-is-executed-1.png b/docs-en/guide/figures/serial-port-logs-displayed-after-the-photographing-command-is-executed-1.png deleted file mode 100755 index 1dd037de6e414565b5c1707fd0aa8bf8ff611f38..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/serial-port-logs-displayed-after-the-photographing-command-is-executed-1.png and /dev/null differ diff --git a/docs-en/guide/figures/serial-port-logs-displayed-after-the-photographing-command-is-executed.png b/docs-en/guide/figures/serial-port-logs-displayed-after-the-photographing-command-is-executed.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/serial-port-logs-displayed-after-the-preview-command-is-executed-4.png b/docs-en/guide/figures/serial-port-logs-displayed-after-the-preview-command-is-executed-4.png deleted file mode 100755 index c4ec2b5dfbeb02b3d6c5469dbe848debfe30d188..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/serial-port-logs-displayed-after-the-preview-command-is-executed-4.png and /dev/null differ diff --git a/docs-en/guide/figures/serial-port-logs-displayed-after-the-preview-command-is-executed.png b/docs-en/guide/figures/serial-port-logs-displayed-after-the-preview-command-is-executed.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/serial-port-logs-displayed-after-the-recording-command-is-executed-3.png b/docs-en/guide/figures/serial-port-logs-displayed-after-the-recording-command-is-executed-3.png deleted file mode 100755 index 5d9aea614e521f2a2c5e634917f16cebf24ae80d..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/serial-port-logs-displayed-after-the-recording-command-is-executed-3.png and /dev/null differ diff --git a/docs-en/guide/figures/serial-port-logs-displayed-after-the-recording-command-is-executed.png b/docs-en/guide/figures/serial-port-logs-displayed-after-the-recording-command-is-executed.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/starting-camera_sample-0.png b/docs-en/guide/figures/starting-camera_sample-0.png deleted file mode 100755 index 881c86b3f1862cd09f013a154d3882060ba8bcba..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/starting-camera_sample-0.png and /dev/null differ diff --git a/docs-en/guide/figures/starting-camera_sample.png b/docs-en/guide/figures/starting-camera_sample.png old mode 100755 new mode 100644 diff --git a/docs-en/guide/figures/successful-burning.png b/docs-en/guide/figures/successful-burning.png deleted file mode 100755 index 3905fb62f3d2dbc9e448066ef508088d601b4212..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/successful-burning.png and /dev/null differ diff --git a/docs-en/guide/figures/successful-driver-installation.png b/docs-en/guide/figures/successful-driver-installation.png deleted file mode 100755 index df6f7562d148fa750afb7d6a7a39d2ef099c9b8c..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/successful-driver-installation.png and /dev/null differ diff --git a/docs-en/guide/figures/successful-login-page.png b/docs-en/guide/figures/successful-login-page.png deleted file mode 100755 index 0690d0e4cc75cd69b45206ba1e11e9827c5d976f..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/successful-login-page.png and /dev/null differ diff --git a/docs-en/guide/figures/video_2020-07-25_173141-0.gif b/docs-en/guide/figures/video_2020-07-25_173141-0.gif deleted file mode 100755 index 6550d44bc4bb28498f8bb149bac1ab8f2cd9b3ed..0000000000000000000000000000000000000000 Binary files a/docs-en/guide/figures/video_2020-07-25_173141-0.gif and /dev/null differ diff --git a/docs-en/guide/figures/video_2020-07-25_173141.gif b/docs-en/guide/figures/video_2020-07-25_173141.gif old mode 100755 new mode 100644 diff --git a/docs-en/guide/hi3516-board-environment-setup.md b/docs-en/guide/hi3516-board-environment-setup.md deleted file mode 100755 index 6b55e0ac19858cf4dd95bd0ce9e54459d668a307..0000000000000000000000000000000000000000 --- a/docs-en/guide/hi3516-board-environment-setup.md +++ /dev/null @@ -1,284 +0,0 @@ -# Hi3516 Board Environment Setup - -## Environment Requirements - -## Hardware - -- Linux server -- Windows workstation \(Host computer\) -- Hi3516DV300 IoT Camera development board -- USB cable and network cable \(The Windows workbench is connected to Hi3516DV300 development board through the USB cable and network cable.\) - -[Figure 1](#fa54d47112b684c65b3dbf1779413545a) shows the hardware connections. - -**Figure 1** Hardware connections -![](figures/hardware-connections.png "hardware-connections") - -## Software - -**Table 1** Platforms required for Hi3516DV300 development board - - - - - - - - - - - - - - - - - - - - - -

Hardware

-

Software

-

Description

-

Remarks

-

Linux compile server

-

Operating system

-

Ubuntu 16.04 64-bit or later is used for the Linux OS, and Bash is used for the shell.

-
NOTE:

Common software such as samba and vim is installed by default in the system. Adaptation is required to support Linux file sharing.

-
-

You can develop programs on the Windows workstation or on the Linux server through remote login.

-

Windows workstation

-

Operating system

-

Windows XP/Windows7/Windows10

-

USB-serial adapter driver

-

http://www.totalcardiagnostics.com/files/PL2303_64bit_Installer.exe

-
- -## Build Tools for Linux - -The following table lists the obtaining channels and common tools required for Linux. - -**Table 2** Development tools and obtaining channels for the Linux server - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Development tool

-

Description

-

Obtaining Channel

-

Python3.7+

-

Runs the compilation scripts.

-

Public internet

-

bash

-

Executes commands.

-

Public internet

-

gn

-

Generates ninja compilation scripts.

-

http://tools.harmonyos.com/mirrors/gn/1523/linux/gn-linux-1523.tar

-

ninja

-

Runs ninja compilation scripts.

-

http://tools.harmonyos.com/mirrors/ninja/1.9.0/linux/ninja-linux-1.9.0.tar

-

LLVM

-

Performs code editing.

-

http://tools.harmonyos.com/mirrors/clang/9.0.0-33042/linux/llvm-linux-9.0.0-33042.tar

-

IPOP, PuTTY, or HyperTerminal

-

Connects to the Linux server(choose one).

-

Public internet (for example, https://www.putty.org/)

-
- -## Development Tools for Windows - -**Table 3** Development tools and obtaining channels for the Windows workstation - - - - - - - - - - - - - - - - -

Development tool

-

Description

-

Obtaining Channel

-

Visual Studio Code

-

Performs code editing.

-

https://code.visualstudio.com/

-

HUAWEI DevEco Device Tool

-

Suppports code compilation, burning, debugging, and C++ languages.

-
NOTE:

HUAWEI DevEco Device Tool is a one-stop integrated development tool provided by OpenHarmony for smart device developers. It supports customization of OpenHarmony components based on demands, and functions such as code editing, compilation, burning, and debugging. It also supports C and C++ languages, and is deployed on Visual Studio Code as a plug-in.

-
-

https://partnerdevicetest.devccsrnd.hwcloudtest.cn:3092/cn/ide

-
- -## Installing the Basic Compilation Environment - -## connecting to the Linux server - -Using PuTTY on Windows to log into Linux server - -1. Open [puTTY](https://www.putty.org/), enter the IP address of the Linux server, and click **Open**. - - **Figure 2** PuTTY configuration page - ![](figures/putty-configuration-page.png "putty-configuration-page") - -2. Select **Yes** in the dialog displaying a security alert. - - **Figure 3** PuTTY Security Alert dialog - ![](figures/putty-security-alert-dialog.png "putty-security-alert-dialog") - -3. Enter the account and password. - - **Figure 4** Login page - ![](figures/login-page.png "login-page") - -4. The login is successful. - - **Figure 5** Successful login page - ![](figures/successful-login-page.png "successful-login-page") - - -## h2 Change Linux shell to bash. - -Run the following command on the terminal to check whether the shell is bash: - -``` -ls -l /bin/sh -``` - -如果为显示为/bin/sh -\> bash则为正常,否则请按以下方式修改: - -**方法一** - -在终端运行如下命令,然后选择 no。 - -``` -sudo dpkg-reconfigure dash -``` - -**方法二** - -Delete sh and run sh again. - -``` -rm -rf /bin/sh -sudo ln -s /bin/bash /bin/sh -``` - -## Installing the Python environment - -1. Open the terminal of the Linux compilation server. -2. Run the **python3 --version** command to check the Python version. Python 3.7 or later is required. Otherwise, perform the following steps \(Python 3.8 is used as an example\): - - If the Ubuntu version is 18, run the sudo apt-get install python3.8 command. - - If the Ubuntu version is 16, download the installation package and install Python. - - 1. Run the following command to install the python dependency: - - Run sudo apt-get install gcc to install gcc. - - Install g++ and run sudo apt-get install g++. - - Run the sudo apt-get install make command to install make. - - Run the sudo apt-get install zlib\* command to install zlib. - - Install libffi and run "sudo apt-get install libffi-dev". - - 2. Obtaining the python3.8.5 Installation Package - - 3. Run the **tar -xvzf Python-3.8.5.tgz** command. - - 4. Run the **cd Python-3.8.5** command. - - 5. Run the **sudo ./configure** command. - - 6. Run the **make** command. - - 7. Run the **sudo make install** command. - - - - Run the sudo apt-get install python3-setuptools python3-pip -y command to install the Python package management tool. \(The root/sudo permission is required for the installation.\) Upgrade pip3 and run "sudo pip3 install --upgrade pip". - - Alternatively, install the Python package management tool as instructed by the official website. Download "curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py" and run python get-pip.py. - - -3. Install **setuptools** by running the **pip3 install setuptools** command. -4. Install GUI menuconfig by running the **sudo pip3 install kconfiglib** command. The Kconfiglib 13.2.0+ version is recommended. \(The root/sudo permission is required.\) - - You can also perform the following steps to install Kconfiglib 13.2.0+: - - 1. Download the .whl file \(for example, kconfiglib-13.2.0-py2.py3-none-any.whl\). - - Download path: https://pypi.org/project/kconfiglib\#files - - - 1. Install the .whl file by running the **sudo pip3 install kconfiglib-xxx.whl** command \(for example, **sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl**\). \(The root/sudo permission is required.\) - 2. If an error of **subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1.** occurs, run the **find / -name lsb\_release** command, for example, **rm -rf /usr/bin/lsb\_release** to locate **sb\_release** and release it. - - -## Installing **gn** - -1. Open a Linux terminal. -2. Download [gn](https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/latest). -3. Decompress the tool package to the **\~/gn** directory on the Linux server. -4. Set environment variables by running the **vim /etc/profile** command and add new variables by running the **export PATH=\~/gn:$PATH** command. -5. Validate environment variables by running the **source /etc/profile** command. - -## Installing **ninjah** - -1. Open a Linux terminal. -2. Download [ninja](https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-linux.zip). -3. Decompress the tool package to the **\~/ninja** directory on the Linux server. -4. Set environment variables by running the **vim /etc/profile** command and add new variables by running the **PATH=\~/ninja:$PATH** command. -5. Validate environment variables by running the **source /etc/profile** command. - -## Installing the LLVM tool chain - -1. Open a Linux terminal. -2. Download [http://tools.harmonyos.com/mirrors/clang/9.0.0-33042/linux/llvm-linux-9.0.0-33042.tar](http://tools.harmonyos.com/mirrors/clang/9.0.0-33042/linux/llvm-linux-9.0.0-33042.tar). -3. Decompress the tool package to the **\~/llvm** directory on the Linux server. -4. Set environment variables by adding **export PATH=\~/bin:$PATH** to the **vim /etc/profile** profile. -5. Validate environment variables by running the **source /etc/profile** command. - -## Installing File Packing Tools - -1. Open a Linux terminal. -2. Run the **mkfs.vfat** command. If the command is not found, run the **sudo apt-get install dosfstools** command. -3. Run the **mcopy** command. If the command is not found, run the **sudo apt-get install mtools** command. - diff --git a/docs-en/guide/introduction-to-the-hi3516-development-board.md b/docs-en/guide/introduction-to-the-hi3516-development-board.md deleted file mode 100755 index b873f9957e3823be0f7458731383a9e4dbe6c059..0000000000000000000000000000000000000000 --- a/docs-en/guide/introduction-to-the-hi3516-development-board.md +++ /dev/null @@ -1,77 +0,0 @@ -# Introduction to the Hi3516 Development Board - -Hi3516DV300 is a new-generation SoC designed for the industry-dedicated smart HD IP camera. It introduces a new-generation ISP, the latest H.265 video compression encoder, and a high-performance NNIE engine, enabling Hi3516C V500 to lead the industry in terms of low bit rate, high image quality, intelligent processing and analysis, and low power consumption. Integrated with the POR, RTC, audio codec, and standby wakeup circuit, Hi3516DV300 can greatly reduce the EBOM costs for customers. - -**Figure 1** Front view of the Hi3516 board -![](figures/front-view-of-the-hi3516-board.png "front-view-of-the-hi3516-board") - -**Figure 2** Rear view of the Hi3516 board -![](figures/rear-view-of-the-hi3516-board.png "rear-view-of-the-hi3516-board") - -## Software Environment of the Development Board - -**Table 1** Description of the software environment - - - - - - - - - - - - - - - - - - - - - -

Hardware

-

Software

-

Description

-

Hi3516DV300

-

IoT camera board

-

bootstrap program

-

U-Boot

-

Operating system

-

Huawei-developed operating system HOSP (including the OHOS kernel, application framework, and service framework)

-

Application software

-

Shell (Starts as the system boots)

-

Program development library

-

musl 1.x

-
- -## Development Board Specifications - -**Table 2** Specification list of the Hi3516 development board - - - - - - - - - - - - - -

Type

-

Specification

-

Processor and internal memory

-
  • Hi3516D V300
  • 2 pieces of 16-bit-width DDR3, with 4 Gb capacity each particle
  • eMMC with 8 GB capacity
-

External Components

-
  • Ethernet port
  • Audio and video
    • 1-channel voice input
    • 1-channel (AC_L) mono output, connected to a 3 W power amplifier (LM4871)
    • MicroHDMI (1-channel HDMI 1.4)
    -
  • Camera
    • Sensor IMX335
    • M12 lens with a focal length of 4 mm and an aperture of 1.4
    -
  • Display
    • 2.35-inch LCD connector
    • 5.5-inch LCD connector
    -
  • External components and interfaces
    • microSD card port circuit
    • JTAG/I2S interface
    • ADC interface
    • Steer gear interface
    • Grove connector
    • USB2.0(Type C)
    • Three function keys: two customized keys and one update key
    • LED indicator (including green and red)
    -
-
- diff --git a/docs-en/guide/led-peripheral-control.md b/docs-en/guide/led-peripheral-control.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/overview-0.md b/docs-en/guide/overview-0.md old mode 100755 new mode 100644 index ed5c37c3bf37cd13525a2d933730499335030812..2c66a2a1a8d3e81e4a0ff3ba91d268867b45fa36 --- a/docs-en/guide/overview-0.md +++ b/docs-en/guide/overview-0.md @@ -4,3 +4,7 @@ This document describes how to use the IoT camera development board and the buil You can perform operations provided in [Use Case](use-case.md) to learn more about development board peripheral control and then develop devices, such as security cameras. +If you want to view the sample effect first, see [Use Case](use-case.md). To customize application behavior, modify the sample code by referring to APIs described in [Development Guidelines](development-guidelines.md). + +For details about basic concepts for camera development, see the [camera development overview](../subsystems/overview.md). + diff --git a/docs-en/guide/overview-1.md b/docs-en/guide/overview-1.md old mode 100755 new mode 100644 index 7711cc431f85d9c33aebd7d58c5c19b5648d0880..6abb1c2e96f5ba584bacb9204dd01a9b76fe6547 --- a/docs-en/guide/overview-1.md +++ b/docs-en/guide/overview-1.md @@ -2,5 +2,9 @@ This document describes how to use the IoT camera development board and the built-in camera and screen of the development kit to implement photographing, video recording, and video preview. -You can perform operations provided in [Use Case](use-case.md) to learn more about development board peripheral control and then develop devices, such as peephole cameras, smart rear-view mirrors, and smart displays. +You can perform operations provided in [Use Case](use-case-5.md) to learn more about development board peripheral control and then develop devices, such as peephole cameras, smart rear-view mirrors, and smart displays. + +If you want to view the sample effect first, see [Use Case](use-case-5.md). To customize application behavior, modify the sample code by referring to APIs described in [Development Guidelines](development-guidelines-2.md). + +For details about basic concepts for camera development, see the [camera development overview](../subsystems/overview.md). diff --git a/docs-en/guide/overview-2.md b/docs-en/guide/overview-2.md deleted file mode 100755 index c9cdbc92f754184473a9caceefa89afce6ea28ab..0000000000000000000000000000000000000000 --- a/docs-en/guide/overview-2.md +++ /dev/null @@ -1,13 +0,0 @@ -# Overview - -This section describes how to quickly set up a development environment for basic head units running on OpenHarmony, and how to create, develop, and debug your application. This section uses an air quality monitoring application \(AirQuality\) as an example. - -## Display Effects - -AirQuality displays air quality information on three pages: **home** page fo overview, **detail** page, and **history** page. The following figure shows AirQuality on the DevEco Studio simulator. - -**Figure 1** Display effect of the air quality application - - -![](figures/video_2020-07-25_173141-0.gif) - diff --git a/docs-en/guide/overview-6.md b/docs-en/guide/overview-6.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/overview-7.md b/docs-en/guide/overview-7.md deleted file mode 100755 index 6e3d607832d96704076a9d9157851ca3caeb2c73..0000000000000000000000000000000000000000 --- a/docs-en/guide/overview-7.md +++ /dev/null @@ -1,13 +0,0 @@ -# Overview - -This section describes how to quickly set up a development environment for the basic head unit running on OpenHarmony and how to create, develop, and debug your application. This section uses an air quality application as an example. - -## Display Effects - -The application displays air quality information on three pages: **home** page fo overview, **detail** page, and **history** page. The following figure shows the application on the DevEco Studio simulator. - -**Figure 1** Display effect of the air quality application - - -![](figures/video_2020-07-25_173141.gif) - diff --git a/docs-en/guide/overview.md b/docs-en/guide/overview.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/photographing-3.md b/docs-en/guide/photographing-3.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/photographing.md b/docs-en/guide/photographing.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/preparations-3.md b/docs-en/guide/preparations-3.md deleted file mode 100755 index 13d27f4fa393838ff8519eb4a9afb98fbffa22df..0000000000000000000000000000000000000000 --- a/docs-en/guide/preparations-3.md +++ /dev/null @@ -1,10 +0,0 @@ -# Preparations - -## Building the Development Environment - -Set up the environment by performing operations provided in **Environment Setup** of the [DevEco Studio User Guide](https://harmonyosdevelopertest.devccsrnd.hwcloudtest.cn:3087/cn/docs/documentation/doc-guides/tools_overview-0000001053582387). - -## Creating a Project - -For details, see section **Creating a Project** in the [DevEco Studio User Guide](https://harmonyosdevelopertest.devccsrnd.hwcloudtest.cn:3087/cn/docs/documentation/doc-guides/tools_overview-0000001053582387). - diff --git a/docs-en/guide/preparations.md b/docs-en/guide/preparations.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/previewing-5.md b/docs-en/guide/previewing-5.md deleted file mode 100755 index faa950a19421653af4c50c2472778da080196d6c..0000000000000000000000000000000000000000 --- a/docs-en/guide/previewing-5.md +++ /dev/null @@ -1,38 +0,0 @@ -# Previewing - -## When to Use - -Use the camera module APIs to generate and play video streams. - -## Available APIs - -For details, see [Available APIs](photographing-3.md#en-us_topic_0000001052170554_section56549532016). - -## Limitations and Constraints - -None - -## How to Develop - -1. Perform step 1 through step 4 provided in [Photographing](photographing-3.md#section7152245183619). -2. Set the preview area. - - ``` - Surface *surface = Surface::CreateSurface(); - /* Set the display area. */ - surface->SetUserData("region_position_x", "480"); // X-coordinate of the upper left corner of the rectangle - surface->SetUserData("region_position_y", "270"); // Y-coordinate of the upper left corner of the rectangle - surface->SetUserData("region_width", "960"); // Width - surface->SetUserData("region_height", "540"); // Height - - fc->AddSurface(*surface); - ``` - -3. Start and stop previewing. - - ``` - stateCallback->camera_->TriggerLoopingCapture(*fc); // Start previewing. - stateCallback->camera_->StopLoopingCapture(); // Stop previewing. - ``` - - diff --git a/docs-en/guide/previewing.md b/docs-en/guide/previewing.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/public_sys-resources/icon-caution.gif b/docs-en/guide/public_sys-resources/icon-caution.gif old mode 100755 new mode 100644 diff --git a/docs-en/guide/public_sys-resources/icon-danger.gif b/docs-en/guide/public_sys-resources/icon-danger.gif old mode 100755 new mode 100644 diff --git a/docs-en/guide/public_sys-resources/icon-note.gif b/docs-en/guide/public_sys-resources/icon-note.gif old mode 100755 new mode 100644 diff --git a/docs-en/guide/public_sys-resources/icon-notice.gif b/docs-en/guide/public_sys-resources/icon-notice.gif old mode 100755 new mode 100644 diff --git a/docs-en/guide/public_sys-resources/icon-tip.gif b/docs-en/guide/public_sys-resources/icon-tip.gif old mode 100755 new mode 100644 diff --git a/docs-en/guide/public_sys-resources/icon-warning.gif b/docs-en/guide/public_sys-resources/icon-warning.gif old mode 100755 new mode 100644 diff --git a/docs-en/guide/running-and-debugging-8.md b/docs-en/guide/running-and-debugging-8.md deleted file mode 100755 index e279fbae50e7ebec11cd64e035e12df681d36551..0000000000000000000000000000000000000000 --- a/docs-en/guide/running-and-debugging-8.md +++ /dev/null @@ -1,4 +0,0 @@ -# Running and Debugging - -After the code is compiled, you can run the application for debugging. For details about how to debug an application, see the [App Debugging](https://harmonyosdevelopertest.devccsrnd.hwcloudtest.cn:3087/cn/docs/documentation/doc-guides/debug_overview-0000001053822404) section in the [DevEco Studio User Guide](https://harmonyosdevelopertest.devccsrnd.hwcloudtest.cn:3087/cn/docs/documentation/doc-guides/tools_overview-0000001053582387). - diff --git a/docs-en/guide/running-and-debugging.md b/docs-en/guide/running-and-debugging.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/running-on-the-device.md b/docs-en/guide/running-on-the-device.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/screen-and-camera-control.md b/docs-en/guide/screen-and-camera-control.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/third-party-sdk-integration.md b/docs-en/guide/third-party-sdk-integration.md old mode 100755 new mode 100644 index 4289409dcb92a7f536f52ba15fcb0c56e599ad6e..021390fd03f7f95f353638238011c9b9ba14359d --- a/docs-en/guide/third-party-sdk-integration.md +++ b/docs-en/guide/third-party-sdk-integration.md @@ -284,7 +284,7 @@ The following uses **demolink** as an example to describe how to compile code include_dirs = [ "//utils/native/lite/include", - "//vendor/hisi/wifi-iot/hi3861/3rd_sdk/demolink" + "//domains/iot/link/libbuild" ] } ``` diff --git a/docs-en/guide/use-case-5.md b/docs-en/guide/use-case-5.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/use-case-6.md b/docs-en/guide/use-case-6.md deleted file mode 100755 index 1e0d9d7408002a05951d4c69c92873746066f5ee..0000000000000000000000000000000000000000 --- a/docs-en/guide/use-case-6.md +++ /dev/null @@ -1,47 +0,0 @@ -# Use Case - -- For details about the development board, compilation, burning, and image running process, see [Getting Started with Hi3516](../quick-start/introduction-to-the-hi3516-development-board.md) and [Getting Started with Hi3518](../quick-start/introduction-to-the-hi3518-development-board.md). The compilation results include that of the **camera\_sample** program. -- The sample code for camera development is stored in **applications/sample/camera/media/camera\_sample.cpp**. - - >![](public_sys-resources/icon-notice.gif) **NOTICE:** - >You should insert a TF card \(maximum capacity: 128 GB\) for photographing and video recording functions. After the system is started, the TF card is automatically mounted to the **/sdcard** directory. If the TF card is inserted after the system is started, you have to manually mount the TF card. To view the photos and videos in the TF card, copy the content to a computer. The preview function does not require a TF card. - - -1. Start the **camera\_sample** program. - - **Figure 1** Starting camera\_sample - ![](figures/starting-camera_sample-0.png "starting-camera_sample-0") - - The control commands are displayed as shown in the preceding figure. Press **S** to stop the current operation \(including video recording and preview\), and press **Q** to exit the program. - -2. Press **1** to take a photo in JPG format. The photo is saved in the **/sdcard** directory and named **Capture\***. - - **Figure 2** Serial port logs displayed after the photographing command is executed - ![](figures/serial-port-logs-displayed-after-the-photographing-command-is-executed-1.png "serial-port-logs-displayed-after-the-photographing-command-is-executed-1") - - To view the saved file, exit the program and enter the file system. To start the program again, return to [Step 1](#li15116103915513). - - **Figure 3** Saved files - ![](figures/saved-files-2.png "saved-files-2") - -3. Press **2** to start recording. The video file is in MP4 format and saved in the **/sdcard** directory with the name **Record\***. Press **S** to stop recording. - - **Figure 4** Serial port logs displayed after the recording command is executed - ![](figures/serial-port-logs-displayed-after-the-recording-command-is-executed-3.png "serial-port-logs-displayed-after-the-recording-command-is-executed-3") - -4. Press **3** to start preview. The preview is displayed on the screen. Press **S** to stop preview. A development board \(such as Hi3518\) without the display module does not support preview. - - **Figure 5** Serial port logs displayed after the preview command is executed - ![](figures/serial-port-logs-displayed-after-the-preview-command-is-executed-4.png "serial-port-logs-displayed-after-the-preview-command-is-executed-4") - - The following figure shows the preview. - - **Figure 6** Preview effect - ![](figures/preview-effect-5.jpg "preview-effect-5") - -5. Press **Q** to exit. - - **Figure 7** Serial port logs displayed after the exit command is executed - ![](figures/serial-port-logs-displayed-after-the-exit-command-is-executed-6.png "serial-port-logs-displayed-after-the-exit-command-is-executed-6") - - diff --git a/docs-en/guide/use-case.md b/docs-en/guide/use-case.md old mode 100755 new mode 100644 index cb6f45730e725f19a84be5e3c22d3672517dde90..95e8f8d5b2b923dc98b7f8f9bfdaf1ba52d29fb3 --- a/docs-en/guide/use-case.md +++ b/docs-en/guide/use-case.md @@ -1,6 +1,6 @@ # Use Case -- For details about the development board, compilation, burning, and image running process, see [Getting Started with Hi3518](en-us_topic_0000001054261054.md). The compilation results include that of the **camera\_sample** program. +- For details about the development board, compilation, burning, and image running process, see [Getting Started with Hi3518](../quick-start/introduction-to-the-hi3518-development-board.md). The compilation results include that of the **camera\_sample** program. - The sample code for camera development is stored in **applications/sample/camera/media/camera\_sample.cpp**. >![](public_sys-resources/icon-notice.gif) **NOTICE:** diff --git a/docs-en/guide/verification.md b/docs-en/guide/verification.md old mode 100755 new mode 100644 index f56ba01c96e8816d914b287d3da25cd381dac422..56aebdb4e073d147cfb30161433359afc7eec23d --- a/docs-en/guide/verification.md +++ b/docs-en/guide/verification.md @@ -1,6 +1,6 @@ # Verification -For details about the compilation and burning processes, see [Compiling Source Code](../quick-start/developing-the-first-example-program-running-on-hi3861.md#section1736014117148) and [Burning Images](../quick-start/developing-the-first-example-program-running-on-hi3861.md#section1610612214150) in the _Getting Started with Hi3861_. +For details about the compilation and burning processes, see [Compiling Source Code](../quick-start/developing-the-first-example-program-running-on-hi3861.md) and [Burning Images](../quick-start/developing-the-first-example-program-running-on-hi3861.md) in _Getting Started with Hi3861_. After the preceding two steps are complete, press the **RST** button to reset the module. The LED blinks periodically, which meets the expectation. The verification is complete. diff --git a/docs-en/guide/video-recording-4.md b/docs-en/guide/video-recording-4.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/video-recording.md b/docs-en/guide/video-recording.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/visual-application-development.md b/docs-en/guide/visual-application-development.md old mode 100755 new mode 100644 diff --git a/docs-en/guide/wlan-connected-products.md b/docs-en/guide/wlan-connected-products.md old mode 100755 new mode 100644 diff --git a/docs-en/quick-start/developing-the-first-application-program-running-on-the-hi3516-development-board.md b/docs-en/quick-start/developing-the-first-application-program-running-on-the-hi3516-development-board.md deleted file mode 100755 index d007424c19126493524be71b1fbcc28d770f5a91..0000000000000000000000000000000000000000 --- a/docs-en/quick-start/developing-the-first-application-program-running-on-the-hi3516-development-board.md +++ /dev/null @@ -1,177 +0,0 @@ -# Developing the First Application Program running on the Hi3516 Development Board - -This section describes how to modify, compile, burn, and run the first application on the board. - -## Obtaining the Source Code - -You need to download a set of source code from the Linux server. For details, see [Source Code Acquisition](en-us_topic_0000001050769927.md). - -## Modifying an application - -The **helloworld.c** code in the **applications/sample/camera/app/src** directory is as following. You can customize the content to be printed. For example, you can change OHOS to World. The current applications can be developed using standard ISO C and C++. - -``` -#include -#include "los_sample.h" - -int main(int argc, char **argv) -{ - printf("\n************************************************\n"); - printf("\n\t\tHello OHOS!\n"); - printf("\n************************************************\n\n"); - - LOS_Sample(g_num); - - return 0; -} -``` - -## Compiling - -On the Linux server, go to the root directory of the source code package. The build.py compilation script is stored in the directory. Run the following script to compile the source code package: - -Run the following script in the root directory of the source code package to compile the source code package. The result file is generated in the out/ipcamera\_hi3516dv300 directory. - -``` -python build.py ipcamera_hi3516dv300 -b debug -``` - -## Burning - -Network Burning Mode - -This method applies only to the board \(such as Hi3516D V300\) that supports the network port. In addition, the PC and the board must be connected by using a network cable and configured on the same network. - ->![](public_sys-resources/icon-notice.gif) **NOTICE:** ->The Visual Studio Code software connects the board to the network. If the board cannot connect to the computer network, check the firewall settings. For details, see FAQ 2. - -1. Install the USB-to-serial adapter driver and obtain the serial port number. - - **Figure 1** Successful driver installation - ![](figures/successful-driver-installation.png "successful-driver-installation") - - 1. Power on the board and connect the serial port cable of the board to the Windows console. - 2. Install the driver and obtain the driver link. - 3. Open Device Manager, then check and record the value of **Prolific USB-to-Serial Comm Port**. - - After the driver is successfully installed, if a warning icon is displayed on the device icon, right-click the device, uninstall the driver, reinstall the driver, and restart the computer as prompted. - - -2. On the Windows console, add the IP address 192.168.1.3 for the interconnection network port of the board. The method is as follows: - - **Figure 2** Adding a Windows host IP address - ![](figures/adding-a-windows-host-ip-address.png "adding-a-windows-host-ip-address") - - 1. Choose Control Panel \> Network and Internet-\> Network Connections, right-click the network adapter connected to the board, and choose Properties from the shortcut menu. - 2. Double-click Internet 协议版本4(TCP/IPv4). - 3. Configure the IP address and gateway according to the preceding figure. - 4. Click OK to save the configuration. - -3. hi3516dv300 is added to the board list. After you choose Board Configure and enable a board, the board configuration table is automatically added. - - **Figure 3** Add a board. - ![](figures/add-a-board.png "add-a-board") - -4. Start the IDE and click the icons in sequence to configure the contents to be burnt over the network. - - **Figure 4** Network configuration diagram of the IDE tool - - - ![](figures/ide.png) - - 1. Select Hi3516 as the board type. - 2. Click Burn. - 3. Set Burning Mode to "network". - 4. Host IP Address: Click to refresh the page and select 192.168.1.3 from the drop-down list box. - -5. Select the chip to be burnt to the flash memory and the burning address. - - **Figure 5** Setting the parameters for burning files - - - ![](figures/ide2.png) - - 1. Select emmc from the Memory Type drop-down list box as the flash memory type. - 2. Click New to add three files. Enter the OHOS\_Image.bin, rootfs.img, and userfs.img files in the file path in sequence. Set the start address and file length as shown in the following figure, obtain the file from the out/ipcamera\_hi3516dv300 directory. - 3. Click Save to save the changes. - 4. Click Burn on the left to start burning. - -6. Select a serial port number from the drop-down list box displayed in the upper part, for example, COM11. - - **Figure 6** Selecting the serial port to be connected to the board - ![](figures/selecting-the-serial-port-to-be-connected-to-the-board.png "selecting-the-serial-port-to-be-connected-to-the-board") - -7. The burning starts. If a message is displayed, you need to manually restart the board \(by powering off and then powering on the board\). - - **Figure 7** The system prompts you to power off the board and power on the board again. - - - ![](figures/reset2.png) - -8. Burning is complete. - - **Figure 8** Successful burning - ![](figures/successful-burning.png "successful-burning") - - -## Running an Image - -1. Connect a serial port cable. - - **Figure 9** Connect the serial port cables. - - - ![](figures/chuankou1.png) - - 1. Click Serial port to enable the serial port. - 2. Enter the serial port number of the "com11" and press Enter until hisillicon is displayed. - 3. If the board is started for the first time or the startup parameters are modified, go to step 2. Otherwise, go to step 3. - -2. \(Mandatory when the board is started for the first time\) Modify the bootcmd and bootargs of the U-boot. This step is a fixed operation. If the parameters are not modified, you need to perform this step only once. The system automatically enters the system each time the board is reset. - - >![](public_sys-resources/icon-notice.gif) **NOTICE:** - >By default, the U-boot boot program waits for two seconds. You can press Enter to interrupt the waiting and the message "hisillicon" is displayed. You can run the reset command to restart the system. - - **Table 1** U-boot startup parameters - - - - - - - - - -

Command

-

setenv bootcmd "sf probe 0;mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

saveenv

-

reset

-

Description

-

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800;go 0x80000000";

-

This command indicates that flash 0 is selected, and the contents with the start address of 0x800 (unit: 512 bytes, that is, 1 MB) and the size of 0x4800 (unit: 512 bytes, that is, 9 MB) are read to the memory address 0x80000000.

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

This command sets the boot parameters to serial port output, baud rate to 115200, data bit to 8, rootfs to be mounted to the eMMC, and file system type to vfat,

-

Enter the start burning position and length of rootfs.img in rootaddr=10M rootsize=15M rw. The size must be the same as that of the rootfs.img file added in the IDE.

-

saveenv indicates that the current configuration is saved.

-

reset indicates that the board is reset.

-

(Optional) go 0x40000000 indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, you can press Enter in the countdown phase of the U-Boot startup to interrupt the automatic startup.

-
- -3. Enter reset and press Enter to restart the board. After the board is restarted successfully, press Enter. OHOS is displayed. - - **Figure 10** Start the system - - - ![](figures/qi1.png) - - -## Run the program. - -In the root directory, run the **./bin/hello\_uart** command line to operate the demo program. The following figure shows the compilation result. - -**Figure 11** Why are apps on my phone launching slowly? - - -![](figures/qidong.png) - diff --git a/docs-en/quick-start/developing-the-first-driver-program-running-on-the-hi3516-development-board.md b/docs-en/quick-start/developing-the-first-driver-program-running-on-the-hi3516-development-board.md deleted file mode 100755 index f1b4fd659d6b227d099965e847ff288fab470a5c..0000000000000000000000000000000000000000 --- a/docs-en/quick-start/developing-the-first-driver-program-running-on-the-hi3516-development-board.md +++ /dev/null @@ -1,560 +0,0 @@ -# Developing the First Driver Program running on the Hi3516 Development Board - -This section describes how to develop a driver program on the board, including introduction, compilation, burning, and operation of the driver. - -## Obtaining the Source Code - -You need to download a set of source code from the Linux server. For details, see [Source Code Acquisition](en-us_topic_0000001050769927.md). - -## Introduction to Driver - -The following example shows how to add configuration files, implement the driver code, and compile the code for interaction between the user-space applications and the driver based on the UART driver. The driver source code is stored in the **vendor/huawei/hdf/sample** directory. - -1. Add Configurations. - - The following example shows how to add the driver configurations to the HDF configuration file \(for example, **vendor/hisi/hi35xx/hi3516dv300/config/uart/uart\_config.hcs**\). - - ``` - root { - platform { - uart_sample { - num = 5; // UART Device No. - base = 0x120a0000; // UART registers - irqNum = 38; - baudrate = 115200; - uartClk = 24000000; - wlen = 0x60; - parity = 0; - stopBit = 0; - match_attr = "sample_uart_5"; - } - } - } - ``` - - This example shows how to add the driver nodes to the HDF configuration file \(for example, **vendor/hisi/hi35xx/hi3516dv300/config/device\_info/device\_info.hcs**\). - - ``` - root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_uart :: device { - device5 :: deviceNode { - policy = 1; - priority = 40; - permission = 0644; - moduleName = "UART_SAMPLE"; - serviceName = "HDF_PLATFORM_UART_5"; - deviceMatchAttr = "sample_uart_5"; - } - } - } - } - } - ``` - - >![](public_sys-resources/icon-note.gif) **NOTE:** - >The configuration file and the source code of the UART driver sample are in the same path. You need to manually add them to the path of the Hi3516D V300 board. - -2. Register a UART driver entry. - - The following example shows how to register the **HdfDriverEntry** entry of the UART driver based on the HDF. - - ``` - // Bind the UART driver interface to the HDF. - static int32_t HdfUartSampleBind(struct HdfDeviceObject *device) - { - if (device == NULL) { - return HDF_ERR_INVALID_OBJECT; - } - HDF_LOGI("Enter %s:", __func__); - return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; - } - - // Obtain configuration information from the HCS of the UART driver. - static uint32_t UartDeviceGetResource( - struct UartDevice *device, const struct DeviceResourceNode *resourceNode) - { - struct UartResource *resource = &device->resource; - struct DeviceResourceIface *dri = NULL; - dri = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (dri == NULL || dri->GetUint32 == NULL) { - HDF_LOGE("DeviceResourceIface is invalid"); - return HDF_FAILURE; - } - - if (dri->GetUint32(resourceNode, "num", &resource->num, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read num fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "base", &resource->base, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read base fail"); - return HDF_FAILURE; - } - resource->physBase = (unsigned long) OsalIoRemap(resource->base, 0x48); - if (resource->physBase == 0) { - HDF_LOGE("uart config fail to remap physBase"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "irqNum", &resource->irqNum, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read irqNum fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "baudrate", &resource->baudrate, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read baudrate fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "wlen", &resource->wlen, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read wlen fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "parity", &resource->parity, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read parity fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "stopBit", &resource->stopBit, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read stopBit fail"); - return HDF_FAILURE; - } - if (dri->GetUint32(resourceNode, "uartClk", &resource->uartClk, 0) != HDF_SUCCESS) { - HDF_LOGE("uart config read uartClk fail"); - return HDF_FAILURE; - } - return HDF_SUCCESS; - } - - // Attach the configuration and interfaces of the UART driver to the HDF. - static int32_t SampleAttach(struct UartHost *host, struct HdfDeviceObject *device) - { - int32_t ret; - struct UartDevice *uartDevice = NULL; - if (device->property == NULL) { - HDF_LOGE("%s: property is NULL", __func__); - return HDF_FAILURE; - } - uartDevice = (struct UartDevice *) OsalMemCalloc(sizeof(struct UartDevice)); - if (uartDevice == NULL) { - HDF_LOGE("%s: OsalMemCalloc uartDevice error", __func__); - return HDF_ERR_MALLOC_FAIL; - } - ret = UartDeviceGetResource(uartDevice, device->property); - if (ret != HDF_SUCCESS) { - (void) OsalMemFree(uartDevice); - return HDF_FAILURE; - } - host->num = uartDevice->resource.num; - host->priv = uartDevice; - UartSampleAddDev(host); // Add a user-space UART node. For details, see the source code uart_dev_sample. - return UartDeviceInit(uartDevice); // Initialize UART PL011. For details, see the source code uart_pl011_sample. - } - - // Initialize the UART driver. - static int32_t HdfUartSampleInit(struct HdfDeviceObject *device) - { - int32_t ret; - struct UartHost *host = NULL; - - if (device == NULL) { - HDF_LOGE("%s: device is NULL", __func__); - return HDF_ERR_INVALID_OBJECT; - } - HDF_LOGI("Enter %s:", __func__); - host = UartHostFromDevice(device); - if (host == NULL) { - HDF_LOGE("%s: host is NULL", __func__); - return HDF_FAILURE; - } - ret = SampleAttach(host, device); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: attach error", __func__); - return HDF_FAILURE; - } - host->method = &g_uartSampleHostMethod; - return ret; - } - - static void UartDeviceDeinit(struct UartDevice *device) - { - struct UartRegisterMap *regMap = (struct UartRegisterMap *) device->resource.physBase; - /* wait for uart enter idle. */ - while (UartPl011IsBusy(regMap)); - UartPl011ResetRegisters(regMap); - uart_clk_cfg(0, false); - OsalIoUnmap((void *) device->resource.physBase); - device->state = UART_DEVICE_UNINITIALIZED; - } - - // Unbind and detach the UART driver. - static void SampleDetach(struct UartHost *host) - { - struct UartDevice *uartDevice = NULL; - - if (host->priv == NULL) { - HDF_LOGE("%s: invalid parameter", __func__); - return; - } - uartDevice = host->priv; - UartDeviceDeinit(uartDevice); - (void) OsalMemFree(uartDevice); - host->priv = NULL; - } - - // Release the UART driver. - static void HdfUartSampleRelease(struct HdfDeviceObject *device) - { - struct UartHost *host = NULL; - HDF_LOGI("Enter %s:", __func__); - - if (device == NULL) { - HDF_LOGE("%s: device is null", __func__); - return; - } - host = UartHostFromDevice(device); - if (host == NULL) { - HDF_LOGE("%s: host is null", __func__); - return; - } - if (host->priv != NULL) { - SampleDetach(host); - } - UartHostDestroy(host); - } - - struct HdfDriverEntry g_hdfUartSample = { - .moduleVersion = 1, - .moduleName = "UART_SAMPLE", - .Bind = HdfUartSampleBind, - .Init = HdfUartSampleInit, - .Release = HdfUartSampleRelease, - }; - - HDF_INIT(g_hdfUartSample); - ``` - -3. Register a UART driver interface. - - The following example shows how to implement the UART driver interface using the template **UartHostMethod** provided by the HDF. - - ``` - static int32_t SampleInit(struct UartHost *host) - { - HDF_LOGI("%s: Enter", __func__); - if (host == NULL) { - HDF_LOGE("%s: invalid parameter", __func__); - return HDF_ERR_INVALID_PARAM; - } - return HDF_SUCCESS; - } - - static int32_t SampleDeinit(struct UartHost *host) - { - HDF_LOGI("%s: Enter", __func__); - if (host == NULL) { - HDF_LOGE("%s: invalid parameter", __func__); - return HDF_ERR_INVALID_PARAM; - } - return HDF_SUCCESS; - } - - // Write data into the UART. - static int32_t SampleWrite(struct UartHost *host, uint8_t *data, uint32_t size) - { - HDF_LOGI("%s: Enter", __func__); - uint32_t idx; - struct UartRegisterMap *regMap = NULL; - struct UartDevice *device = NULL; - - if (host == NULL || data == NULL || size == 0) { - HDF_LOGE("%s: invalid parameter", __func__); - return HDF_ERR_INVALID_PARAM; - } - device = (struct UartDevice *) host->priv; - if (device == NULL) { - HDF_LOGE("%s: device is NULL", __func__); - return HDF_ERR_INVALID_PARAM; - } - regMap = (struct UartRegisterMap *) device->resource.physBase; - for (idx = 0; idx < size; idx++) { - while (UartPl011IsBusy(regMap)); - UartPl011Write(regMap, data[idx]); - } - return HDF_SUCCESS; - } - - // Set the baud rate of the UART. - static int32_t SampleSetBaud(struct UartHost *host, uint32_t baudRate) - { - HDF_LOGI("%s: Enter", __func__); - struct UartDevice *device = NULL; - struct UartRegisterMap *regMap = NULL; - UartPl011Error err; - - if (host == NULL) { - HDF_LOGE("%s: invalid parameter", __func__); - return HDF_ERR_INVALID_PARAM; - } - device = (struct UartDevice *) host->priv; - if (device == NULL) { - HDF_LOGE("%s: device is NULL", __func__); - return HDF_ERR_INVALID_PARAM; - } - regMap = (struct UartRegisterMap *) device->resource.physBase; - if (device->state != UART_DEVICE_INITIALIZED) { - return UART_PL011_ERR_NOT_INIT; - } - if (baudRate == 0) { - return UART_PL011_ERR_INVALID_BAUD; - } - err = UartPl011SetBaudrate(regMap, device->uartClk, baudRate); - if (err == UART_PL011_ERR_NONE) { - device->baudrate = baudRate; - } - return err; - } - - // Obtain the baud rate of the UART. - static int32_t SampleGetBaud(struct UartHost *host, uint32_t *baudRate) - { - HDF_LOGI("%s: Enter", __func__); - struct UartDevice *device = NULL; - - if (host == NULL) { - HDF_LOGE("%s: invalid parameter", __func__); - return HDF_ERR_INVALID_PARAM; - } - device = (struct UartDevice *) host->priv; - if (device == NULL) { - HDF_LOGE("%s: device is NULL", __func__); - return HDF_ERR_INVALID_PARAM; - } - *baudRate = device->baudrate; - return HDF_SUCCESS; - } - - // Bind the UART using HdfUartSampleInit. - struct UartHostMethod g_uartSampleHostMethod = { - .Init = SampleInit, - .Deinit = SampleDeinit, - .Read = NULL, - .Write = SampleWrite, - .SetBaud = SampleSetBaud, - .GetBaud = SampleGetBaud, - .SetAttribute = NULL, - .GetAttribute = NULL, - .SetTransMode = NULL, - }; - ``` - - The following sample code shows how to add the module of the UART driver to the compilation script **vendor/huawei/hdf/hdf\_vendor.mk**. - - ``` - LITEOS_BASELIB += -lhdf_uart_sample - LIB_SUBDIRS += $(VENDOR_HDF_DRIVERS_ROOT)/sample/platform/uart - ``` - -4. Compile the code for interaction between the user-space applications and driver. - - Create the **/dev/uartdev-5** node after the UART driver is initialized successfully. The following example shows how to interact with the UART driver through the node. - - ``` - #include - #include - #include - #include "hdf_log.h" - - #define HDF_LOG_TAG "hello_uart" - #define INFO_SIZE 16 - - int main(void) - { - int ret; - int fd; - const char info[INFO_SIZE] = {" HELLO UART! "}; - - fd = open("/dev/uartdev-5", O_RDWR); - if (fd < 0) { - HDF_LOGE("hello_uart uartdev-5 open failed %d", fd); - return -1; - } - ret = write(fd, info, INFO_SIZE); - if (ret != 0) { - HDF_LOGE("hello_uart write uartdev-5 ret is %d", ret); - } - ret = close(fd); - if (ret != 0) { - HDF_LOGE("hello_uart uartdev-5 close failed %d", fd); - return -1; - } - return ret; - } - ``` - - The following example shows how to add the **hello\_uart\_sample** component to the **hdf** subsystem in the **build/lite/product/ipcamera\_hi3516dv300.json** file. - - ``` - { - "subsystem": [ - { - "name": "hdf", - "component": [ - { "name": "hdf_sample", "dir": "//vendor/huawei/hdf/sample/platform/uart:hello_uart_sample", "feature":[] } - ] - } - ] - } - ``` - - >![](public_sys-resources/icon-note.gif) **NOTE:** - >The preceding code is only a sample code. You can view the complete code in **vendor/huawei/hdf/sample**. - >The sample code is not automatically compiled by default. You need to manually add it to the compilation script. - - -## Compiling - -On the Linux server, go to the root directory of the source code package. The build.py compilation script is stored in the directory. Run the following script to compile the source code package: - -Run the following script in the root directory of the source code package to compile the source code package. The result file is generated in the out/ipcamera\_hi3516dv300 directory. - -``` -python build.py ipcamera_hi3516dv300 -b debug -``` - -## Burning - -Network Burning Mode - -This method applies only to the board \(such as Hi3516D V300\) that supports the network port. In addition, the PC and the board must be connected by using a network cable and configured on the same network. - ->![](public_sys-resources/icon-notice.gif) **NOTICE:** ->The Visual Studio Code software connects the board to the network. If the board cannot connect to the computer network, check the firewall settings. For details, see FAQ 2. - -1. Install the USB-to-serial adapter driver and obtain the serial port number. - - **Figure 1** Successful driver installation - ![](figures/successful-driver-installation.png "successful-driver-installation") - - 1. Power on the board and connect the serial port cable of the board to the Windows console. - 2. Install the driver and obtain the driver link. - 3. Open Device Manager, then check and record the value of **Prolific USB-to-Serial Comm Port**. - - After the driver is successfully installed, if a warning icon is displayed on the device icon, right-click the device, uninstall the driver, reinstall the driver, and restart the computer as prompted. - - -2. On the Windows console, add the IP address 192.168.1.3 for the interconnection network port of the board. The method is as follows: - - **Figure 2** Adding a Windows host IP address - ![](figures/adding-a-windows-host-ip-address.png "adding-a-windows-host-ip-address") - - 1. Choose Control Panel \> Network and Internet-\> Network Connections, right-click the network adapter connected to the board, and choose Properties from the shortcut menu. - 2. Double-click Internet 协议版本4(TCP/IPv4). - 3. Configure the IP address and gateway according to the preceding figure. - 4. Click OK to save the configuration. - -3. hi3516dv300 is added to the board list. After you choose Board Configure and enable a board, the board configuration table is automatically added. - - **Figure 3** Add a board. - ![](figures/add-a-board.png "add-a-board") - -4. Start the IDE and click the icons in sequence to configure the contents to be burnt over the network. - - **Figure 4** Network configuration diagram of the IDE tool - - - ![](figures/ide.png) - - 1. Select Hi3516 as the board type. - 2. Click Burn. - 3. Set Burning Mode to "network". - 4. Host IP Address: Click to refresh the page and select 192.168.1.3 from the drop-down list box. - -5. Select the chip to be burnt to the flash memory and the burning address. - - **Figure 5** Setting the parameters for burning files - - - ![](figures/ide2.png) - - 1. Select emmc from the Memory Type drop-down list box as the flash memory type. - 2. Click New to add three files. Enter the OHOS\_Image.bin, rootfs.img, and userfs.img files in the file path in sequence. Set the start address and file length as shown in the following figure, obtain the file from the out/ipcamera\_hi3516dv300 directory. - 3. Click Save to save the changes. - 4. Click Burn on the left to start burning. - -6. Select a serial port number from the drop-down list box displayed in the upper part, for example, COM11. - - **Figure 6** Selecting the serial port to be connected to the board - ![](figures/selecting-the-serial-port-to-be-connected-to-the-board.png "selecting-the-serial-port-to-be-connected-to-the-board") - -7. The burning starts. If a message is displayed, you need to manually restart the board \(by powering off and then powering on the board\). - - **Figure 7** The system prompts you to power off the board and power on the board again. - - - ![](figures/reset2.png) - -8. Burning is complete. - - **Figure 8** Successful burning - ![](figures/successful-burning.png "successful-burning") - - -## Running an Image - -1. Connect a serial port cable. - - **Figure 9** Connect the serial port cables. - - - ![](figures/chuankou1.png) - - 1. Click Serial port to enable the serial port. - 2. Enter the serial port number of the "com11" and press Enter until hisillicon is displayed. - 3. If the board is started for the first time or the startup parameters are modified, go to step 2. Otherwise, go to step 3. - -2. \(Mandatory when the board is started for the first time\) Modify the bootcmd and bootargs of the U-boot. This step is a fixed operation. If the parameters are not modified, you need to perform this step only once. The system automatically enters the system each time the board is reset. - - >![](public_sys-resources/icon-notice.gif) **NOTICE:** - >By default, the U-boot boot program waits for two seconds. You can press Enter to interrupt the waiting and the message "hisillicon" is displayed. You can run the reset command to restart the system. - - **Table 1** U-boot startup parameters - - - - - - - - - -

Command

-

setenv bootcmd "sf probe 0;mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

saveenv

-

reset

-

Description

-

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800;go 0x80000000";

-

This command indicates that flash 0 is selected, and the contents with the start address of 0x800 (unit: 512 bytes, that is, 1 MB) and the size of 0x4800 (unit: 512 bytes, that is, 9 MB) are read to the memory address 0x80000000.

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

This command sets the boot parameters to serial port output, baud rate to 115200, data bit to 8, rootfs to be mounted to the eMMC, and file system type to vfat,

-

Enter the start burning position and length of rootfs.img in rootaddr=10M rootsize=15M rw. The size must be the same as that of the rootfs.img file added in the IDE.

-

saveenv indicates that the current configuration is saved.

-

reset indicates that the board is reset.

-

(Optional) go 0x40000000 indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, you can press Enter in the countdown phase of the U-Boot startup to interrupt the automatic startup.

-
- -3. Enter reset and press Enter to restart the board. After the board is restarted successfully, press Enter. OHOS is displayed. - - **Figure 10** Start the system - - - ![](figures/qi1.png) - -4. In the root directory, run the **./bin/hello\_uart** command line to operate the demo program. The following figure shows the compilation result. - - ``` - OHOS # ./bin/hello_uart - OHOS # HELLO UART! - ``` - - diff --git a/docs-en/quick-start/developing-the-first-driver-running-on-hi3516.md b/docs-en/quick-start/developing-the-first-driver-running-on-hi3516.md index 4988949c0330b60f51ed618cdf20e1e7d89f4bd6..fc45c48badb92cf3e580a41b518d8ec6c4a24e68 100755 --- a/docs-en/quick-start/developing-the-first-driver-running-on-hi3516.md +++ b/docs-en/quick-start/developing-the-first-driver-running-on-hi3516.md @@ -64,17 +64,26 @@ The following operations take a HDF-based UART driver as an example to show how ``` // Bind the UART driver interface to the HDF. - static int32_t HdfUartSampleBind(struct HdfDeviceObject *device) + static int32_t SampleUartDriverBind(struct HdfDeviceObject *device) { + struct UartHost *uartHost = NULL; + if (device == NULL) { return HDF_ERR_INVALID_OBJECT; } HDF_LOGI("Enter %s:", __func__); - return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; + + uartHost = UartHostCreate(device); + if (uartHost == NULL) { + HDF_LOGE("%s: UartHostCreate failed", __func__); + return HDF_FAILURE; + } + uartHost->service.Dispatch = SampleDispatch; + return HDF_SUCCESS; } // Obtain configuration information from the HCS of the UART driver. - static uint32_t UartDeviceGetResource( + static uint32_t GetUartDeviceResource( struct UartDevice *device, const struct DeviceResourceNode *resourceNode) { struct UartResource *resource = &device->resource; @@ -84,7 +93,7 @@ The following operations take a HDF-based UART driver as an example to show how HDF_LOGE("DeviceResourceIface is invalid"); return HDF_FAILURE; } - + if (dri->GetUint32(resourceNode, "num", &resource->num, 0) != HDF_SUCCESS) { HDF_LOGE("uart config read num fail"); return HDF_FAILURE; @@ -93,7 +102,7 @@ The following operations take a HDF-based UART driver as an example to show how HDF_LOGE("uart config read base fail"); return HDF_FAILURE; } - resource->physBase = (unsigned long) OsalIoRemap(resource->base, 0x48); + resource->physBase = (unsigned long)OsalIoRemap(resource->base, 0x48); if (resource->physBase == 0) { HDF_LOGE("uart config fail to remap physBase"); return HDF_FAILURE; @@ -126,7 +135,7 @@ The following operations take a HDF-based UART driver as an example to show how } // Attach the configuration and interface of the UART driver to the HDF. - static int32_t SampleAttach(struct UartHost *host, struct HdfDeviceObject *device) + static int32_t AttachUartDevice(struct UartHost *host, struct HdfDeviceObject *device) { int32_t ret; struct UartDevice *uartDevice = NULL; @@ -134,28 +143,28 @@ The following operations take a HDF-based UART driver as an example to show how HDF_LOGE("%s: property is NULL", __func__); return HDF_FAILURE; } - uartDevice = (struct UartDevice *) OsalMemCalloc(sizeof(struct UartDevice)); + uartDevice = (struct UartDevice *)OsalMemCalloc(sizeof(struct UartDevice)); if (uartDevice == NULL) { HDF_LOGE("%s: OsalMemCalloc uartDevice error", __func__); return HDF_ERR_MALLOC_FAIL; } - ret = UartDeviceGetResource(uartDevice, device->property); + ret = GetUartDeviceResource(uartDevice, device->property); if (ret != HDF_SUCCESS) { - (void) OsalMemFree(uartDevice); + (void)OsalMemFree(uartDevice); return HDF_FAILURE; } host->num = uartDevice->resource.num; host->priv = uartDevice; - UartSampleAddDev(host); // Add a user-space UART node. For details, see the source code uart_dev_sample. - return UartDeviceInit(uartDevice); // Initialize UART PL011. For details, see the source code uart_pl011_sample. + AddUartDevice(host); + return InitUartDevice(uartDevice); } // Initialize the UART driver. - static int32_t HdfUartSampleInit(struct HdfDeviceObject *device) + static int32_t SampleUartDriverInit(struct HdfDeviceObject *device) { int32_t ret; struct UartHost *host = NULL; - + if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_OBJECT; @@ -166,71 +175,71 @@ The following operations take a HDF-based UART driver as an example to show how HDF_LOGE("%s: host is NULL", __func__); return HDF_FAILURE; } - ret = SampleAttach(host, device); + ret = AttachUartDevice(host, device); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: attach error", __func__); return HDF_FAILURE; } - host->method = &g_uartSampleHostMethod; + host->method = &g_sampleUartHostMethod; return ret; } - static void UartDeviceDeinit(struct UartDevice *device) + static void DeinitUartDevice(struct UartDevice *device) { - struct UartRegisterMap *regMap = (struct UartRegisterMap *) device->resource.physBase; + struct UartRegisterMap *regMap = (struct UartRegisterMap *)device->resource.physBase; /* Wait for the UART to enter the idle state. */ while (UartPl011IsBusy(regMap)); UartPl011ResetRegisters(regMap); uart_clk_cfg(0, false); - OsalIoUnmap((void *) device->resource.physBase); + OsalIoUnmap((void *)device->resource.physBase); device->state = UART_DEVICE_UNINITIALIZED; } // Detach and release the UART driver. - static void SampleDetach(struct UartHost *host) + static void DetachUartDevice(struct UartHost *host) { struct UartDevice *uartDevice = NULL; - + if (host->priv == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return; } uartDevice = host->priv; - UartDeviceDeinit(uartDevice); - (void) OsalMemFree(uartDevice); + DeinitUartDevice(uartDevice); + (void)OsalMemFree(uartDevice); host->priv = NULL; } // Release the UART driver. - static void HdfUartSampleRelease(struct HdfDeviceObject *device) + static void SampleUartDriverRelease(struct HdfDeviceObject *device) { struct UartHost *host = NULL; HDF_LOGI("Enter %s:", __func__); - + if (device == NULL) { - HDF_LOGE("%s: device is null", __func__); + HDF_LOGE("%s: device is NULL", __func__); return; } host = UartHostFromDevice(device); if (host == NULL) { - HDF_LOGE("%s: host is null", __func__); + HDF_LOGE("%s: host is NULL", __func__); return; } if (host->priv != NULL) { - SampleDetach(host); + DetachUartDevice(host); } UartHostDestroy(host); } - struct HdfDriverEntry g_hdfUartSample = { + struct HdfDriverEntry g_sampleUartDriverEntry = { .moduleVersion = 1, .moduleName = "UART_SAMPLE", - .Bind = HdfUartSampleBind, - .Init = HdfUartSampleInit, - .Release = HdfUartSampleRelease, + .Bind = SampleUartDriverBind, + .Init = SampleUartDriverInit, + .Release = SampleUartDriverRelease, }; - HDF_INIT(g_hdfUartSample); + HDF_INIT(g_sampleUartDriverEntry); ``` 3. Register a UART driver interface. @@ -238,7 +247,7 @@ The following operations take a HDF-based UART driver as an example to show how Implement the UART driver interface using the template **UartHostMethod** provided by the HDF. ``` - static int32_t SampleInit(struct UartHost *host) + static int32_t SampleUartHostInit(struct UartHost *host) { HDF_LOGI("%s: Enter", __func__); if (host == NULL) { @@ -248,7 +257,7 @@ The following operations take a HDF-based UART driver as an example to show how return HDF_SUCCESS; } - static int32_t SampleDeinit(struct UartHost *host) + static int32_t SampleUartHostDeinit(struct UartHost *host) { HDF_LOGI("%s: Enter", __func__); if (host == NULL) { @@ -259,48 +268,47 @@ The following operations take a HDF-based UART driver as an example to show how } // Write data into the UART device. - static int32_t SampleWrite(struct UartHost *host, uint8_t *data, uint32_t size) + static int32_t SampleUartHostWrite(struct UartHost *host, uint8_t *data, uint32_t size) { HDF_LOGI("%s: Enter", __func__); uint32_t idx; struct UartRegisterMap *regMap = NULL; struct UartDevice *device = NULL; - + if (host == NULL || data == NULL || size == 0) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } - device = (struct UartDevice *) host->priv; + device = (struct UartDevice *)host->priv; if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_PARAM; } - regMap = (struct UartRegisterMap *) device->resource.physBase; + regMap = (struct UartRegisterMap *)device->resource.physBase; for (idx = 0; idx < size; idx++) { - while (UartPl011IsBusy(regMap)); UartPl011Write(regMap, data[idx]); } return HDF_SUCCESS; } // Set the baud rate of the UART device. - static int32_t SampleSetBaud(struct UartHost *host, uint32_t baudRate) + static int32_t SampleUartHostSetBaud(struct UartHost *host, uint32_t baudRate) { HDF_LOGI("%s: Enter", __func__); struct UartDevice *device = NULL; struct UartRegisterMap *regMap = NULL; UartPl011Error err; - + if (host == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } - device = (struct UartDevice *) host->priv; + device = (struct UartDevice *)host->priv; if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_PARAM; } - regMap = (struct UartRegisterMap *) device->resource.physBase; + regMap = (struct UartRegisterMap *)device->resource.physBase; if (device->state != UART_DEVICE_INITIALIZED) { return UART_PL011_ERR_NOT_INIT; } @@ -315,16 +323,16 @@ The following operations take a HDF-based UART driver as an example to show how } // Obtain the baud rate of the UART device. - static int32_t SampleGetBaud(struct UartHost *host, uint32_t *baudRate) + static int32_t SampleUartHostGetBaud(struct UartHost *host, uint32_t *baudRate) { HDF_LOGI("%s: Enter", __func__); struct UartDevice *device = NULL; - + if (host == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } - device = (struct UartDevice *) host->priv; + device = (struct UartDevice *)host->priv; if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_PARAM; @@ -334,13 +342,13 @@ The following operations take a HDF-based UART driver as an example to show how } // Bind the UART device using HdfUartSampleInit. - struct UartHostMethod g_uartSampleHostMethod = { - .Init = SampleInit, - .Deinit = SampleDeinit, + struct UartHostMethod g_sampleUartHostMethod = { + .Init = SampleUartHostInit, + .Deinit = SampleUartHostDeinit, .Read = NULL, - .Write = SampleWrite, - .SetBaud = SampleSetBaud, - .GetBaud = SampleGetBaud, + .Write = SampleUartHostWrite, + .SetBaud = SampleUartHostSetBaud, + .GetBaud = SampleUartHostGetBaud, .SetAttribute = NULL, .GetAttribute = NULL, .SetTransMode = NULL, @@ -436,32 +444,42 @@ Compile and burn images by referring to [Compiling Code](developing-the-first-e >![](public_sys-resources/icon-notice.gif) **NOTICE:** >The default waiting time in the U-boot is 2s. You can press **Enter** to interrupt the waiting and run the **reset** command to restart the system after "hisillicon" is displayed. - **Table 1** Startup parameters of the U-boot + **Table 1** Parameters of the U-boot - -

Command

+ + + - + + + - - + + + + + + +

Command

+

Description

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

saveenv

-

reset

+

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

+

Run this command to read content that has a size of 0x4800 (9 MB) and a start address of 0x800 (1 MB) to the memory address 0x80000000.

Description

-

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800;go 0x80000000";

-

Run this command to set the flash number to 0, and read content that has a size of 0x4800 (9 MB) and a start address of 0x800 (1 MB) to the memory address 0x80000000.

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

Run this command to set the output mode to serial port output, baud rate to 115200, data bit to 8, rootfs to be mounted to the emmc component, and file system type to vfat.

-

rootaddr=10M, rootsize=15M rw indicates the start address and size of the rootfs.img file to be burnt, respectively. The file size must be the same as that of the compiled file in the IDE.

-

saveenv means to save the current configuration.

-

reset means to reset the board.

-

(Optional) go 0x80000000 indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, press Enter in the countdown phase of the U-boot startup to interrupt the automatic startup.

+

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10 M rootsize=15 M rw";

+

Run this command to set the output mode to serial port output, baud rate to 115200, data bit to 8, rootfs to be mounted to the emmc component, and file system type to vfat.

+

rootaddr=10 M, rootsize=15 M rw indicates the start address and size of the rootfs.img file to be burnt, respectively. The file size must be the same as that of the compiled file in the IDE.

+

saveenv

+

saveenv means to save the current configuration.

+

reset

+

reset means to reset the board.

+ >![](public_sys-resources/icon-notice.gif) **NOTICE:** + >**go 0x80000000** \(optional\) indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, press **Enter** in the countdown phase of the U-boot startup to interrupt the automatic startup. + 3. Run the **reset** command and press **Enter** to restart the board. After the board is restarted, **OHOS** is displayed when you press **Enter**. **Figure 2** System startup diff --git a/docs-en/quick-start/developing-the-first-example-program-running-on-hi3516.md b/docs-en/quick-start/developing-the-first-example-program-running-on-hi3516.md index a325e9bf965df8ea1444b453d999dd6db5dbb7af..6bf2584da577b9443352544f5372c0d65cf241c3 100755 --- a/docs-en/quick-start/developing-the-first-example-program-running-on-hi3516.md +++ b/docs-en/quick-start/developing-the-first-example-program-running-on-hi3516.md @@ -47,7 +47,7 @@ This method applies only to development boards that have network ports, for exam ![](figures/successful-driver-installation.png "successful-driver-installation") 1. Power on the board and connect the serial port of the board to the Windows workstation. - 2. Install the [driver](http://www.hihope.org/download). + 2. Install the driver. 3. Open **Device Manager**, and then check and record the port number of **Prolific USB-to-Serial Comm Port**. In this example, the port number is COM11. After the driver is installed, right-click the device to uninstall it if a yellow exclamation mark is displayed on the device icon. After the driver is reinstalled, restart the PC as required. @@ -131,32 +131,42 @@ This method applies only to development boards that have network ports, for exam >![](public_sys-resources/icon-notice.gif) **NOTICE:** >The default waiting time in the U-boot is 2s. You can press **Enter** to interrupt the waiting and run the **reset** command to restart the system after "hisillicon" is displayed. - **Table 1** Startup parameters of the U-boot + **Table 1** Parameters of the U-boot - -

Command

+ + - + + + + - - + + + + + + +

Command

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

saveenv

-

reset

+

Description

+

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

+

Run this command to read content that has a size of 0x4800 (9 MB) and a start address of 0x800 (1 MB) to the memory address 0x80000000.

Description

-

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800;go 0x80000000";

-

Run this command to set the flash number to 0, and read content that has a size of 0x4800 (9 MB) and a start address of 0x800 (1 MB) to the memory address 0x80000000.

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

Run this command to set the output mode to serial port output, baud rate to 115200, data bit to 8, rootfs to be mounted to the emmc component, and file system type to vfat.

-

rootaddr=10 M, rootsize=15 M rw indicates the start address and size of the rootfs.img file to be burnt, respectively. The file size must be the same as that of the compiled file in the IDE.

-

saveenv means to save the current configuration.

-

reset means to reset the board.

-

(Optional) go 0x80000000 indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, press Enter in the countdown phase of the U-boot startup to interrupt the automatic startup.

+

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10 M rootsize=15 M rw";

+

Run this command to set the output mode to serial port output, baud rate to 115200, data bit to 8, rootfs to be mounted to the emmc component, and file system type to vfat.

+

rootaddr=10 M, rootsize=15 M rw indicates the start address and size of the rootfs.img file to be burnt, respectively. The file size must be the same as that of the compiled file in the IDE.

+

saveenv

+

saveenv means to save the current configuration.

+

reset

+

reset means to reset the board.

+ >![](public_sys-resources/icon-notice.gif) **NOTICE:** + >**go 0x80000000** \(optional\) indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, press **Enter** in the countdown phase of the U-boot startup to interrupt the automatic startup. + 3. Run the **reset** command and press **Enter** to restart the board. After the board is restarted, **OHOS** is displayed when you press **Enter**. **Figure 10** System startup diff --git a/docs-en/quick-start/developing-the-first-example-program-running-on-hi3518.md b/docs-en/quick-start/developing-the-first-example-program-running-on-hi3518.md index c089ebe8c505fb96a49ec77e69acec50e21f255c..3f640af00e50c7e0cc2d5f5d1caa58060fb21852 100755 --- a/docs-en/quick-start/developing-the-first-example-program-running-on-hi3518.md +++ b/docs-en/quick-start/developing-the-first-example-program-running-on-hi3518.md @@ -78,32 +78,46 @@ Burn images to the Hi3518EV300 board over the serial port. 2. \(Mandatory for the first burning\) Modify the **bootcmd** and **bootargs** parameters of U-boot. This step is a fixed operation and the result can be saved. However, you need to perform the following steps again if U-boot needs to be reburnt. - **Table 1** Parameters of the Hi3518EV300 U-boot + **Table 1** Parameters of the U-boot - -

Command

+ + - + + + + - - + + + + + + + + + +

Command

setenv bootcmd "sf probe 0;sf read 0x40000000 0x100000 0x600000;go 0x40000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=flash fstype=jffs2 rw rootaddr=7 M rootsize=8 M"; saveenv;

-

reset

+

Description

+

setenv bootcmd "sf probe 0;sf read 0x40000000 0x100000 0x600000;go 0x40000000";

+

Run this command to set the content of bootcmd. Select the flash whose number is 0, and read content that has a size of 0x600000 and a start address of 0x100000 to memory address 0x40000000.

Description

-

setenv bootcmd "sf probe 0;sf read 0x40000000 0x100000 0x600000;go 0x40000000";

-

Run this command to set the content of bootcmd. Select the flash whose number is 0, and read content that has a size of 0x600000 and a start address of 0x100000 to memory address 0x40000000.

-

(Optional) go 0x40000000 indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, press Enter in the countdown phase of the U-boot startup to interrupt the automatic startup.

-

setenv bootargs "console=ttyAMA0,115200n8 root=flash fstype=jffs2 rw rootaddr=7 M rootsize=8 M";

-

In this command, bootargs is set to the serial port output, the baud rate is 115200, the data bit is 8, and the rootfs is mounted to the flash memory. The file system type is set to jffs2 rw, which provides the read-write attribute for the JFFS2 file system.

-

rootaddr=7 M rootsize=8 M indicates the actual start address and length of the rootfs.img file to be burnt. The size must be the same as that of the compiled files in the HiTool.

-

saveenv indicates that the current configuration is saved.

-

reset indicates that the board is reset.

-

pri indicates the display parameters to be viewed.

+

setenv bootargs "console=ttyAMA0,115200n8 root=flash fstype=jffs2 rw rootaddr=7 M rootsize=8 M";

+

In this command, bootargs is set to the serial port output, the baud rate is 115200, the data bit is 8, and the rootfs is mounted to the flash memory. The file system type is set to jffs2 rw, which provides the read-write attribute for the JFFS2 file system. rootaddr=7 M rootsize=8 M indicates the actual start address and length of the rootfs.img file to be burnt, respectively. The size must be the same as that of the compiled files in the HiTool.

+

saveenv

+

saveenv means to save the current configuration.

+

reset

+

reset means to reset the board.

+

pri

+

pri means to view the displayed parameters.

+ >![](public_sys-resources/icon-notice.gif) **NOTICE:** + >**go 0x40000000** \(optional\) indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, press **Enter** in the countdown phase of the U-boot startup to interrupt the automatic startup. + **Figure 7** Startup parameter settings ![](figures/startup-parameter-settings.png "startup-parameter-settings") diff --git a/docs-en/quick-start/developing-the-first-example-program-running-on-hi3861.md b/docs-en/quick-start/developing-the-first-example-program-running-on-hi3861.md index 2093cee18a07929ea7b2ff0480e6f880d4db9c76..bc01e3820de773e8e0845e050e34006d2fdc4c57 100755 --- a/docs-en/quick-start/developing-the-first-example-program-running-on-hi3861.md +++ b/docs-en/quick-start/developing-the-first-example-program-running-on-hi3861.md @@ -4,7 +4,7 @@ This example shows how to use attention \(AT\) commands to complete WLAN module ## Acquiring Source Code -You need to acquire [Hi3861 source code](https://repo.huaweicloud.com/harmonyos/os/1.0/code-1.0.tar.gz) and download it on a Linux server. For more obtaining methods, see [Source Code Acquisition](en-us_topic_0000001050769927.md). +You need to acquire [Hi3861 source code](https://repo.huaweicloud.com/harmonyos/os/1.0/code-1.0.tar.gz) and download it on a Linux server. For more methods, see [Source Code Acquisition](../get-code/source-code-acquisition.md). ## Compiling Source Code @@ -66,7 +66,7 @@ You can use the DevEco tool to perform the image burning of the Hi3861 WLAN modu 1. Set a proper baud rate. A higher baud rate indicates a faster burning speed. Here **921600** is recommended. 2. Select a data bit. The default value of **Data bit** for the WLAN module is **8**. - 3. Select the version package path by running the **./out/wifiiot/Hi3861\_wifiiot\_app\_allinone.bin** command. + 3. Select the version package path **./out/wifiiot/Hi3861\_wifiiot\_app\_allinone.bin** and **Hiburn** mode. 4. Click **Save** to save configurations. **Figure 8** Baud rate and data bit configurations @@ -75,7 +75,7 @@ You can use the DevEco tool to perform the image burning of the Hi3861 WLAN modu **Figure 9** Path of the burning package - ![](figures/en-us_image_0000001054087868.png) + ![](figures/en-us_image_0000001055427138.png) 4. On the DevEco, click **Burn** ![](figures/en-us_image_0000001054443694.png) and select the serial port **COM11**. @@ -95,26 +95,36 @@ You can use the DevEco tool to perform the image burning of the Hi3861 WLAN modu ![](figures/en-us_image_0000001054802306.png) -## Connecting WLAN Module to Internet +## Connecting WLAN Module to the Internet -After completing version building and burning, do as follows to execute AT commands on DevEco to connect the WLAN module to internet. +After completing version building and burning, do as follows to connect the WLAN module to the Internet using AT commands. -1. Keep Windows workstation connected to the WLAN module and click **Serial port** at the bottom of DevEco to view the configuration page. +>![](public_sys-resources/icon-note.gif) **NOTE:** +>- The DevEco serial port connection function is to be launched soon, so stay tuned. +>- In this example, the serial port tool IPOP is used for demonstration. You can obtain this tool from the Internet. - **Figure 13** Opening the DevEco serial port - ![](figures/opening-the-deveco-serial-port.png "opening-the-deveco-serial-port") +1. Use a USB cable to connect the Windows workstation to the Hi3861 development board. Ensure that the serial port to be connected is **COM11**, as shown in the following figure. -2. Select the serial port and set parameters. Enter the actual serial port number. Here the serial port number is **COM11**; the default values of Baud rate, Data bit, and Stop bit are used; **1** is used as the end-line character since a valid input AT command must end with **\\r\\n**. + **Figure 13** COM port of the device manager + + + ![](figures/en-us_image_0000001055268090.png) + +2. On the Windows workstation, use IPOP to connect to the serial port \(COM11\) of the WLAN module, set the baud rate to **115200**, select **Newline**, and ensure that the entered string ends with **\\r\\n** to avoid AT command input failures. - **Figure 14** Configuring the serial port - ![](figures/configuring-the-serial-port.png "configuring-the-serial-port") + **Figure 14** IPOP connection configuration + + + ![](figures/en-us_image_0000001055427946.png) 3. Reset the WLAN module. The message **ready to OS start** is displayed on the terminal page, indicating that the WLAN module is started successfully. - **Figure 15** Resetting the WLAN module - ![](figures/resetting-the-wlan-module.png "resetting-the-wlan-module") + **Figure 15** Successful resetting of the WLAN module + + + ![](figures/en-us_image_0000001055148043.png) -4. Run the following AT commands on the DevEco serial port in sequence to start the STA, connect to the specified AP, and enable the Dynamic Host Configuration Protocol \(DHCP\). +4. Run the following AT commands in sequence via the IPOP serial terminal to start the STA mode, connect to the specified AP, and enable Dynamic Host Configuration Protocol \(DHCP\). ``` AT+STARTSTA - Start the STA mode. @@ -122,12 +132,19 @@ After completing version building and burning, do as follows to execute AT comma AT+SCANRESULT - Display the scanning result. AT+CONN="SSID",,2,"PASSWORD" - Connect to the specified AP. (SSID and PASSWORD represent the name and password of the hotspot to be connected, respectively.) AT+STASTAT - View the connection result. - AT+DHCP=wlan0,1 - Request the IP address of wlan0 from the AP via DHCP. + AT+DHCP=wlan0,1 - Request the IP address of wlan0 from the AP using DHCP. + ``` + +5. Check whether the WLAN module is properly connected to the gateway, as shown in the following figure. + + ``` + AT+IFCFG - View the IP address assigned to an interface of the module. + AT+PING=X.X.X.X - Check the connectivity between the module and the gateway. Replace X.X.X.X with the actual gateway address. ``` -5. An IP address is assigned and the interface is properly connected to the gateway. + **Figure 16** Successful networking of the WLAN module + - **Figure 16** Successful networking of the WLAN module - ![](figures/successful-networking-of-the-wlan-module.png "successful-networking-of-the-wlan-module") + ![](figures/en-us_image_0000001055428072.png) diff --git a/docs-en/quick-start/developing-the-first-example-program-running-on-the-hi3518-development-board.md b/docs-en/quick-start/developing-the-first-example-program-running-on-the-hi3518-development-board.md deleted file mode 100755 index 93b2832eea459c8cba79ba9658184a7a56e40e9f..0000000000000000000000000000000000000000 --- a/docs-en/quick-start/developing-the-first-example-program-running-on-the-hi3518-development-board.md +++ /dev/null @@ -1,124 +0,0 @@ -# Developing the First Example Program running on the Hi3518 Development Board - -Prerequisites: A Windows workstation, Linux compilation environment, and board hardware have been set up. - -This section describes how to modify, compile, burn, and run the first application on the board. - -## Obtaining the Source Code - -You need to download a set of source code from the Linux server. For details, see [Source Code Acquisition](en-us_topic_0000001050769927.md). - -## Modifying an application - -The **helloworld.c** code in the **applications/sample/camera/app/src** directory is as following. You can customize the content to be printed. For example, you can change OHOS to World. The current applications can be developed using ISO C and C++. - -``` -#include -#include "los_sample.h" - -int main(int argc, char **argv) -{ - printf("\n************************************************\n"); - printf("\n\t\tHello OHOS!\n"); - printf("\n************************************************\n\n"); - - LOS_Sample(g_num); - - return 0; -} -``` - -## Compiling - -On the Linux server, access the root directory of the source code package and run the following script to compile the source code package. - -Hi3518EV300 - -``` -python build.py ipcamera_hi3518ev300 -b debug -``` - -## Burning - -## Methods for Burning the Kernel Images - -Burn images to the Hi3518EV300 board over the serial port. - -**Burning images over the serial port** - -1. Connect a serial cable \(USB port\) of the board to the Windows host and open the **Device Manager** to check whether the serial port number is COM7. - - **Figure 1** Successful driver installation - - - ![](figures/en-us_image_0000001054099459.png) - -2. Open the HiTool, select the chip Hi3518EV300, set the serial port to COM7 in the **Local PC Config**, and set the **Transfer Mode** to serial according to the number sequence. - - **Figure 2** Procedures for configuring the serial port of HiTool - ![](figures/procedures-for-configuring-the-serial-port-of-hitool.png "procedures-for-configuring-the-serial-port-of-hitool") - -3. Burn the newly compiled **OHOS\_Image.bin** file, **rootfs.img**, and **userfs.img** files to the board. Ensure the file path, flash type, burning start address, and burning length are correct. Modify relevant parameters based on the actual file size. The value is rounded up, in MB. - - **Figure 3** Procedures for burning images using HiTool - ![](figures/procedures-for-burning-images-using-hitool.png "procedures-for-burning-images-using-hitool") - -4. Click **Burn** and power on the board as required. \(If the board is powered on, power it off and then power it on.\) - - **Figure 4** - ![](figures/en-us_image_0000001054219799.png "en-us_image_0000001054219799") - -5. Click OK. - - **Figure 5** Successful burning result - ![](figures/successful-burning-result.png "successful-burning-result") - - -## Running an Image - -1. Connect to the serial port. After the images are burnt successfully, open the terminal, click **Settings** to set the view title to Terminal 11 and serial port number to COM7, and click OK to open the serial port. You have logged in to the U-Boot if **hisilicon \#** is displayed. - - **Figure 6** Serial port connection - ![](figures/serial-port-connection.png "serial-port-connection") - -2. \(Mandatory for the first burning\) Modify the **bootcmd** and **bootargs** parameters of U-Boot. This step is a fixed operation and the result can be saved. However, you need to perform the following steps again if U-Boot needs to be reburnt. - - **Table 1** Parameter of the Hi3518EV300 U-Boot - - - - - - - - - -

Command

-

setenv bootcmd "sf probe 0;sf read 0x40000000 0x100000 0x600000;go 0x40000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=flash fstype=jffs2 rw rootaddr=7M rootsize=8M"; saveenv;

-

reset

-

Description

-

setenv bootcmd "sf probe 0;sf read 0x40000000 0x100000 0x600000;go 0x40000000";

-

Set the content of bootcmd. Select the flash whose number is 0, and read content of 0x600000 that has a flash start address of 0x100000 to memory address 0x40000000.

-

(Optional) go 0x40000000 indicates that the command is fixed in the startup parameters by default and the board automatically starts after it is reset. If you want to manually start the board, you can press Enter in the countdown phase of the U-Boot startup to interrupt the automatic startup.

-

setenv bootargs "console=ttyAMA0,115200n8 root=flash fstype=jffs2 rw rootaddr=7M rootsize=8M";

-

In this command, bootargs is set to the serial port output, the baud rate is 115200, the data bit is 8, the rootfs is mounted to the flash memory. The file system type is set to jffs2 rw to provide the read-write attribute for the JFFS2 file system.

-

rootaddr=7M rootsize=8M indicates the actual start address and length of the rootfs.img file to be burnt. The size must be the same as that of the compiled files in the HiTool.

-

saveenv indicates that the current configuration is saved.

-

reset indicates that the board is reset.

-

pri indicates the display parameters to be viewed.

-
- - **Figure 7** Startup parameter setting - ![](figures/startup-parameter-setting.png "startup-parameter-setting") - -3. Run the **reset** command if **hisilicon \#** is displayed during the startup. After the system automatically starts, run the **./bin/camera\_app** command and then click **Enter** when **OHOS** is displayed. The following figure indicates that the operation is successful. - - **Figure 8** Successful startup and application execution - ![](figures/successful-startup-and-application-execution.png "successful-startup-and-application-execution") - - -## Follow-up Learning - -You have got started with the IoT Camera. You can go on learning [Cameras with a Screen](en-us_topic_0000001052854655.md) in the next step. - diff --git a/docs-en/quick-start/developing-the-second-example-program-running-on-hi3861.md b/docs-en/quick-start/developing-the-second-example-program-running-on-hi3861.md index 7277d0ec3ffc0de0b82c7f062045f1e2639f97b5..e6dea83f27f5d44c29e023b0765a1fe83f642885 100755 --- a/docs-en/quick-start/developing-the-second-example-program-running-on-hi3861.md +++ b/docs-en/quick-start/developing-the-second-example-program-running-on-hi3861.md @@ -29,7 +29,7 @@ The source code needs to be modified when fixing bugs or compiling a new service Create the service entry function **HelloWorld** in **hello\_world.c** and implement service logic. Use the SYS\_RUN\(\) of the OpenHarmony **bootstrap** module to start services. \(**SYS\_RUN** is defined in the **ohos\_init.h** file.\) ``` - #include + #include #include "ohos_init.h" #include "ohos_types.h" @@ -145,5 +145,5 @@ wifi init success! ## Follow-up Learning -Congratulations! You have finished all steps! You are advised to go on learning how to develop [WLAN-connected products](en-us_topic_0000001054530966.md). +Congratulations! You have finished all steps! You are advised to go on learning how to develop WLAN-connected products. diff --git a/docs-en/quick-start/faqs-0.md b/docs-en/quick-start/faqs-0.md index 12610762aaa2b6fdc3817b3ee45220e1a65e6f91..b945555a3b5a2b00136fe4e0365f8db6ba51ed7d 100755 --- a/docs-en/quick-start/faqs-0.md +++ b/docs-en/quick-start/faqs-0.md @@ -1,19 +1,19 @@ # FAQs -**Problem 1: What should I do when the images failed to be burnt over the selected serial port?** +1. What should I do when the images failed to be burnt over the selected serial port? + - **Symptom** -- **Symptom** + **Error: Opening COMxx: Access denied** is displayed after clicking **Burn** and selecting a serial port. - **Error: Opening COMxx: Access denied** is displayed after clicking **Burn** and selecting a serial port. + **Figure 1** Failed to open the serial port + ![](figures/failed-to-open-the-serial-port.png "failed-to-open-the-serial-port") - **Figure 1** Failed to open the serial port - ![](figures/failed-to-open-the-serial-port.png "failed-to-open-the-serial-port") + - **Possible Causes** -- **Possible Causes** + The serial port has been used. - The serial port has been used. + - **Solutions** -- **Solutions** 1. Search for the terminal using serial-xx from the drop-down list in the **TERMINAL** panel. @@ -31,7 +31,7 @@ ![](figures/restarting-burning.png "restarting-burning") -**Problem 2: What should I do when Windows-based PC failed to be connected to the board?** +2. What should I do when Windows-based PC failed to be connected to the board? - **Symptom** @@ -70,7 +70,7 @@ ![](figures/allowing-the-visual-studio-code-application-to-access-the-network.png "allowing-the-visual-studio-code-application-to-access-the-network") -**Problem 3: What should I do when the image failed to be burnt?** +3. What should I do when the image failed to be burnt? - **Symptom** @@ -85,7 +85,7 @@ Restart the IDE. -**Question 4:** **What should I do when the message indicating Python cannot be found is displayed during compilation and building?** +4. What should I do when the message indicating Python cannot be found is displayed during compilation and building? - **Symptom** @@ -103,7 +103,7 @@ - **Solutions** - Run the following command: + Run the following commands: ``` # cd /usr/bin/ @@ -117,19 +117,19 @@ ![](figures/en-us_image_0000001054875562.png) -**Problem 5:** **What should I do when no command output is displayed?** +5. What should I do when no command output is displayed? - **Symptom** The serial port shows that the connection has been established. After the board is restarted, nothing is displayed when you press **Enter**. -- **Possible Causes** - - The serial port is connected incorrectly. - - The U-boot of the board is damaged. +- **Possible Cause 1** + + The serial port is connected incorrectly. - **Solutions** - **Solution 1: Change the serial port number.** + Change the serial port number. Start **Device Manager** to check whether the serial port connected to the board is the same as that connected to the terminal device. If not, perform the following steps to change the serial port number. @@ -142,9 +142,16 @@ 3. Change the serial port number in the dialog box and click **OK**. 4. Press **Enter** in the dialog box to check whether any command output is displayed after the connection is established. -**Solution 2: Burn the U-boot.** +- **Possible Cause 2** + + The U-boot of the board is damaged. + +- **Solutions** + + Burn the U-boot. + + If the fault persists after you perform the preceding operations, the U-boot of the board may be damaged. You can burn the U-boot by performing the following steps: -If the fault persists after you perform the preceding operations, the U-boot of the board may be damaged. You can burn the U-boot by performing the following steps: 1. Obtain the U-boot file. diff --git a/docs-en/quick-start/faqs-1.md b/docs-en/quick-start/faqs-1.md deleted file mode 100755 index 6352b21e98011fcd12bc1a666747b6e6c8281fc6..0000000000000000000000000000000000000000 --- a/docs-en/quick-start/faqs-1.md +++ /dev/null @@ -1,73 +0,0 @@ -# FAQs - -**Problem 1:** **No command output is displayed.** - -- **Description** - - The serial port shows that the connection has been established. After the board is restarted, nothing is displayed when you press **Enter**. - -- **Possible Causes** - - The serial port is connected incorrectly. - - The U-Boot of the board is damaged. - -- **Solutions** - - Open the Device Manager to check whether the serial port connected to the board is the same as that connected to the terminal. If not, change the serial port as required. - - **Figure 1** Procedures for changing the serial port number - ![](figures/procedures-for-changing-the-serial-port-number.png "procedures-for-changing-the-serial-port-number") - - -1. Disconnect from the current serial port. -2. Click **Settings**. -3. Change the serial port number in the dialog box and click **OK**. -4. Press **Enter** in the dialog box to check whether any command output is displayed after the connection is established. - -If the fault persists after you perform all the preceding operations, the U-Boot of the board may be damaged. You can burn the U-Boot by performing the following steps. - -1. Obtain the U-Boot file. - - >![](public_sys-resources/icon-notice.gif) **NOTICE:** - >The U-Boot file of the board can be obtained from **vendor\\hisi\\hi35xx\\hi3518ev300\\uboot\\out\\boot\\u-boot-hi3518ev300.bin** in the open-source package. - -2. Use HiTool to burn the U-Boot as required and click **Burn**. - - **Figure 2** Procedures for burning the U-Boot using HiTool - ![](figures/procedures-for-burning-the-u-boot-using-hitool.png "procedures-for-burning-the-u-boot-using-hitool") - - 1. Select the COM7 serial port. - 2. Set **Transfer Mode** to serial. - 3. Select **Burn Fastboot**. - 4. Set the flash type to **spi nor**. - 5. Click **Browse** and select the corresponding U-Boot file. - 6. Click **Burn** to start burning. - -3. Power off the board and then power it on. Connect the serial port after the burning is complete, and the result is shown in the following figure. - - **Figure 3** Serial port display after burning the U-Boot - ![](figures/serial-port-display-after-burning-the-u-boot.png "serial-port-display-after-burning-the-u-boot") - - -**Problem 2: The following error is reported when HiTool is burning the U-Boot.** - -- **Description** - - **Figure 4** Burning error reporting - - - ![](figures/en-us_image_0000001054421058.png) - -- **Possible Cause** - - The flash type selected by HiTool is incorrect. - -- **Solution** - - Change the flash type in the area marked with **1** in the following figure. - - **Figure 5** Incorrect flash type - - - ![](figures/en-us_image_0000001053941057.png) - - diff --git a/docs-en/quick-start/faqs-2.md b/docs-en/quick-start/faqs-2.md index feb6b39d5cd0b5b64fa2ca606ae2921b739b9879..d25fdac81e684dd1009d20d3096d33dbf647eb1a 100755 --- a/docs-en/quick-start/faqs-2.md +++ b/docs-en/quick-start/faqs-2.md @@ -1,112 +1,121 @@ # FAQs -**Problem 1:** **What should I do when no command output is displayed?** +1. What should I do when no command output is displayed? + - **Symptom** -- **Symptom** + The serial port shows that the connection has been established. After the board is restarted, nothing is displayed when you press **Enter**. - The serial port shows that the connection has been established. After the board is restarted, nothing is displayed when you press **Enter**. + - **Possible Cause 1** -- **Possible Causes** - - The serial port is connected incorrectly. - - The U-boot of the board is damaged. + The serial port is connected incorrectly. -- **Solutions** + - **Solutions** - **Solution 1: Change the serial port number.** + Change the serial port number. - Start **Device Manager** to check whether the serial port connected to the board is the same as that connected to the terminal device. If not, perform the following steps to change the serial port number. + Start **Device Manager** to check whether the serial port connected to the board is the same as that connected to the terminal device. If not, perform the following steps to change the serial port number. - **Figure 1** Procedure for changing the serial port number - ![](figures/procedure-for-changing-the-serial-port-number-4.png "procedure-for-changing-the-serial-port-number-4") + **Figure 1** Procedure for changing the serial port number + ![](figures/procedure-for-changing-the-serial-port-number-4.png "procedure-for-changing-the-serial-port-number-4") + 1. Disconnect from the current serial port. + 2. Click **Settings**. + 3. Change the serial port number in the dialog box and click **OK**. + 4. Press **Enter** in the dialog box to check whether any command output is displayed after the connection is established. -1. Disconnect from the current serial port. -2. Click **Settings**. -3. Change the serial port number in the dialog box and click **OK**. -4. Press **Enter** in the dialog box to check whether any command output is displayed after the connection is established. + - **Possible Cause 2** -**Solution 2: Burn the U-boot.** + The U-boot of the board is damaged. -If the fault persists after you perform the preceding operations, the U-boot of the board may be damaged. You can burn the U-boot by performing the following steps: + - **Solutions** -1. Obtain the U-boot file. + Burn the U-boot. - >![](public_sys-resources/icon-notice.gif) **NOTICE:** - >The U-boot file of the board can be obtained from **vendor\\hisi\\hi35xx\\hi3518ev300\\uboot\\out\\boot\\u-boot-hi3518ev300.bin** in the open-source package. + If the fault persists after you perform the preceding operations, the U-boot of the board may be damaged. You can burn the U-boot by performing the following steps: -2. Use HiTool to burn the U-boot. + 1. Obtain the U-boot file. - **Figure 2** Procedure for burning the U-boot using HiTool - ![](figures/procedure-for-burning-the-u-boot-using-hitool.png "procedure-for-burning-the-u-boot-using-hitool") + >![](public_sys-resources/icon-notice.gif) **NOTICE:** + >1. The U-boot file of the board can be obtained from **vendor\\hisi\\hi35xx\\hi3518ev300\\uboot\\out\\boot\\u-boot-hi3518ev300.bin** in the open-source package. - 1. Select the COM7 serial port. - 2. Select **Serial** for **Transfer Mode**. - 3. Select **Burn Fastboot**. - 4. Select **spi nor** for **Flash Type**. - 5. Click **Browse** and select the corresponding U-boot file. - 6. Click **Burn** to start burning. + 2. Use HiTool to burn the U-boot. -3. Power off the board and then power it on. Connect the serial port after the burning is complete. Serial ports shown in the following figure are displayed after the U-boot is burnt. + **Figure 2** Procedure for burning the U-boot using HiTool + ![](figures/procedure-for-burning-the-u-boot-using-hitool.png "procedure-for-burning-the-u-boot-using-hitool") - **Figure 3** Serial port displayed after the U-boot is burnt - ![](figures/serial-port-displayed-after-the-u-boot-is-burnt-5.png "serial-port-displayed-after-the-u-boot-is-burnt-5") + 1. Select the COM7 serial port. + 2. Select **Serial** for **Transfer Mode**. -**Problem 2: What should I do when an error is reported when the U-boot is burnt using HiTool?** + 3. Select **Burn Fastboot**. -- **Symptom** + 4. Select **spi nor** for **Flash Type**. - **Figure 4** Burning error - + 5. Click **Browse** and select the corresponding U-boot file. - ![](figures/en-us_image_0000001054421058.png) + 6. Click **Burn** to start burning. -- **Possible Causes** + 3. Power off the board and then power it on. Connect the serial port after the burning is complete. Serial ports shown in the following figure are displayed after the U-boot is burnt. - The flash type selected by HiTool is incorrect. + **Figure 3** Serial port displayed after the U-boot is burnt + ![](figures/serial-port-displayed-after-the-u-boot-is-burnt-5.png "serial-port-displayed-after-the-u-boot-is-burnt-5") -- **Solutions** - Change the flash type in the area marked with **1** in the following figure. - **Figure 5** Incorrect flash type - +2. What should I do when an error is reported when the U-Boot is burnt using HiTool? + - **Symptom** - ![](figures/en-us_image_0000001053941057.png) + **Figure 4** Burning error + + ![](figures/en-us_image_0000001054421058.png) -**Question 3:** **What should I do when the message indicating Python cannot be found is displayed during compilation and building?** + - **Possible Causes** -- **Symptom** + The flash type selected by HiTool is incorrect. - ![](figures/en-us_image_0000001054476115.png) + - **Solutions** + Change the flash type in the area marked with **1** in the following figure. -- **Possible Cause 1** + **Figure 5** Incorrect flash type + - Python is not installed. + ![](figures/en-us_image_0000001053941057.png) -- **Solutions** - Install the Python environment by referring to [Installing a Python Environment](setting-up-a-development-environment-1.md#section918195118487). +3. What should I do when the message indicating Python cannot be found is displayed during compilation and building? + - **Symptom** -- **Possible Cause 2** + ![](figures/en-us_image_0000001054476115.png) - ![](figures/en-us_image_0000001054876092.png) -- **Solutions** + - **Possible Cause 1** - If the soft link that points to the Python does not exist in the **usr/bin** directory, run the following command: + Python is not installed. - ``` - # cd /usr/bin/ - # which python3 - # ln -s /usr/local/bin/python3 python - # python --version - ``` + - **Solutions** - Example: + Install the Python environment by referring to [Installing a Python Environment](setting-up-a-development-environment-1.md#section918195118487). + + - **Possible Cause 2** + + ![](figures/en-us_image_0000001054876092.png) + + - **Solutions** + + Run the following commands: + + ``` + # cd /usr/bin/ + # which python3 + # ln -s /usr/local/bin/python3 python + # python --version + ``` + + Example: + + ![](figures/en-us_image_0000001055194682.png) - ![](figures/en-us_image_0000001055194682.png) diff --git a/docs-en/quick-start/faqs.md b/docs-en/quick-start/faqs.md index 623cb4b8ad3825e1915fa976f1a6714a97b6f217..a8fb9deaf46bde688f259f939317f859808fb2ef 100755 --- a/docs-en/quick-start/faqs.md +++ b/docs-en/quick-start/faqs.md @@ -1,217 +1,224 @@ # FAQs -**Question 1:** What should I do when the message **configure: error: no acceptable C compiler found in $PATH** is displayed during Python 3 installation? +1. What should I do when the message **configure: error: no acceptable C compiler found in $PATH** is displayed during Python 3 installation? + - **Symptom** -- **Symptom** + ![](figures/en-us_image_0000001053782588.png) - ![](figures/en-us_image_0000001053782588.png) + - **Possible Causes** -- **Possible Causes** + **gcc** is not installed. - **gcc** is not installed. + - **Solutions** -- **Solutions** + 1. Run the **apt-get install gcc** command to install **gcc** online. - 1. Run the **apt-get install gcc** command to install **gcc** online. + 2. After the installation, reinstall Python 3. - 2. After the installation, reinstall Python 3. +2. What should I do when the message **-bash: make: command not found** is displayed during Python 3 installation? + - **Symptom** -**Question 2:** What should I do when the message **-bash: make: command not found** is displayed during Python 3 installation? + ![](figures/en-us_image_0000001053302604.png) -- **Symptom** + - **Possible Causes** - ![](figures/en-us_image_0000001053302604.png) + **make** is not installed. -- **Possible Causes** + - **Solutions** - **make** is not installed. + 1. Run the **apt-get install make** command to install **make** online. -- **Solutions** + 2. After the installation, reinstall Python 3. - 1. Run the **apt-get install make** command to install **make** online. - 2. After the installation, reinstall Python 3. +3. What should I do when the message **zlib not available** is displayed during Python 3 installation? + - **Symptom** + ![](figures/en-us_image_0000001053183929.png) -**Question 3:** What should I do when the message **zlib not available** is displayed during Python 3 installation? + - **Possible Causes** -- **Symptom** + **zlib** is not installed. - ![](figures/en-us_image_0000001053183929.png) + - **Solutions** -- **Possible Causes** + Solution 1: Run the **apt-get install zlib** command to install **zlib** online. - **zlib** is not installed. + Solution 2: If the software source does not contain **zlib**, download the source code from [http://www.zlib.net/](http://www.zlib.net/). -- **Solutions** + ![](figures/10.png) - Method 1: Run the **apt-get install zlib** command to install **zlib** online. + Then run the following commands to install **zlib** offline: - Method 2: If the software source does not contain **zlib**, download the source code from http://www.zlib.net/ and install it offline. + ``` + # tar xvf zlib-1.2.11.tar.gz + # cd zlib-1.2.11 + # ./configure + # make && make install + ``` - ![](figures/en-us_image_0000001053782588.png) + After the installation, reinstall Python 3. - Installation commands: - tar xvf zlib-1.2.11.tar.gz +4. What should I do when the message **No module named '\_ctypes'** is displayed during Python 3 installation? + - **Symptom** - cd zlib-1.2.11 + ![](figures/en-us_image_0000001052623895.png) - ./configure - make && make install + - **Possible Causes** - 3. After the installation, reinstall Python 3. + **libffi** and **libffi-devel** are not installed. -**Question 4:** What should I do when the message **No module named '\_ctypes'** is displayed during Python 3 installation? + - **Solutions** -- **Symptom** + 1. Run the **apt-get install libffi\* -y** command to install **libffi** and **libffi-devel** online. - ![](figures/en-us_image_0000001052623895.png) + 2. After the installation, reinstall Python 3. -- **Possible Causes** +5. What should I do when the message **No module named 'Crypto'** is displayed during compilation and building? + - **Symptom** - **libffi** and **libffi-devel** are not installed. + ![](figures/en-us_image_0000001052983874.png) -- **Solutions** + - **Possible Causes** - 1. Run the **apt-get install libffi\* -y** command to install **libffi** and **libffi-devel** online. + **Crypto** is not installed. - 2. After the installation, reinstall Python 3. + - **Solutions** -**Question 5:** What should I do when the message **No module named 'Crypto'** is displayed during compilation and building? + Solution 1: Run the **pip3 install Crypto** command to install **Crypto** online. -- **Symptom** + Solution 2: Install **Crypto** offline. - ![](figures/en-us_image_0000001052983874.png) + - Download the source code from [https://pypi.org/project/pycrypto/\#files](https://pypi.org/project/pycrypto/#files). + ![](figures/en-us_image_0000001053462612.png) -- **Possible Causes** + - Save the source code package to the Linux server, decompress the package, and run the **python3 setup.py install** command to install **Crypto**. + - Rebuild an environment. - **Crypto** is not installed. +6. What should I do when the message **No module named 'ecdsa'** is displayed during compilation and building? + - **Symptom** -- **Solutions** + ![](figures/en-us_image_0000001053782588.png) - Method 1: Run the **pip3 install Crypto** command to install **Crypto** online. - Method 2: Install **Crypto** offline. + - **Possible Causes** - - Download the source code from [https://pypi.org/project/pycrypto/\#files](https://pypi.org/project/pycrypto/#files). + **ecdsa** is not installed. - ![](figures/en-us_image_0000001053462612.png) - - Save the source code package to the Linux server, decompress the package, and run the **python3 setup.py install** command to install **Crypto**. - - Rebuild an environment. + - **Solutions** + Solution 1: Run the **pip3 install ecdsa** command to install **ecdsa** online. -**Problem 6:** What should I do when the message **No module named 'ecdsa'** is displayed during compilation and building? + Solution 2: Install **ecdsa** offline. -- **Symptom** + - Download the installation package from [https://pypi.org/project/ecdsa/\#files](https://pypi.org/project/ecdsa/#files). - ![](figures/en-us_image_0000001053782588.png) + ![](figures/en-us_image_0000001053022609.png) + - Save the installation package to the Linux server and run the **pip3 install ecdsa-0.15-py2.py3-none-any.whl** command to install **ecdsa**. + - Rebuild an environment. -- **Possible Causes** - **ecdsa** is not installed. +7. What should I do when the message **Could not find a version that satisfies the requirement six\>=1.9.0** is displayed during compilation and building? + - **Symptom** + ![](figures/en-us_image_0000001052862621.png) -- **Solutions** - Method 1: Run the **pip3 install ecdsa** command to install **ecdsa** online. + - **Possible Causes** - Method 2: Install **ecdsa** offline. + **six** is not installed. - - Download the installation package from [https://pypi.org/project/ecdsa/\#files](https://pypi.org/project/ecdsa/#files). - ![](figures/en-us_image_0000001053022609.png) + - **Solutions** - - Save the installation package to the Linux server and run the **pip3 install ecdsa-0.15-py2.py3-none-any.whl** command to install **ecdsa**. - - Rebuild an environment. + Solution 1: Run the **pip3 install six** command to install **six** online. + Solution 2: Install **six** offline. -**Question 7:** What should I do when the message **Could not find a version that satisfies the requirement six\>=1.9.0** is displayed during compilation and building? + - Download the installation package from [https://pypi.org/project/six/\#files](https://pypi.org/project/six/#files). -- **Symptom** + ![](figures/en-us_image_0000001052742629.png) - ![](figures/en-us_image_0000001052862621.png) + - Save the source code to the Linux server and run the **pip3 install six-1.14.0-py2.py3-none-any.whl** command to install **six**. + - Rebuild an environment. -- **Possible Causes** +8. What should I do when the message **cannot find -lgcc** is displayed during compilation and building? + - **Symptom** - **six** is not installed. + ![](figures/en-us_image_0000001053142611.png) -- **Solutions** + - **Possible Causes** - Method 1: Run the **pip3 install six** command to install **six** online. + The PATH is incorrectly written by **gcc\_riscv32**, as shown in the following figure. There is an extra slash \(/\). - Method 2: Install **six** offline. + ![](figures/en-us_image_0000001055682654.png) - - Download the installation package from [https://pypi.org/project/six/\#files](https://pypi.org/project/six/#files). - ![](figures/en-us_image_0000001052742629.png) + - **Solutions** - - Save the source code to the Linux server and run the **pip3 install six-1.14.0-py2.py3-none-any.whl** command to install **six**. - - Rebuild an environment. + Modify the PATH by deleting the slash \(/\). + ![](figures/en-us_image_0000001054804171.png) -**Question 8:** What should I do when the message **cannot find -lgcc** is displayed during compilation and building? -- **Symptom** +9. What should I do when the message indicating Python cannot be found is displayed during compilation and building? + - **Symptom** - ![](figures/en-us_image_0000001053142611.png) + ![](figures/en-us_image_0000001055172843.png) -- **Possible Causes** + - **Possible Cause 1:** Python is not installed. + - **Solutions** - The PATH is incorrectly written by **gcc\_riscv32**, as shown in the following figure. There is an extra slash \(/\). + Install Python by referring to [Installing a Python Environment](setting-up-a-development-environment.md#section126831816258). - ![](figures/en-us_image_0000001055682654.png) + - **Possible Cause 2:** The soft link that points to the Python does not exist in the **usr/bin** directory. + ![](figures/en-us_image_0000001055372855.png) -- **Solutions** + - **Solutions** - Modify the PATH by deleting the slash \(/\). + Run the following commands: - ![](figures/en-us_image_0000001054804171.png) + ``` + # cd /usr/bin/ + # which python3 + # ln -s /usr/local/bin/python3 python + # python --version + ``` + Example: -**Question 9:** What should I do when the message indicating Python cannot be found is displayed during compilation and building? + ![](figures/en-us_image_0000001055012855.png) -- **Symptom** - ![](figures/en-us_image_0000001055073889.png) +10. What should I do when an error with **lsb\_release** occurs during **kconfiglib** installation? + - **Symptom** + The following error information is displayed during **kconfiglib** installation: -- **Possible Cause 1:** Python is not installed. -- **Solutions** + "subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1." - Install Python by referring to [Installing a Python Environment](setting-up-a-development-environment.md#section126831816258). + - **Possible Causes** -- **Possible Cause 2:** The soft link that points to the Python does not exist in the **usr/bin** directory. + The Python version matched with the **lsb\_release** module is different from the current Python version. - ![](figures/en-us_image_0000001054713955.png) + - **Solutions** -- **Solutions** + Run the **find / -name lsb\_release** command, for example, **sudo rm -rf /usr/bin/lsb\_release** to locate and delete **lsb\_release**. - Run the following command: - - ``` - # cd /usr/bin/ - # which python3 - # ln -s /usr/local/bin/python3 python - # python --version - ``` - - Example: - - ![](figures/en-us_image_0000001055674130.png) diff --git a/docs-en/quick-start/figures/10.png b/docs-en/quick-start/figures/10.png new file mode 100644 index 0000000000000000000000000000000000000000..3b7f6f4766c54f6ca1e0057fc8f869785cc63e56 Binary files /dev/null and b/docs-en/quick-start/figures/10.png differ diff --git a/docs-en/quick-start/figures/add-a-board.png b/docs-en/quick-start/figures/add-a-board.png deleted file mode 100755 index 81a2b496aacb056d6003d0fe440e9d1dfe8f96e8..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/add-a-board.png and /dev/null differ diff --git a/docs-en/quick-start/figures/adding-a-windows-host-ip-address.png b/docs-en/quick-start/figures/adding-a-windows-host-ip-address.png deleted file mode 100755 index 55cbac151897a29072411148afb76fae70c84d41..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/adding-a-windows-host-ip-address.png and /dev/null differ diff --git a/docs-en/quick-start/figures/appearance-of-the-hi3861-mother-board.png b/docs-en/quick-start/figures/appearance-of-the-hi3861-mother-board.png index eecdc07022bb5c66e9cf5e2f24b10221a3bb3cbb..ca4ecac7d44591aa5db2e7a1f899ef8241bb2e59 100755 Binary files a/docs-en/quick-start/figures/appearance-of-the-hi3861-mother-board.png and b/docs-en/quick-start/figures/appearance-of-the-hi3861-mother-board.png differ diff --git a/docs-en/quick-start/figures/appearance-of-the-hi3861-wi-fi-module.png b/docs-en/quick-start/figures/appearance-of-the-hi3861-wi-fi-module.png deleted file mode 100755 index 508041f2c1f9795def76a85f56c6209677fa0d03..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/appearance-of-the-hi3861-wi-fi-module.png and /dev/null differ diff --git a/docs-en/quick-start/figures/baud-rate-and-data-bit-configuration.png b/docs-en/quick-start/figures/baud-rate-and-data-bit-configuration.png deleted file mode 100755 index 78360310eb26b4a45182f1ccfb8c8b0eb0a8d502..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/baud-rate-and-data-bit-configuration.png and /dev/null differ diff --git a/docs-en/quick-start/figures/burning-and-starting.png b/docs-en/quick-start/figures/burning-and-starting.png deleted file mode 100755 index a157a483dc54a5f8e15f0c4ecfacaaf2c8ce546f..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/burning-and-starting.png and /dev/null differ diff --git a/docs-en/quick-start/figures/burning-basic-configurations.png b/docs-en/quick-start/figures/burning-basic-configurations.png deleted file mode 100755 index c79fc8a2fada743ccf752eff09c0ad48428dd9e1..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/burning-basic-configurations.png and /dev/null differ diff --git a/docs-en/quick-start/figures/checking-whether-the-serial-port-is-used-by-a-terminal.png b/docs-en/quick-start/figures/checking-whether-the-serial-port-is-used-by-a-terminal.png deleted file mode 100755 index cfa0ceb21f5a11d459b93721f512309c9d6da2ac..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/checking-whether-the-serial-port-is-used-by-a-terminal.png and /dev/null differ diff --git a/docs-en/quick-start/figures/com-port-of-the-device-manager-0.png b/docs-en/quick-start/figures/com-port-of-the-device-manager-0.png deleted file mode 100755 index dfc4a7bd9438664d3fe1cade7738ba72e68c586d..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/com-port-of-the-device-manager-0.png and /dev/null differ diff --git a/docs-en/quick-start/figures/configuring-the-serial-port.png b/docs-en/quick-start/figures/configuring-the-serial-port.png deleted file mode 100755 index a934f82bf50dd36c605fed836761d33cf72b87b6..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/configuring-the-serial-port.png and /dev/null differ diff --git a/docs-en/quick-start/figures/disabling-the-terminal-running-the-serial-port.png b/docs-en/quick-start/figures/disabling-the-terminal-running-the-serial-port.png deleted file mode 100755 index 0c1f60638087d0fe56127f2f842244355afad85f..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/disabling-the-terminal-running-the-serial-port.png and /dev/null differ diff --git a/docs-en/quick-start/figures/en-us_image_0000001053302606.png b/docs-en/quick-start/figures/en-us_image_0000001053302606.png deleted file mode 100755 index 88ef6c83597c6c5845b6b4ff661d3fc77dc159e8..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/en-us_image_0000001053302606.png and /dev/null differ diff --git a/docs-en/quick-start/figures/en-us_image_0000001053782590.png b/docs-en/quick-start/figures/en-us_image_0000001053782590.png deleted file mode 100755 index d7837de870094a6a1651936ac203d088d3931f45..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/en-us_image_0000001053782590.png and /dev/null differ diff --git a/docs-en/quick-start/figures/en-us_image_0000001054087868.png b/docs-en/quick-start/figures/en-us_image_0000001054087868.png deleted file mode 100755 index 4b697b65879025fafe5b6133df971a11773cbf14..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/en-us_image_0000001054087868.png and /dev/null differ diff --git a/docs-en/quick-start/figures/en-us_image_0000001054099459.png b/docs-en/quick-start/figures/en-us_image_0000001054099459.png deleted file mode 100755 index 8f3708a8a658a3c9edcd0131115ded0c3a4a4840..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/en-us_image_0000001054099459.png and /dev/null differ diff --git a/docs-en/quick-start/figures/en-us_image_0000001054218563.png b/docs-en/quick-start/figures/en-us_image_0000001054218563.png deleted file mode 100755 index 80942191e87d75cea1a315b0fec16d45dce95c0e..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/en-us_image_0000001054218563.png and /dev/null differ diff --git a/docs-en/quick-start/figures/en-us_image_0000001055674130.png b/docs-en/quick-start/figures/en-us_image_0000001055012855.png old mode 100755 new mode 100644 similarity index 100% rename from docs-en/quick-start/figures/en-us_image_0000001055674130.png rename to docs-en/quick-start/figures/en-us_image_0000001055012855.png diff --git a/docs-en/quick-start/figures/en-us_image_0000001055148043.png b/docs-en/quick-start/figures/en-us_image_0000001055148043.png new file mode 100644 index 0000000000000000000000000000000000000000..d8f0f95d6e464a7af8e81ef8466fedd7f760a75d Binary files /dev/null and b/docs-en/quick-start/figures/en-us_image_0000001055148043.png differ diff --git a/docs-en/quick-start/figures/en-us_image_0000001055073889.png b/docs-en/quick-start/figures/en-us_image_0000001055172843.png old mode 100755 new mode 100644 similarity index 100% rename from docs-en/quick-start/figures/en-us_image_0000001055073889.png rename to docs-en/quick-start/figures/en-us_image_0000001055172843.png diff --git a/docs-en/quick-start/figures/en-us_image_0000001055268090.png b/docs-en/quick-start/figures/en-us_image_0000001055268090.png new file mode 100644 index 0000000000000000000000000000000000000000..c6538531a9a0e9bd1954599e7a3e76c598b79796 Binary files /dev/null and b/docs-en/quick-start/figures/en-us_image_0000001055268090.png differ diff --git a/docs-en/quick-start/figures/en-us_image_0000001054713955.png b/docs-en/quick-start/figures/en-us_image_0000001055372855.png old mode 100755 new mode 100644 similarity index 100% rename from docs-en/quick-start/figures/en-us_image_0000001054713955.png rename to docs-en/quick-start/figures/en-us_image_0000001055372855.png diff --git a/docs-en/quick-start/figures/en-us_image_0000001055427138.png b/docs-en/quick-start/figures/en-us_image_0000001055427138.png new file mode 100644 index 0000000000000000000000000000000000000000..8c1b5d1af9f49d9d8a00602e0a658fcb6123cd71 Binary files /dev/null and b/docs-en/quick-start/figures/en-us_image_0000001055427138.png differ diff --git a/docs-en/quick-start/figures/en-us_image_0000001055427946.png b/docs-en/quick-start/figures/en-us_image_0000001055427946.png new file mode 100644 index 0000000000000000000000000000000000000000..4b62783425473ffb74dbadca6e77abf69c6eff9e Binary files /dev/null and b/docs-en/quick-start/figures/en-us_image_0000001055427946.png differ diff --git a/docs-en/quick-start/figures/successful-networking-of-the-wi-fi-module.png b/docs-en/quick-start/figures/en-us_image_0000001055428072.png old mode 100755 new mode 100644 similarity index 100% rename from docs-en/quick-start/figures/successful-networking-of-the-wi-fi-module.png rename to docs-en/quick-start/figures/en-us_image_0000001055428072.png diff --git a/docs-en/quick-start/figures/failed-to-obtain-the-file-due-to-unavailable-connection.png b/docs-en/quick-start/figures/failed-to-obtain-the-file-due-to-unavailable-connection.png deleted file mode 100755 index 548e03da4b76123cb67d41cbd1de4a0f33f5ef4b..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/failed-to-obtain-the-file-due-to-unavailable-connection.png and /dev/null differ diff --git a/docs-en/quick-start/figures/functional-block-diagram-of-the-hi3861.png b/docs-en/quick-start/figures/functional-block-diagram-of-the-hi3861.png deleted file mode 100755 index b66822e6b6c924e01f8136227c408fc4fc54271d..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/functional-block-diagram-of-the-hi3861.png and /dev/null differ diff --git a/docs-en/quick-start/figures/hardware-connections-2.png b/docs-en/quick-start/figures/hardware-connections-2.png deleted file mode 100755 index b5dfc506b818515555959aa29a297185d816ff0b..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/hardware-connections-2.png and /dev/null differ diff --git a/docs-en/quick-start/figures/ide2.png b/docs-en/quick-start/figures/ide2.png deleted file mode 100755 index eb7e6970c46416e8d4d5706ce1c10de7dc19fde4..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/ide2.png and /dev/null differ diff --git a/docs-en/quick-start/figures/ipop-configuration.png b/docs-en/quick-start/figures/ipop-configuration.png deleted file mode 100755 index 8ea8020b78e06e23b07526853ff91af830dcbfc6..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/ipop-configuration.png and /dev/null differ diff --git a/docs-en/quick-start/figures/login-page.png b/docs-en/quick-start/figures/login-page.png deleted file mode 100755 index 74c853ec188b0f36b2122d07abe74f3b546c0d1d..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/login-page.png and /dev/null differ diff --git a/docs-en/quick-start/figures/opening-the-deveco-serial-port.png b/docs-en/quick-start/figures/opening-the-deveco-serial-port.png deleted file mode 100755 index 77ee650774eccadccd6e23dd7d950f5d5cdbfcbe..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/opening-the-deveco-serial-port.png and /dev/null differ diff --git a/docs-en/quick-start/figures/procedures-for-burning-images-using-hitool.png b/docs-en/quick-start/figures/procedures-for-burning-images-using-hitool.png deleted file mode 100755 index 203b6dd2b0e2c55281ea7e684bef73c91962003d..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/procedures-for-burning-images-using-hitool.png and /dev/null differ diff --git a/docs-en/quick-start/figures/procedures-for-burning-the-u-boot-using-hitool.png b/docs-en/quick-start/figures/procedures-for-burning-the-u-boot-using-hitool.png deleted file mode 100755 index e9feaf3a9d19cc02770caaa57c30a6655c193451..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/procedures-for-burning-the-u-boot-using-hitool.png and /dev/null differ diff --git a/docs-en/quick-start/figures/procedures-for-changing-the-serial-port-number.png b/docs-en/quick-start/figures/procedures-for-changing-the-serial-port-number.png deleted file mode 100755 index f699204d10d39eb088c0ecc7aa08ba134ec4b6b6..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/procedures-for-changing-the-serial-port-number.png and /dev/null differ diff --git a/docs-en/quick-start/figures/procedures-for-configuring-the-serial-port-of-hitool.png b/docs-en/quick-start/figures/procedures-for-configuring-the-serial-port-of-hitool.png deleted file mode 100755 index e2c35bd19b634100eb3ce6120d45cf795c5db5ab..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/procedures-for-configuring-the-serial-port-of-hitool.png and /dev/null differ diff --git a/docs-en/quick-start/figures/putty-configuration-page.png b/docs-en/quick-start/figures/putty-configuration-page.png deleted file mode 100755 index 2200a705d03c1c910fe14a2f0b39407b95c830e1..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/putty-configuration-page.png and /dev/null differ diff --git a/docs-en/quick-start/figures/putty-security-alert-dialog-box.png b/docs-en/quick-start/figures/putty-security-alert-dialog-box.png deleted file mode 100755 index 7d9f4d6416337cd875b9a298bafc5f31fb39487f..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/putty-security-alert-dialog-box.png and /dev/null differ diff --git a/docs-en/quick-start/figures/putty-security-alert-dialog.png b/docs-en/quick-start/figures/putty-security-alert-dialog.png deleted file mode 100755 index 283a1d1afc63077a302df0288ae686ae2a54be87..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/putty-security-alert-dialog.png and /dev/null differ diff --git a/docs-en/quick-start/figures/rear-view-of-the-hi3516-board.png b/docs-en/quick-start/figures/rear-view-of-the-hi3516-board.png deleted file mode 100755 index 2e2dc3b4ae6c08c044068545b23062955d8272f8..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/rear-view-of-the-hi3516-board.png and /dev/null differ diff --git a/docs-en/quick-start/figures/resetting-the-wi-fi-module.png b/docs-en/quick-start/figures/resetting-the-wi-fi-module.png deleted file mode 100755 index fef01ce7d01e0ffba31af83333f4f57966fcf358..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/resetting-the-wi-fi-module.png and /dev/null differ diff --git a/docs-en/quick-start/figures/resetting-the-wlan-module.png b/docs-en/quick-start/figures/resetting-the-wlan-module.png deleted file mode 100755 index 164623da06187f49983420a660dadae683273705..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/resetting-the-wlan-module.png and /dev/null differ diff --git a/docs-en/quick-start/figures/restarting-a-burning-task.png b/docs-en/quick-start/figures/restarting-a-burning-task.png deleted file mode 100755 index f2f78d789e8e4d19b7db435d8a013187847deea0..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/restarting-a-burning-task.png and /dev/null differ diff --git a/docs-en/quick-start/figures/searching-for-the-visual-studio-code-application.png b/docs-en/quick-start/figures/searching-for-the-visual-studio-code-application.png deleted file mode 100755 index c735ae362e184083329cdf710289a169ad5625d4..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/searching-for-the-visual-studio-code-application.png and /dev/null differ diff --git a/docs-en/quick-start/figures/selecting-the-serial-port-to-be-connected-to-the-board.png b/docs-en/quick-start/figures/selecting-the-serial-port-to-be-connected-to-the-board.png deleted file mode 100755 index bf0c01278e517224774c228f27e5a8e2a2e58dbc..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/selecting-the-serial-port-to-be-connected-to-the-board.png and /dev/null differ diff --git a/docs-en/quick-start/figures/serial-port-display-after-burning-the-u-boot.png b/docs-en/quick-start/figures/serial-port-display-after-burning-the-u-boot.png deleted file mode 100755 index 4e2a2794e63f64341e448313968b6f82d237543d..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/serial-port-display-after-burning-the-u-boot.png and /dev/null differ diff --git a/docs-en/quick-start/figures/startup-parameter-setting.png b/docs-en/quick-start/figures/startup-parameter-setting.png deleted file mode 100755 index 59b3944993c2b6e120b60b44ac58cf2690d14b5c..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/startup-parameter-setting.png and /dev/null differ diff --git a/docs-en/quick-start/figures/successful-burning-2.png b/docs-en/quick-start/figures/successful-burning-2.png deleted file mode 100755 index 082716b665dc1c0d96b0fc56964d735bcff5767c..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/successful-burning-2.png and /dev/null differ diff --git a/docs-en/quick-start/figures/successful-burning-result.png b/docs-en/quick-start/figures/successful-burning-result.png deleted file mode 100755 index 082716b665dc1c0d96b0fc56964d735bcff5767c..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/successful-burning-result.png and /dev/null differ diff --git a/docs-en/quick-start/figures/successful-compilation.png b/docs-en/quick-start/figures/successful-compilation.png deleted file mode 100755 index 1ab55a38e6cf63f8dab4d2dc4a1798df103c5853..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/successful-compilation.png and /dev/null differ diff --git a/docs-en/quick-start/figures/successful-login-page.png b/docs-en/quick-start/figures/successful-login-page.png deleted file mode 100755 index 0690d0e4cc75cd69b45206ba1e11e9827c5d976f..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/successful-login-page.png and /dev/null differ diff --git a/docs-en/quick-start/figures/successful-networking-of-the-wlan-module.png b/docs-en/quick-start/figures/successful-networking-of-the-wlan-module.png deleted file mode 100755 index fb08d131d721f7f9850ee4a53b379554fc3181c0..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/successful-networking-of-the-wlan-module.png and /dev/null differ diff --git a/docs-en/quick-start/figures/successful-startup-and-application-execution.png b/docs-en/quick-start/figures/successful-startup-and-application-execution.png deleted file mode 100755 index 37610cceb8665b58e7d312801c29771e623553b2..0000000000000000000000000000000000000000 Binary files a/docs-en/quick-start/figures/successful-startup-and-application-execution.png and /dev/null differ diff --git a/docs-en/quick-start/hi3516-board-environment-setup.md b/docs-en/quick-start/hi3516-board-environment-setup.md deleted file mode 100755 index c4e49ddf574e935b104ea480eecbd2580915a56e..0000000000000000000000000000000000000000 --- a/docs-en/quick-start/hi3516-board-environment-setup.md +++ /dev/null @@ -1,284 +0,0 @@ -# Hi3516 Board Environment Setup - -## Environment Requirements - -## Hardware - -- Linux server -- Windows workstation \(Host computer\) -- Hi3516DV300 IoT Camera development board -- USB cable and network cable \(The Windows workbench is connected to Hi3516DV300 development board through the USB cable and network cable.\) - -[Figure 1](#fa54d47112b684c65b3dbf1779413545a) shows the hardware connections. - -**Figure 1** Hardware connections -![](figures/hardware-connections-1.png "hardware-connections-1") - -## Software - -**Table 1** Platforms required for Hi3516DV300 development board - - - - - - - - - - - - - - - - - - - - - -

Hardware

-

Software

-

Description

-

Remarks

-

Linux compile server

-

Operating system

-

Ubuntu 16.04 64-bit or later is used for the Linux OS, and Bash is used for the shell.

-
NOTE:

Common software such as samba and vim is installed by default in the system. Adaptation is required to support Linux file sharing.

-
-

You can develop programs on the Windows workstation or on the Linux server through remote login.

-

Windows workstation

-

Operating system

-

Windows XP/Windows7/Windows10

-

USB-serial adapter driver

-

http://www.totalcardiagnostics.com/files/PL2303_64bit_Installer.exe

-
- -## Build Tools for Linux - -The following table lists the obtaining channels and common tools required for Linux. - -**Table 2** Development tools and obtaining channels for the Linux server - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Development tool

-

Description

-

Obtaining Channel

-

Python3.7+

-

Runs the compilation scripts.

-

Public internet

-

bash

-

Executes commands.

-

Public internet

-

gn

-

Generates ninja compilation scripts.

-

http://tools.harmonyos.com/mirrors/gn/1523/linux/gn-linux-1523.tar

-

ninja

-

Runs ninja compilation scripts.

-

http://tools.harmonyos.com/mirrors/ninja/1.9.0/linux/ninja-linux-1.9.0.tar

-

LLVM

-

Performs code editing.

-

http://tools.harmonyos.com/mirrors/clang/9.0.0-33042/linux/llvm-linux-9.0.0-33042.tar

-

IPOP, PuTTY, or HyperTerminal

-

Connects to the Linux server(choose one).

-

Public internet (for example, https://www.putty.org/)

-
- -## Development Tools for Windows - -**Table 3** Development tools and obtaining channels for the Windows workstation - - - - - - - - - - - - - - - - -

Development tool

-

Description

-

Obtaining Channel

-

Visual Studio Code

-

Performs code editing.

-

https://code.visualstudio.com/

-

HUAWEI DevEco Device Tool

-

Suppports code compilation, burning, debugging, and C++ languages.

-
NOTE:

HUAWEI DevEco Device Tool is a one-stop integrated development tool provided by OpenHarmony for smart device developers. It supports customization of OpenHarmony components based on demands, and functions such as code editing, compilation, burning, and debugging. It also supports C and C++ languages, and is deployed on Visual Studio Code as a plug-in.

-
-

https://partnerdevicetest.devccsrnd.hwcloudtest.cn:3092/cn/ide

-
- -## Installing the Basic Compilation Environment - -## connecting to the Linux server - -Using PuTTY on Windows to log into Linux server - -1. Open [puTTY](https://www.putty.org/), enter the IP address of the Linux server, and click **Open**. - - **Figure 2** PuTTY configuration page - ![](figures/putty-configuration-page.png "putty-configuration-page") - -2. Select **Yes** in the dialog displaying a security alert. - - **Figure 3** PuTTY Security Alert dialog - ![](figures/putty-security-alert-dialog.png "putty-security-alert-dialog") - -3. Enter the account and password. - - **Figure 4** Login page - ![](figures/login-page.png "login-page") - -4. The login is successful. - - **Figure 5** Successful login page - ![](figures/successful-login-page.png "successful-login-page") - - -## h2 Change Linux shell to bash. - -Run the following command on the terminal to check whether the shell is bash: - -``` -ls -l /bin/sh -``` - -如果为显示为/bin/sh -\> bash则为正常,否则请按以下方式修改: - -**方法一** - -在终端运行如下命令,然后选择 no。 - -``` -sudo dpkg-reconfigure dash -``` - -**方法二** - -Delete sh and run sh again. - -``` -rm -rf /bin/sh -sudo ln -s /bin/bash /bin/sh -``` - -## Installing the Python environment - -1. Open the terminal of the Linux compilation server. -2. Run the **python3 --version** command to check the Python version. Python 3.7 or later is required. Otherwise, perform the following steps \(Python 3.8 is used as an example\): - - If the Ubuntu version is 18, run the sudo apt-get install python3.8 command. - - If the Ubuntu version is 16, download the installation package and install Python. - - 1. Run the following command to install the python dependency: - - Run sudo apt-get install gcc to install gcc. - - Install g++ and run sudo apt-get install g++. - - Run the sudo apt-get install make command to install make. - - Run the sudo apt-get install zlib\* command to install zlib. - - Install libffi and run "sudo apt-get install libffi-dev". - - 2. Obtaining the python3.8.5 Installation Package - - 3. Run the **tar -xvzf Python-3.8.5.tgz** command. - - 4. Run the **cd Python-3.8.5** command. - - 5. Run the **sudo ./configure** command. - - 6. Run the **make** command. - - 7. Run the **sudo make install** command. - - - - Run the sudo apt-get install python3-setuptools python3-pip -y command to install the Python package management tool. \(The root/sudo permission is required for the installation.\) Upgrade pip3 and run "sudo pip3 install --upgrade pip". - - Alternatively, install the Python package management tool as instructed by the official website. Download "curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py" and run python get-pip.py. - - -3. Install **setuptools** by running the **pip3 install setuptools** command. -4. Install GUI menuconfig by running the **sudo pip3 install kconfiglib** command. The Kconfiglib 13.2.0+ version is recommended. \(The root/sudo permission is required.\) - - You can also perform the following steps to install Kconfiglib 13.2.0+: - - 1. Download the .whl file \(for example, kconfiglib-13.2.0-py2.py3-none-any.whl\). - - Download path: https://pypi.org/project/kconfiglib\#files - - - 1. Install the .whl file by running the **sudo pip3 install kconfiglib-xxx.whl** command \(for example, **sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl**\). \(The root/sudo permission is required.\) - 2. If an error of **subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1.** occurs, run the **find / -name lsb\_release** command, for example, **rm -rf /usr/bin/lsb\_release** to locate **sb\_release** and release it. - - -## Installing **gn** - -1. Open a Linux terminal. -2. Download [gn](https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/latest). -3. Decompress the tool package to the **\~/gn** directory on the Linux server. -4. Set environment variables by running the **vim /etc/profile** command and add new variables by running the **export PATH=\~/gn:$PATH** command. -5. Validate environment variables by running the **source /etc/profile** command. - -## Installing **ninjah** - -1. Open a Linux terminal. -2. Download [ninja](https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-linux.zip). -3. Decompress the tool package to the **\~/ninja** directory on the Linux server. -4. Set environment variables by running the **vim /etc/profile** command and add new variables by running the **PATH=\~/ninja:$PATH** command. -5. Validate environment variables by running the **source /etc/profile** command. - -## Installing the LLVM tool chain - -1. Open a Linux terminal. -2. Download [http://tools.harmonyos.com/mirrors/clang/9.0.0-33042/linux/llvm-linux-9.0.0-33042.tar](http://tools.harmonyos.com/mirrors/clang/9.0.0-33042/linux/llvm-linux-9.0.0-33042.tar). -3. Decompress the tool package to the **\~/llvm** directory on the Linux server. -4. Set environment variables by adding **export PATH=\~/bin:$PATH** to the **vim /etc/profile** profile. -5. Validate environment variables by running the **source /etc/profile** command. - -## Installing File Packing Tools - -1. Open a Linux terminal. -2. Run the **mkfs.vfat** command. If the command is not found, run the **sudo apt-get install dosfstools** command. -3. Run the **mcopy** command. If the command is not found, run the **sudo apt-get install mtools** command. - diff --git a/docs-en/quick-start/hi3518-board-environment-setup.md b/docs-en/quick-start/hi3518-board-environment-setup.md deleted file mode 100755 index 70d952f107da08259433f69101252258758187e1..0000000000000000000000000000000000000000 --- a/docs-en/quick-start/hi3518-board-environment-setup.md +++ /dev/null @@ -1,278 +0,0 @@ -# Hi3518 Board Environment Setup - -## Environment Requirements - -## Hardware - -- Linux server -- Windows workstation \(Host computer\) -- Hi3518EV300 IoT Camera development board -- USB cable and network cable \(The Windows workstation is connected to Hi3518EV300 development board through the USB cable and network cable.\) - - Figure 1 shows the hardware connections. - - -**Figure 1** Hardware connections -![](figures/hardware-connections-2.png "hardware-connections-2") - -## Software - -**Table 1** Platforms required for the Hi3518EV300 development board - - - - - - - - - - - - - - - - - - -

Hardware

-

Software

-

Description

-

Remarks

-

Linux compile server

-

Operating system

-

Ubuntu 16.04 64-bit or later is used for the Linux OS, and bash is used as the shell.

-
NOTE:

Common software such as samba and vim is installed by default in the system. Adaptation is required to support Linux file sharing.

-
-

You can develop programs on the Windows workstation or on the Linux server through remote login.

-

Windows workstation

-

Operating system

-

Windows XP/Windows7/Windows10

-
- -## Build Tools for Linux - -The following table lists the obtaining channels and common tools required for Linux. - -**Table 2** Development tools and obtaining channels for the Linux server - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Development tool

-

Description

-

Obtaining Channel

-

Python3.7+

-

Runs the compilation scripts.

-

Public internet

-

bash

-

Executes commands.

-

Public internet

-

gn

-

Generates ninja compilation scripts.

-

http://tools.harmonyos.com/mirrors/gn/1523/linux/gn-linux-1523.tar

-

ninja

-

Runs ninja compilation scripts.

-

http://tools.harmonyos.com/mirrors/ninja/1.9.0/linux/ninja-linux-1.9.0.tar

-

LLVM

-

Performs code editing.

-

http://tools.harmonyos.com/mirrors/clang/9.0.0-33042/linux/llvm-linux-9.0.0-33042.tar

-

IPOP, PuTTY, or HyperTerminal

-

Connects to the Linux server(choose one).

-

Public internet (for example, https://www.putty.org/)

-
- -## Development Tools for Windows - -**Table 3** Development tools and obtaining channels for the Windows workbench - - - - - - - - - - - - - - - - -

Development tool

-

Description

-

Obtaining Channel

-

Visual Studio Code

-

Performs code editing.

-

https://code.visualstudio.com/

-

HiTool

-

Burns the images and the U-Boot.

-

http://www.hihope.org/download

-
- -## Installing the Basic Compilation Environment - -## connecting to the Linux server - -Using PuTTY on Windows to log into Linux server - -1. Open [puTTY](https://www.putty.org/), enter the IP address of the Linux server, and click **Open**. - - **Figure 2** PuTTY configuration page - ![](figures/putty-configuration-page.png "putty-configuration-page") - -2. Select **Yes** in the dialog displaying a security alert. - - **Figure 3** PuTTY Security Alert dialog - ![](figures/putty-security-alert-dialog.png "putty-security-alert-dialog") - -3. Enter the account and password. - - **Figure 4** Login page - ![](figures/login-page.png "login-page") - -4. The login is successful. - - **Figure 5** Successful login page - ![](figures/successful-login-page.png "successful-login-page") - - -## h2 Change Linux shell to bash. - -Run the following command on the terminal to check whether the shell is bash: - -``` -ls -l /bin/sh -``` - -如果为显示为/bin/sh -\> bash则为正常,否则请按以下方式修改: - -**方法一** - -在终端运行如下命令,然后选择 no。 - -``` -sudo dpkg-reconfigure dash -``` - -**方法二** - -Delete sh and run sh again. - -``` -rm -rf /bin/sh -sudo ln -s /bin/bash /bin/sh -``` - -## Installing the Python environment - -1. Open the terminal of the Linux compilation server. -2. Run the **python3 --version** command to check the Python version. Python 3.7 or later is required. Otherwise, perform the following steps \(Python 3.8 is used as an example\): - - If the Ubuntu version is 18, run the sudo apt-get install python3.8 command. - - If the Ubuntu version is 16, download the installation package and install Python. - - 1. Run the following command to install the python dependency: - - Run sudo apt-get install gcc to install gcc. - - Install g++ and run sudo apt-get install g++. - - Run the sudo apt-get install make command to install make. - - Run the sudo apt-get install zlib\* command to install zlib. - - Install libffi and run "sudo apt-get install libffi-dev". - - 2. Obtaining the python3.8.5 Installation Package - - 3. Run the **tar -xvzf Python-3.8.5.tgz** command. - - 4. Run the **cd Python-3.8.5** command. - - 5. Run the **sudo ./configure** command. - - 6. Run the **make** command. - - 7. Run the **sudo make install** command. - - - - Run the sudo apt-get install python3-setuptools python3-pip -y command to install the Python package management tool. \(The root/sudo permission is required for the installation.\) Upgrade pip3 and run "sudo pip3 install --upgrade pip". - - Alternatively, install the Python package management tool as instructed by the official website. Download "curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py" and run python get-pip.py. - - -3. Install **setuptools** by running the **pip3 install setuptools** command. -4. Install GUI menuconfig by running the **sudo pip3 install kconfiglib** command. The Kconfiglib 13.2.0+ version is recommended. \(The root/sudo permission is required.\) - - You can also perform the following steps to install Kconfiglib 13.2.0+: - - 1. Download the .whl file \(for example, kconfiglib-13.2.0-py2.py3-none-any.whl\). - - Download path: https://pypi.org/project/kconfiglib\#files - - - 1. Install the .whl file by running the **sudo pip3 install kconfiglib-xxx.whl** command \(for example, **sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl**\). \(The root/sudo permission is required.\) - 2. If an error of **subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1.** occurs, run the **find / -name lsb\_release** command, for example, **rm -rf /usr/bin/lsb\_release** to locate **sb\_release** and release it. - - -## Installing **gn** - -1. Open a Linux terminal. -2. Download [gn](https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/latest). -3. Decompress the tool package to the **\~/gn** directory on the Linux server. -4. Set environment variables by running the **vim /etc/profile** command and add new variables by running the **export PATH=\~/gn:$PATH** command. -5. Validate environment variables by running the **source /etc/profile** command. - -## Installing **ninjah** - -1. Open a Linux terminal. -2. Download [ninja](https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-linux.zip). -3. Decompress the tool package to the **\~/ninja** directory on the Linux server. -4. Set environment variables by running the **vim /etc/profile** command and add new variables by running the **PATH=\~/ninja:$PATH** command. -5. Validate environment variables by running the **source /etc/profile** command. - -## Installing the LLVM tool chain - -1. Open a Linux terminal. -2. Download [http://tools.harmonyos.com/mirrors/clang/9.0.0-33042/linux/llvm-linux-9.0.0-33042.tar](http://tools.harmonyos.com/mirrors/clang/9.0.0-33042/linux/llvm-linux-9.0.0-33042.tar). -3. Decompress the tool package to the **\~/llvm** directory on the Linux server. -4. Set environment variables by adding **export PATH=\~/bin:$PATH** to the **vim /etc/profile** profile. -5. Validate environment variables by running the **source /etc/profile** command. - -## Installing File Packing Tools - -1. Open a Linux terminal. -2. Run the **mkfs.vfat** command. If the command is not found, run the **sudo apt-get install dosfstools** command. -3. Run the **mcopy** command. If the command is not found, run the **sudo apt-get install mtools** command. - diff --git a/docs-en/quick-start/hi3861-board-environment-setup.md b/docs-en/quick-start/hi3861-board-environment-setup.md deleted file mode 100755 index c69ec5dad63315bb19b1a431f6d6235e669e5bf5..0000000000000000000000000000000000000000 --- a/docs-en/quick-start/hi3861-board-environment-setup.md +++ /dev/null @@ -1,358 +0,0 @@ -# Hi3861 Board Environment Setup - -## Environment Requirements - -## Hardware - -- Linux server -- Windows workstation \(Host computer\) -- Hi3861 WLAN module -- USB Type-C cable \(This cable connects Windows workstation and Hi3861 Wi-Fi module.\) - -[Figure 1](#fa54d47112b684c65b3dbf1779413545a) shows the hardware connections. - -**Figure 1** Hardware connections -![](figures/hardware-connections.png "hardware-connections") - -## Software - -**Table 1** Requirements on the development platform - - - - - - - - - - - - - - - - - - -

Hardware

-

Software

-

Description

-

Remarks

-

Linux compile server

-

Operating system

-

Ubuntu 16.04 64-bit server or later (Use bash as the shell.)

-

You can develop programs on the Windows workstation or on the Linux server via remote login.

-

Windows workstation

-

Operating system

-

Windows XP/Windows7/Windows10

-
- -## Build Tools for Linux - -[Table 2](en-us_topic_0000001053782584.md#table6299192712513) lists the obtaining channels and common tools required for Linux. - -**Table 2** Development tools and obtaining channels - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Development Tool

-

Description

-

Obtaining Channel

-

gcc_riscv32

-

Cross compilation tool

-

http://tools.harmonyos.com/mirrors/gcc_riscv32/7.3.0/linux/gcc_riscv32-linux-7.3.0.tar.gz

-

Python3.7+

-

Executes script compilation.

-

Public internet

-

SCons3.0.4+

-

Compilation and build tool

-

Internet

-

bash

-

Command processor

-

Internet

-

build-essential

-

Packages on which compilation depends

-

Internet

-

gn

-

Generates ninja files.

-

http://tools.harmonyos.com/mirrors/gn/1523/linux/gn-linux-1523.tar

-

ninja

-

Runs ninja files.

-

http://tools.harmonyos.com/mirrors/ninja/1.9.0/linux/ninja-linux-1.9.0.tar

-
- -## Development Tools for Windows - -**Table 3** Development tools and obtaining channels - - - - - - - - - - - - - - - - - - - - - - - - -

Development tool

-

Description

-

Obtaining Channel

-

Visual Studio Code

-

A code editor

-

https://code.visualstudio.com/

-

HUAWEI DevEco Device Tool

-

The integrated development environment (IDE) development tool is used to compile and burn the code of the Wi-Fi module.

-
NOTE:

HUAWEI DevEco Device Tool is a one-stop integrated development environment for smart device developers. It supports on-demand component customization, code editing and compilation, and C/C++ languages. It is deployed on Visual Studio Code as a plug-in.

-
-

https://partnerdevicetest.devccsrnd.hwcloudtest.cn:3092/cn/ide

-

IPOP, PuTTY, or other HyperTerminal

-

A tool that remotely connects to the Linux compilation server and cmodule serial port

-

Internet (for example, https://www.putty.org/)

-

CH341SER.EXE

-

USB-serial adapter driver

-

http://www.wch.cn/search?q=ch340g&t=downloads

-
- -## Installing the basic compilation environment - -## connecting to the Linux server - -Using PuTTY on Windows to log into Linux server - -1. Open [puTTY](https://www.putty.org/), enter the IP address of the Linux server, and click **Open**. - - **Figure 2** PuTTY configuration page - ![](figures/putty-configuration-page.png "putty-configuration-page") - -2. Select **Yes** in the dialog displaying a security alert. - - **Figure 3** PuTTY Security Alert dialog - ![](figures/putty-security-alert-dialog.png "putty-security-alert-dialog") - -3. Enter the account and password. - - **Figure 4** Login page - ![](figures/login-page.png "login-page") - -4. The login is successful. - - **Figure 5** Successful login page - ![](figures/successful-login-page.png "successful-login-page") - - -## h2 Change Linux shell to bash. - -Run the following command on the terminal to check whether the shell is bash: - -``` -ls -l /bin/sh -``` - -如果为显示为/bin/sh -\> bash则为正常,否则请按以下方式修改: - -**方法一** - -在终端运行如下命令,然后选择 no。 - -``` -sudo dpkg-reconfigure dash -``` - -**方法二** - -Delete sh and run sh again. - -``` -rm -rf /bin/sh -sudo ln -s /bin/bash /bin/sh -``` - -## Installing the Python environment - -1. Open the terminal of the Linux compilation server. -2. Run the **python3 --version** command to check the Python version. Python 3.7 or later is required. Otherwise, perform the following steps \(Python 3.8 is used as an example\): - - If the Ubuntu version is 18, run the sudo apt-get install python3.8 command. - - If the Ubuntu version is 16, download the installation package and install Python. - - 1. Run the following command to install the python dependency: - - Run sudo apt-get install gcc to install gcc. - - Install g++ and run sudo apt-get install g++. - - Run the sudo apt-get install make command to install make. - - Run the sudo apt-get install zlib\* command to install zlib. - - Install libffi and run "sudo apt-get install libffi-dev". - - 2. Obtaining the python3.8.5 Installation Package - - 3. Run the **tar -xvzf Python-3.8.5.tgz** command. - - 4. Run the **cd Python-3.8.5** command. - - 5. Run the **sudo ./configure** command. - - 6. Run the **make** command. - - 7. Run the **sudo make install** command. - - - - Run the sudo apt-get install python3-setuptools python3-pip -y command to install the Python package management tool. \(The root/sudo permission is required for the installation.\) Upgrade pip3 and run "sudo pip3 install --upgrade pip". - - Alternatively, install the Python package management tool as instructed by the official website. Download "curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py" and run python get-pip.py. - - -3. Install **setuptools** by running the **pip3 install setuptools** command. -4. Install GUI menuconfig by running the **sudo pip3 install kconfiglib** command. The Kconfiglib 13.2.0+ version is recommended. \(The root/sudo permission is required.\) - - You can also perform the following steps to install Kconfiglib 13.2.0+: - - 1. Download the .whl file \(for example, kconfiglib-13.2.0-py2.py3-none-any.whl\). - - Download path: https://pypi.org/project/kconfiglib\#files - - - 1. Install the .whl file by running the **sudo pip3 install kconfiglib-xxx.whl** command \(for example, **sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl**\). \(The root/sudo permission is required.\) - 2. If an error of **subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1.** occurs, run the **find / -name lsb\_release** command, for example, **rm -rf /usr/bin/lsb\_release** to locate **sb\_release** and release it. - -5. Install pycryptodome by running the **sudo pip3 install pycryptodome** command or performing the following operations: - - >![](public_sys-resources/icon-note.gif) **NOTE:** - >- Install the Python component packages on which the signature of the upgrade file depends, including pycryptodome, six, and ecdsa. - >- The ecdsa installation depends on six. Please install six before installing ecdsa. - - 1. Download the .whl file \(for example, pycryptodome-3.7.3-cp37-cp37m-manylinux1\_x86\_64.whl\). - - Download path: [https://pypi.org/project/pycryptodome/\#files](https://pypi.org/project/pycryptodome/#files) - - - 1. Install the .whl file by running the **sudo pip3 install pycryptodome-xxx.whl** command \(for example, sudo pip3 install pycryptodome-3.7.3-cp37-cp37m-manylinux1\_x86\_64.whl\). - - The message **Successfully installed pycryptodome-3.7.3** is displayed after the installation. - -6. Install **six** by running the **sudo pip3 install six --upgrade --ignore-installed six** command or performing the following operations: - - 1. Download the .whl file, for example, six-1.12.0-py2.py3-none-any.whl. - - Download path: [https://pypi.org/project/six/\#files](https://pypi.org/project/six/#files) - - - 1. Install the .whl file by running the **sudo pip3 install six-xxx.whl** command, for example, **sudo pip3 install six-1.12.0-py2.py3-none-any.whl**. \(The root/sudo permission is required.\) - - After the installation, the message **Successfully installed six-1.12.0** is displayed. - -7. Install ecdsa by running the **sudo pip3 install ecdsa** command, or by doing as follows: - - 1. Download the .whl file, for example, ecdsa-0.14.1-py2.py3-none-any.whl. - - Download path: [https://pypi.org/project/ecdsa/\#files](https://pypi.org/project/ecdsa/#files) - - - 1. Install the .whl file by running the **sudo pip3 install ecdsa-xxx.whl** command, for example, **sudo pip3 install ecdsa-0.14.1-py2.py3-none-any.whl**. \(The root/sudo permission is required for the installation.\) - - After the installation, the message **Successfully installed ecdsa-0.14.1** is displayed. - - -## Installing Scons - -1. Open the Linux terminal. -2. Install the SCons package by running the **sudo apt-get install scons -y** command. \(The root/sudo permission is required for the installation.\) - - Run the **sudo apt-get install scons -y** command. If the installation package cannot be found in the software source, do as follows: - - 1. Download the source code package from [https://scons.org/pages/download.html](https://scons.org/pages/download.html). The recommended SCons version is 3.0.4+. - 2. Decompress the source code package to any directory. - 3. Enter the source code directory and run the **sudo python3 setup.py install** command to install the source code package. \(The root/sudo permission is required for the installation.\) - -3. Run the **scons -v** command to check whether the installation is successful. - - **Figure 6** Successful installation - ![](figures/successful-installation.png "successful-installation") - - -## Installing **gn** - -1. Open a Linux terminal. -2. Download [gn](https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/latest). -3. Decompress the tool package to the **\~/gn** directory on the Linux server. -4. Set environment variables by running the **vim /etc/profile** command and add new variables by running the **export PATH=\~/gn:$PATH** command. -5. Validate environment variables by running the **source /etc/profile** command. - -## Installing **ninjah** - -1. Open a Linux terminal. -2. Download [ninja](https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-linux.zip). -3. Decompress the tool package to the **\~/ninja** directory on the Linux server. -4. Set environment variables by running the **vim /etc/profile** command and add new variables by running the **PATH=\~/ninja:$PATH** command. -5. Validate environment variables by running the **source /etc/profile** command. - -## Obtaining the Compilation Tool Chain of the WLAN Module - -## Installing **riscv gcc** tool chain - ->![](public_sys-resources/icon-notice.gif) **NOTICE:** ->The Hi3861 platform supports only the static link of the library when libgcc is running. You are not advised to use the dynamic link of the library when libgcc is running. Otherwise, GPL V3 will be polluted during commercial distribution. - -1. Open a Linux terminal. -2. Download the gcc\_riscv32 compilation tool chain. - - Use the Huawei image cloud to download the gcc\_riscv32 binary compilation tool chain and decompress it to the /opt/gcc\_riscv32 directory on Linux. - -3. Decompress the package to the /opt/gcc\_riscv32 directory. -4. Set the environment variable $vim /etc/profile and add $ export PATH=/opt/gcc\_riscv32/bin:$PATH. -5. Validate environment variables by running the **$source /etc/profile** command. -6. Check whether the installation is successful by entering **$ riscv32-unknown-elf-gcc -v** in the shell command line. - - If the compiler version number can be correctly displayed, the compiler is successfully installed. - - diff --git a/docs-en/quick-start/introduction-to-the-hi3516-development-board.md b/docs-en/quick-start/introduction-to-the-hi3516-development-board.md index 9b2879ee606214ed91782a244084788e92cf0c5b..002047c9f779f905a152de6c9e0d1ca25855cba0 100755 --- a/docs-en/quick-start/introduction-to-the-hi3516-development-board.md +++ b/docs-en/quick-start/introduction-to-the-hi3516-development-board.md @@ -2,7 +2,7 @@ ## Hi3516 -Hi3516DV300 is a next-generation SoC \(System on Chip\) designed for the industry-dedicated smart HD IP camera. It introduces a next-generation ISP, the latest H.265 video compression encoder, and a high-performance NNIE engine, leading the industry in terms of low bit rate, high image quality, intelligent processing and analysis, and low power consumption. +Hi3516DV300 is a next-generation system on chip \(SoC\) designed for the industry-dedicated smart HD IP camera. It introduces a next-generation image signal processor \(ISP\), the latest H.265 video compression encoder, and a high-performance NNIE engine, leading the industry in terms of low bit rate, high image quality, intelligent processing and analysis, and low power consumption. **Figure 1** Front view of the Hi3516 board ![](figures/front-view-of-the-hi3516-board.png "front-view-of-the-hi3516-board") diff --git a/docs-en/quick-start/introduction-to-the-hi3518-development-board.md b/docs-en/quick-start/introduction-to-the-hi3518-development-board.md index 57d5b995a47ff2ab154eaa2c59ba13549a305725..88eabfeda015606b4bdd99518c41c78aaa8158e3 100755 --- a/docs-en/quick-start/introduction-to-the-hi3518-development-board.md +++ b/docs-en/quick-start/introduction-to-the-hi3518-development-board.md @@ -2,7 +2,7 @@ ## Hi3518 -Hi3518EV300 is a next-generation SoC \(System on Chip\) designed for the industry-dedicated smart HD IP camera. It introduces a next-generation image signal processor ISP, the latest H.265 video compression encoder, and the advanced low-power process and architecture design, leading the industry in terms of low bit rate, high image quality, and low power consumption. +Hi3518EV300 is a next-generation system on chip \(SoC\) designed for the industry-dedicated smart HD IP camera. It introduces a next-generation image signal processor \(ISP\), the latest H.265 video compression encoder, and the advanced low-power process and architecture design, leading the industry in terms of low bit rate, high image quality, and low power consumption. **Figure 1** Front view of the Hi3518EV300 board ![](figures/front-view-of-the-hi3518ev300-board.png "front-view-of-the-hi3518ev300-board") diff --git a/docs-en/quick-start/introduction-to-the-hi3861-development-board.md b/docs-en/quick-start/introduction-to-the-hi3861-development-board.md index 8fb93d867301459da72cecec565d434d12411515..d7b4511674eff9805335bf43a3f74fe6f17dab4c 100755 --- a/docs-en/quick-start/introduction-to-the-hi3861-development-board.md +++ b/docs-en/quick-start/introduction-to-the-hi3861-development-board.md @@ -4,19 +4,19 @@ This document describes how to quickly start with the Hi3861 WLAN module, includ ## Hi3861 -The Hi3861 WLAN module is a development board with 2 x 5 cm form factor. It contains a 2.4 GHz WLAN SoC chip that highly integrates the IEEE 802.11b/g/n baseband and radio frequency \(RF\) circuit. This module provides open and easy-to-use development and debugging environments for running OpenHarmony. +The Hi3861 WLAN module is a development board with 2 x 5 cm form factor. It contains a 2.4 GHz WLAN SoC that highly integrates the IEEE 802.11b/g/n baseband and radio frequency \(RF\) circuit. This module provides open and easy-to-use development and debugging environments for running OpenHarmony. **Figure 1** Appearance of Hi3861 WLAN module ![](figures/appearance-of-hi3861-wlan-module.png "appearance-of-hi3861-wlan-module") The Hi3861 WLAN module can also be connected to the Hi3861 mother board to expand its peripheral capabilities. The following figure shows the Hi3861 mother board. -**Figure 2** Appearance of the Hi3861 mother board +**Figure 2** Appearance of the Hi3861 mother board ![](figures/appearance-of-the-hi3861-mother-board.png "appearance-of-the-hi3861-mother-board") - The RF circuit includes modules such as the power amplifier \(PA\), low noise amplifier \(LNA\), RF Balun, antenna switch, and power management. It supports a standard bandwidth of 20 MHz and a narrow bandwidth of 5 MHz or 10 MHz, and provides a maximum rate of 72.2 Mbit/s at the physical layer. -- The Hi3861 WLAN baseband supports the orthogonal frequency division multiplexing \(OFDM\) technology and is backward compatible with the direct sequence spread spectrum \(DSSS\) and complementary code keying \(CCK\) technologies. In addition, the Hi3861 WLAN baseband supports various data rates complying with the IEEE 802.11 b/g/n protocol. -- The Hi3861 chip integrates the high-performance 32-bit microprocessor, hardware security engine, and various peripheral interfaces. The peripheral interfaces include the Synchronous Peripheral Interface \(SPI\), Universal Asynchronous Receiver & Transmitter \(UART\), the Inter Integrated Circuit \(I2C\), Pulse Width Modulation \(PWM\), General Purpose Input/Output \(GPIO\) interface, and Analog to Digital Converter \(ADC\). The Hi3861 chip also supports the high-speed Secure Digital Input/Output \(SDIO\) 2.0 interface, with a maximum clock frequency of 50 MHz. This chip has a built-in static random access memory \(SRAM\) and flash memory, which can run programs independently or run from a flash drive. +- The Hi3861 WLAN baseband supports the orthogonal frequency division multiplexing \(OFDM\) technology and is backward compatible with the direct sequence spread spectrum \(DSSS\) and complementary code keying \(CCK\) technologies. In addition, the Hi3861 WLAN baseband supports various data rates specified in the IEEE 802.11 b/g/n protocol. +- The Hi3861 chip integrates the high-performance 32-bit microprocessor, hardware security engine, and various peripheral interfaces. The peripheral interfaces include the Synchronous Peripheral Interface \(SPI\), Universal Asynchronous Receiver & Transmitter \(UART\), the Inter Integrated Circuit \(I2C\), Pulse Width Modulation \(PWM\), General Purpose Input/Output \(GPIO\) interface, and Analog to Digital Converter \(ADC\). The Hi3861 chip also supports the high-speed Secure Digital Input/Output \(SDIO\) 2.0 interface, with a maximum clock frequency of 50 MHz. This chip has a built-in static random access memory \(SRAM\) and flash memory, so that programs can run independently or run from a flash drive. - The Hi3861 chip applies to Internet of Things \(IoT\) devices such as smart home appliances. **Figure 3** Hi3861 functions @@ -34,44 +34,44 @@ As the Hi3861 only offers 2 MB Flash and 352 KB RAM, use them efficiently when c **Table 1** Hi3861 WLAN module specifications -

Type

+ - - - - - - - - - - - - - diff --git a/docs-en/quick-start/setting-up-a-development-environment-1.md b/docs-en/quick-start/setting-up-a-development-environment-1.md index 1c51e40f99c7b67441dc81b2bc49f40c358ea3e3..51000f66b72370669318191d8b23bb4c71ff5916 100755 --- a/docs-en/quick-start/setting-up-a-development-environment-1.md +++ b/docs-en/quick-start/setting-up-a-development-environment-1.md @@ -125,24 +125,25 @@ The following table describes the common tools required for Linux and how to obt

Type

Description

+

Description

General specifications

+

General specifications

  • Operates over 1×1 2.4 GHz frequency band (ch1-ch14).
  • The physical layer (PHY) complies with the IEEE 802.11b/g/n protocol.
  • The media access control (MAC) layer complies with the IEEE802.11 d/e/h/i/k/v/w protocol.
-
  • Includes the built-in public address (PA) and local area network (LAN); integrates transmit-receive (Tx/Rx) switch and Balun.
  • Supports the station (STA) and access point (AP) modes. When the Hi3861 WLAN module functions as an AP, a maximum of six STAs are supported.
  • Supports WPA and WPA2 from WFA (personal), and WPS 2.0.
  • Supports three kinds of packet traffic arbiter (PTA) (2-, 3-, or 4-wire PTA), each of which coexists with the BT or BLE chip.
  • The input voltage ranges from 2.3 V to 3.6 V.
-
  • The input/output (I/O) power voltage can be 1.8 V or 3.3 V.
-
  • Supports self-calibration for RF hardware.
  • Performs with low power consumption:
    • Ultra deep sleep mode: 5 μA @ 3.3 V
    • DTIM1: 1.5 mA @ 3.3 V
    • DTIM3: 0.8 mA @ 3.3 V
    +
  • Operates over 1×1 2.4 GHz frequency band (ch1-ch14).
  • The physical layer (PHY) complies with the IEEE 802.11b/g/n protocol.
  • The media access control (MAC) layer complies with the IEEE802.11 d/e/h/i/k/v/w protocol.
+
  • Includes the built-in public address (PA) and local area network (LAN); integrates transmit-receive (Tx/Rx) switch and Balun.
  • Supports the station (STA) and access point (AP) modes. When the Hi3861 WLAN module functions as an AP, a maximum of six STAs are supported.
  • Supports WFA WPA, WFA WPA2 personal, and WPS2.0.
  • Supports three kinds of packet traffic arbiter (PTA) (2- , 3- , or 4-wire PTA), each of which coexists with the BT or BLE chip.
  • The input voltage ranges from 2.3 V to 3.6 V.
+
  • The input/output (I/O) power voltage can be 1.8 V or 3.3 V.
+
  • Supports self-calibration for RF hardware.
  • Performs with low power consumption:
    • Ultra deep sleep mode: 5 μA @ 3.3 V
    • DTIM1: 1.5 mA @ 3.3 V
    • DTIM3: 0.8 mA @ 3.3 V

PHY features

+

PHY features

  • Supports all data rates of the single antenna required by the IEEE802.11b/g/n protocol.
  • Supports a maximum rate of 72.2 Mbit/s@HT20 MCS7.
  • Supports the standard bandwidth (20 MHz) and narrow bandwidth (5 MHz or 10 MHz).
  • Supports space-time block coding (STBC).
  • Supports short guard interval (Short-GI).
+
  • Supports all data rates of the single antenna required by the IEEE802.11b/g/n protocol.
  • Supports a maximum rate of 72.2 Mbps@HT20 MCS7
  • Supports the standard bandwidth (20 MHz) and narrow bandwidth (5 MHz or 10 MHz).
  • Supports space-time block coding (STBC).
  • Supports short guard interval (Short-GI).

MAC features

+

MAC features

  • Supports aggregate MAC service data unit (A-MPDU) and aggregate MAC protocol data unit (A-MSDU).
  • Supports block acknowledgment (Blk-ACK).
  • Supports quality of service (QoS), meeting customer's service requirements.
+
  • Supports aggregate MAC service data unit (A-MPDU) and aggregate MAC protocol data unit (A-MSDU).
  • Supports block acknowledgment (Blk-ACK).
  • Supports quality of service (QoS), meeting customer's service requirements.

CPU subsystem

+

CPU subsystem

  • Integrates a high-performance 32-bit microprocessor with a maximum operating frequency of 160 MHz.
  • Includes built-in 352 KB SRAM and 288 KB ROM.
  • Includes a built-in 2 MB flash memory.
+
  • Integrates a high-performance 32-bit microprocessor with a maximum operating frequency of 160 MHz.
  • Includes built-in 352 KB SRAM and 288 KB ROM.
  • Includes a built-in 2 MB flash memory.

Peripheral interfaces

+

Peripheral interfaces

  • Include one SDIO interface, two SPI interfaces, two I2C interfaces, three UART interfaces, 15 GPIO interfaces, seven ADC inputs, six PWM interfaces, and one I2S interface (Note: These interfaces are all multiplexed.)
  • The frequency of the external primary crystal oscillator is 40 MHz or 24 MHz.
+
  • Include one SDIO interface, two SPI interfaces, two I2C interfaces, three UART interfaces, 15 GPIO interfaces, seven ADC inputs, six PWM interfaces, and one I2S interface (Note: These interfaces are all multiplexed.)
  • The frequency of the external primary crystal oscillator is 40 MHz or 24 MHz.

Other information

+

Other information

  • Package: QFN-32, 5 mm x 5 mm
  • Operating temperature: –40°C to +85°C
+
  • Package: QFN-32, 5 mm x 5 mm
  • Operating temperature: –40°C to +85°C
- - - - - - @@ -179,7 +180,7 @@ Run the following command to check whether bash is used as the shell: ls -l /bin/sh ``` -If **/bin/sh -\> bash** is not displayed, do as follows to change the shell to bash. +If **/bin/sh -\> bash** is not displayed, do as follows to change shell to bash. **Method 1:** Run the following command on the device and then click **No**. @@ -190,51 +191,117 @@ sudo dpkg-reconfigure dash **Method 2:** Run the **rm -rf /bin/sh** command to delete sh and then run the sudo **ln -s /bin/bash /bin/sh** command to create a new soft link. ``` -rm -rf /bin/sh +sudo rm -rf /bin/sh sudo ln -s /bin/bash /bin/sh ``` ## Installing a Python Environment 1. Start a Linux server. -2. Run the **python3 --version** command to check the Python version. If the Python version is not 3.7 or later, do as follows to install an appropriate Python version, for example, Python 3.8: - - If the Ubuntu version is 18, run the **sudo apt-get install python3.8** command. - - If the Ubuntu version is 16, download the installation package and install Python. +2. Run the following command to check the Python version \(Python 3.7 or later is required\): - 1. Run the following command to install Python environment dependencies \(gcc, g++, make, zlib, libffi\): + ``` + python3 --version + ``` - "sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib\* && sudo apt-get install libffi-dev " + Do as follows to install Python, for example, Python 3.8. - 2. Obtain [Python 3.8.5 installation package](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz), save it to the Linux server, and run the following command to install it: + 1. Run the following command to check the Ubuntu version: - "tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && make && sudo make install" + ``` + cat /etc/issue + ``` - 3. After Python-3.8.5 is installed, run **which python3.8** to display a path and link it to **/usr/bin/python**. For example: + 1. Install Python based on the Ubuntu version. + - If the Ubuntu version is 18 or later, run the following command: - "cd /usr/bin && rm python && ln -s /usr/local/bin/python3.8 python && python --version" + ``` + sudo apt-get install python3.8 + ``` + - If the Ubuntu version is 16, download the installation package and install Python. - - Run the **sudo apt-get install python3-setuptools python3-pip -y** command to install the Python package management tool. \(The root/sudo permission is required.\) Run the **sudo pip3 install --upgrade pip** command to upgrade **pip3**. + 1. Run the following command to install Python environment dependencies \(gcc, g++, make, zlib, libffi\): - Alternatively, install the Python package management tool as instructed by the official website. Specifically, run the **curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py** command to download the script **get-pip.py** and run the **python get-pip.py** command to install this script. + ``` + sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib* && sudo apt-get install libffi-dev + ``` + 1. Obtain the [Python3.8.5 installation package](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz), save it to the Linux server, and run the following command: -3. Run the **pip3 install setuptools** command to install setuptools module of Python. -4. Run the **sudo pip3 install kconfiglib** command to install **GUI menuconfig**. The Kconfiglib 13.2.0 or later is recommended. \(The root/sudo permission is required.\) + ``` + tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && sudo make && sudo make install + ``` - Alternatively, you can do as follows to install Kconfiglib 13.2.0 or later: - 1. Download the .whl file, for example, **kconfiglib-13.2.0-py2.py3-none-any.whl**, from [https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files). - 1. Run the **sudo pip3 install kconfiglib-xxx.whl** command to install the .whl file, for example, **sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl**\). \(The root/sudo permission is required.\) - 2. If you encounter the error **subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1.**, run the **find / -name lsb\_release** command, for example, **rm -rf /usr/bin/lsb\_release** to locate and delete it **sb\_release**. +3. After Python is installed, run the following command to link the Python path to **/usr/bin/python**: + + ``` + which python3.8 + cd /usr/bin && sudo rm python && sudo ln -s /usr/local/bin/python3.8 python && python --version + ``` + +4. Install and upgrade the Python package management tool \(pip3\) using either of the following methods: + - **Command line:** + + ``` + sudo apt-get install python3-setuptools python3-pip -y + sudo pip3 install --upgrade pip + ``` + + - **Installation package:** + + ``` + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + python get-pip.py + ``` + + +5. Run the following command to install setuptools: + + ``` + pip3 install setuptools + ``` + +6. Install the GUI menuconfig tool \(Kconfiglib\). You are advised to install Kconfiglib 13.2.0 or later. + - **Command line:** + + ``` + sudo pip3 install kconfiglib + ``` + + + - **Installation package:** + 1. Download the .whl file \(for example, **kconfiglib-13.2.0-py2.py3-none-any.whl**\). + + Download path: [https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files) + + + 1. Run the following command to install the .whl file: + + ``` + sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl + ``` + + ## Installing a File Packing Tool 1. Start a Linux server. -2. Run **mkfs.vfat**. If the command is not found, run **sudo apt-get install dosfstools**. -3. Run **mcopy**. If the command is not found, run **sudo apt-get install mtools**. +2. Run the following command to install **dosfstools**. + + ``` + sudo apt-get install dosfstools + ``` + +3. Run the following command to install **mtools**. + + ``` + sudo apt-get install mtools + ``` + ## Installing Compilation Tools @@ -247,31 +314,115 @@ sudo ln -s /bin/bash /bin/sh 1. Start a Linux server. 2. Download [gn](https://repo.huaweicloud.com/harmonyos/compiler/gn/1523/linux/gn.1523.tar). -3. Decompress the **gn** installation package to the **tar -xvf gn.1523.tar -C \~/** in **\~/gn** directory. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/gn:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. +3. Run the following command to decompress the gn installation package to **\~/gn**. + + ``` + tar -xvf gn.1523.tar -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/gn:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + ## Installing ninjah 1. Start a Linux server. 2. Download [ninja](https://repo.huaweicloud.com/harmonyos/compiler/ninja/1.9.0/linux/ninja.1.9.0.tar). -3. Decompress the **ninja** installation package to **tar -xvf ninja.1.9.0.tar -C \~/** in **\~/ninja** directory. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/ninja:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. +3. Run the following command to decompress the ninja installation package to **\~/ninja**. + + ``` + tar -xvf ninja.1.9.0.tar -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/ninja:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + ## Installing the LLVM Toolchain 1. Start a Linux server. 2. Download [LLVM](http://tools.harmonyos.com/mirrors/clang/9.0.0-34042/linux/llvm-linux-9.0.0-34042.tar). -3. Decompress the LLVM installation package to the **\~/llvm** path by running **"tar -xvf llvm-linux-9.0.0-34042.tar -C \~/"**. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/llvm/bin:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. +3. Run the following command to decompress the LLVM installation package to **\~/llvm**. + + ``` + tar -xvf llvm-linux-9.0.0-34042.tar -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/llvm/bin:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + ## Installing hc-gen 1. Start a Linux server. 2. Download [hc-gen](https://repo.huaweicloud.com/harmonyos/compiler/hc-gen/0.65/linux/hc-gen-0.65-linux.tar). -3. Decompress the hc-gen installation package to **tar -xvf hc-gen-0.65-linux.tar -C \~/** in **\~/hc-gen** on the Linux. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/hc-gen:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. +3. Run the following command to decompress the hc-gen installation package to **\~/hc-gen** on the Linux server. + + ``` + tar -xvf hc-gen-0.65-linux.tar -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/hc-gen:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + diff --git a/docs-en/quick-start/setting-up-a-development-environment.md b/docs-en/quick-start/setting-up-a-development-environment.md index 95e689004c9b0970d06ad93bdd0d70751855d4c6..1c925af1ffc5a7589354add74a06ef9566d9c384 100755 --- a/docs-en/quick-start/setting-up-a-development-environment.md +++ b/docs-en/quick-start/setting-up-a-development-environment.md @@ -9,7 +9,7 @@ - Hi3861 WLAN module - USB Type-C cable used to connect to Windows workstation and Hi3861 WLAN module -[Figure 1](#fa54d47112b684c65b3dbf1779413545a) shows the hardware connections. +[Figure 1](#fig1651211924914) shows the hardware connections. **Figure 1** Hardware connections ![](figures/hardware-connections.png "hardware-connections") @@ -192,7 +192,7 @@ Run the following command to check whether bash is used as the shell: ls -l /bin/sh ``` -If **/bin/sh -\> bash** is not displayed, do as follows to change the shell to bash. +If **/bin/sh -\> bash** is not displayed, do as follows to change shell to bash. **Method 1:** Run the following command on the device and then click **No**. @@ -203,86 +203,185 @@ sudo dpkg-reconfigure dash **Method 2:** Run the **rm -rf /bin/sh** command to delete sh and then run the sudo **ln -s /bin/bash /bin/sh** command to create a new soft link. ``` -rm -rf /bin/sh +sudo rm -rf /bin/sh sudo ln -s /bin/bash /bin/sh ``` ## Installing a Python Environment 1. Start a Linux server. -2. Run the **python3 --version** command to check the Python version. If the Python version is not 3.7 or later, do as follows to install an appropriate Python version, for example, Python 3.8: - - If the Ubuntu version is 18, run the **sudo apt-get install python3.8** command. - - If the Ubuntu version is 16, download the installation package and install Python. +2. Run the following command to check the Python version \(Python 3.7 or later is required\): - 1. Run the following command to install Python environment dependencies \(gcc, g++, make, zlib, libffi\): + ``` + python3 --version + ``` - "sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib\* && sudo apt-get install libffi-dev " + Do as follows to install Python, for example, Python 3.8. - 2. Obtain [Python 3.8.5 installation package](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz), save it to the Linux server, and run the following command to install it: + 1. Run the following command to check the Ubuntu version: - "tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && make && sudo make install" + ``` + cat /etc/issue + ``` - 3. After Python-3.8.5 is installed, run **which python3.8** to display a path and link it to **/usr/bin/python**. For example: + 1. Install Python based on the Ubuntu version. + - If the Ubuntu version is 18 or later, run the following command: - "cd /usr/bin && rm python && ln -s /usr/local/bin/python3.8 python && python --version" + ``` + sudo apt-get install python3.8 + ``` + - If the Ubuntu version is 16, download the installation package and install Python. - - Run the **sudo apt-get install python3-setuptools python3-pip -y** command to install the Python package management tool. \(The root/sudo permission is required.\) Run the **sudo pip3 install --upgrade pip** command to upgrade **pip3**. + 1. Run the following command to install Python environment dependencies \(gcc, g++, make, zlib, libffi\): - Alternatively, install the Python package management tool as instructed by the official website. Specifically, run the **curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py** command to download the script **get-pip.py** and run the **python get-pip.py** command to install this script. + ``` + sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib* && sudo apt-get install libffi-dev + ``` + 1. Obtain the [Python3.8.5 installation package](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz), save it to the Linux server, and run the following command: -3. Run the **pip3 install setuptools** command to install setuptools module of Python. -4. Run the **sudo pip3 install kconfiglib** command to install **GUI menuconfig**. The Kconfiglib 13.2.0 or later is recommended. \(The root/sudo permission is required.\) + ``` + tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && sudo make && sudo make install + ``` - Alternatively, you can do as follows to install Kconfiglib 13.2.0 or later: - 1. Download the .whl file, for example, **kconfiglib-13.2.0-py2.py3-none-any.whl**, from [https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files). - 1. Run the **sudo pip3 install kconfiglib-xxx.whl** command to install the .whl file, for example, **sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl**\). \(The root/sudo permission is required.\) - 2. If you encounter the error **subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1.**, run the **find / -name lsb\_release** command, for example, **rm -rf /usr/bin/lsb\_release** to locate and delete it **sb\_release**. +3. After Python is installed, run the following command to link the Python path to **/usr/bin/python**: -5. Run the **sudo pip3 install pycryptodome** command to install pycryptodome or do as follows: + ``` + which python3.8 + cd /usr/bin && sudo rm python && sudo ln -s /usr/local/bin/python3.8 python && python --version + ``` - >![](public_sys-resources/icon-note.gif) **NOTE:** - >- Install the Python component packages on which the file signature depends, including pycryptodome, six, and ecdsa. - >- As the installation of ecdsa depends on that of six, install six first. +4. Install and upgrade the Python package management tool \(pip3\) using either of the following methods: + - **Command line:** - 1. Download the .whl file, for example, **pycryptodome-3.7.3-cp37-cp37m-manylinux1\_x86\_64.whl** from [https://pypi.org/project/pycryptodome/\#files](https://pypi.org/project/pycryptodome/#files). + ``` + sudo apt-get install python3-setuptools python3-pip -y + sudo pip3 install --upgrade pip + ``` - 1. Run the **sudo pip3 install pycryptodome-xxx.whl** command to install the .whl file, for example, **sudo pip3 install pycryptodome-3.7.3-cp37-cp37m-manylinux1\_x86\_64.whl**. + - **Installation package:** - After the installation is complete, the message **Successfully installed pycryptodome-3.7.3** is displayed. + ``` + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + python get-pip.py + ``` -6. Run the **sudo pip3 install six --upgrade --ignore-installed six** command to install **six** or do as follows: - 1. Download the .whl file, for example, **six-1.12.0-py2.py3-none-any.whl** from [https://pypi.org/project/six/\#files](https://pypi.org/project/six/#files). +5. Run the following command to install setuptools: - 1. Run the **sudo pip3 install six-xxx.whl** command to install the .whl file, for example, **sudo pip3 install six-1.12.0-py2.py3-none-any.whl**. \(The root/sudo permission is required.\) + ``` + pip3 install setuptools + ``` - After the installation is complete, the message **Successfully installed six-1.12.0** is displayed. +6. Install the GUI menuconfig tool \(Kconfiglib\). You are advised to install Kconfiglib 13.2.0 or later. + - **Command line:** -7. Run the **sudo pip3 install ecdsa** command to install **ecdsa** or do as follows: + ``` + sudo pip3 install kconfiglib + ``` - 1. Download the .whl file, for example, **ecdsa-0.14.1-py2.py3-none-any.whl** from [https://pypi.org/project/ecdsa/\#files](https://pypi.org/project/ecdsa/#files). - 1. Run the **sudo pip3 install ecdsa-xxx.whl** command to install the .whl file, for example, **sudo pip3 install ecdsa-0.14.1-py2.py3-none-any.whl**. \(The root/sudo permission is required.\) + - **Installation package:** + 1. Download the .whl file \(for example, **kconfiglib-13.2.0-py2.py3-none-any.whl**\). + + Download path: [https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files) + + + 1. Run the following command to install the .whl file: + + ``` + sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl + ``` + + + +7. Install **pycryptodome** using either of the following methods: + + Install the Python component packages on which the file signature depends, including pycryptodome, six, and ecdsa. As the installation of **ecdsa** depends on that of **six**, install **six** first. + + - **Command line:** + + ``` + sudo pip3 install pycryptodome + ``` + + - **Installation package:** + 1. Download the .whl file, for example, **pycryptodome-3.7.3-cp37-cp37m-manylinux1\_x86\_64.whl** from [https://pypi.org/project/pycryptodome/\#files](https://pypi.org/project/pycryptodome/#files). + + 1. Run the following command to install the .whl file: + + ``` + sudo pip3 install pycryptodome-3.7.3-cp37-cp37m-manylinux1_x86_64.whl + ``` + + + +8. Install **six** using either of the following methods: + - **Command line:** + + ``` + sudo pip3 install six --upgrade --ignore-installed six + ``` + + + - **Installation package:** + 1. Download the .whl file, for example, **six-1.12.0-py2.py3-none-any.whl** from [https://pypi.org/project/six/\#files](https://pypi.org/project/six/#files). + + 1. Run the following command to install the .whl file: + + ``` + sudo pip3 install six-1.12.0-py2.py3-none-any.whl + ``` + + + +9. Install **ecdsa** using either of the following methods: + - **Command line:** + + ``` + sudo pip3 install ecdsa + ``` + + - **Installation package:** + 1. Download the .whl file, for example, **ecdsa-0.14.1-py2.py3-none-any.whl** from [https://pypi.org/project/ecdsa/\#files](https://pypi.org/project/ecdsa/#files). + + 1. Run the following command to install the .whl file: + + ``` + sudo pip3 install ecdsa-0.14.1-py2.py3-none-any.whl + ``` + - After the installation is complete, the message **Successfully installed ecdsa-0.14.1** is displayed. ## Installing Scons 1. Start a Linux server. -2. Run the **sudo apt-get install scons -y** command to install the SCons package. \(The root/sudo permission is required.\) +2. Run the following command to install the SCons installation package: + + ``` + sudo apt-get install scons -y + ``` - Run the **sudo apt-get install scons -y** command. If the installation package cannot be found in the software source, do as follows: + If the installation package cannot be found in the software source, do as follows: 1. Download the source code package from [https://scons.org/pages/download.html](https://scons.org/pages/download.html). The recommended SCons version is 3.0.4 or later. 2. Decompress the source code package to any directory. - 3. Enter the source code directory and run the **sudo python3 setup.py install** command to install the source code package. \(The root/sudo permission is required.\) + 3. Go to the source code directory and run the following command to install the source code package: -3. Run the **scons -v** command to check whether the installation is successful. + ``` + sudo python3 setup.py install + ``` + + +3. Run the following command to check whether the installation is successful. + + ``` + scons -v + ``` **Figure 5** Successful installation ![](figures/successful-installation.png "successful-installation") @@ -299,17 +398,59 @@ sudo ln -s /bin/bash /bin/sh 1. Start a Linux server. 2. Download [gn](https://repo.huaweicloud.com/harmonyos/compiler/gn/1523/linux/gn.1523.tar). -3. Decompress the **gn** installation package to the **tar -xvf gn.1523.tar -C \~/** in **\~/gn** directory. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/gn:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. +3. Run the following command to decompress the gn installation package to **\~/gn**. + + ``` + tar -xvf gn.1523.tar -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/gn:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + ## Installing ninjah 1. Start a Linux server. 2. Download [ninja](https://repo.huaweicloud.com/harmonyos/compiler/ninja/1.9.0/linux/ninja.1.9.0.tar). -3. Decompress the **ninja** installation package to **tar -xvf ninja.1.9.0.tar -C \~/** in **\~/ninja** directory. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/ninja:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. +3. Run the following command to decompress the ninja installation package to **\~/ninja**. + + ``` + tar -xvf ninja.1.9.0.tar -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/ninja:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + ## Installing gcc\_riscv32 \(Compilation Toolchain for WLAN Module\) @@ -318,8 +459,34 @@ sudo ln -s /bin/bash /bin/sh 1. Start a Linux server. 2. Download [gcc\_riscv32](https://repo.huaweicloud.com/harmonyos/compiler/gcc_riscv32/7.3.0/linux/gcc_riscv32-linux-7.3.0.tar.gz). -3. Decompress the gcc\_riscv32 installation package to **tar -xvf gcc\_riscv32-linux-7.3.0.tar.gz -C \~/** in **/opt/gcc\_riscv32**. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/gcc\_riscv32/bin:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. -6. Enter **$ riscv32-unknown-elf-gcc -v** in the shell command line to check whether the compiler is successfully installed. If the compiler version number is correctly displayed, the installation is successful. +3. Run the following command to decompress the gcc\_riscv32 installation package to **\~/gcc\_riscv32**. + + ``` + tar -xvf gcc_riscv32-linux-7.3.0.tar.gz -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/gcc_riscv32/bin:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + +6. Enter the following command to check whether the compiler is successfully installed. If the compiler version number is correctly displayed, the installation is successful. + + ``` + riscv32-unknown-elf-gcc -v + ``` + diff --git a/docs-en/quick-start/setting-up-a-hi3516-board-environment.md b/docs-en/quick-start/setting-up-a-hi3516-board-environment.md index 205948a0587106616ca431e9accfa1175eb0e3f0..dd3763b1f3cf8bdfc11cc5778d18e7e51d6482ae 100755 --- a/docs-en/quick-start/setting-up-a-hi3516-board-environment.md +++ b/docs-en/quick-start/setting-up-a-hi3516-board-environment.md @@ -49,8 +49,8 @@ - @@ -156,7 +156,8 @@ The following table describes the common tools required for Linux and how to obt - @@ -193,7 +194,7 @@ Run the following command to check whether bash is used as the shell: ls -l /bin/sh ``` -If **/bin/sh -\> bash** is not displayed, do as follows to change the shell to bash. +If **/bin/sh -\> bash** is not displayed, do as follows to change shell to bash. **Method 1:** Run the following command on the device and then click **No**. @@ -204,51 +205,117 @@ sudo dpkg-reconfigure dash **Method 2:** Run the **rm -rf /bin/sh** command to delete sh and then run the sudo **ln -s /bin/bash /bin/sh** command to create a new soft link. ``` -rm -rf /bin/sh +sudo rm -rf /bin/sh sudo ln -s /bin/bash /bin/sh ``` ## Installing a Python Environment 1. Start a Linux server. -2. Run the **python3 --version** command to check the Python version. If the Python version is not 3.7 or later, do as follows to install an appropriate Python version, for example, Python 3.8: - - If the Ubuntu version is 18, run the **sudo apt-get install python3.8** command. - - If the Ubuntu version is 16, download the installation package and install Python. +2. Run the following command to check the Python version \(Python 3.7 or later is required\): - 1. Run the following command to install Python environment dependencies \(gcc, g++, make, zlib, libffi\): + ``` + python3 --version + ``` - "sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib\* && sudo apt-get install libffi-dev " + Do as follows to install Python, for example, Python 3.8. - 2. Obtain [Python 3.8.5 installation package](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz), save it to the Linux server, and run the following command to install it: + 1. Run the following command to check the Ubuntu version: - "tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && make && sudo make install" + ``` + cat /etc/issue + ``` - 3. After Python-3.8.5 is installed, run **which python3.8** to display a path and link it to **/usr/bin/python**. For example: + 1. Install Python based on the Ubuntu version. + - If the Ubuntu version is 18 or later, run the following command: - "cd /usr/bin && rm python && ln -s /usr/local/bin/python3.8 python && python --version" + ``` + sudo apt-get install python3.8 + ``` + - If the Ubuntu version is 16, download the installation package and install Python. - - Run the **sudo apt-get install python3-setuptools python3-pip -y** command to install the Python package management tool. \(The root/sudo permission is required.\) Run the **sudo pip3 install --upgrade pip** command to upgrade **pip3**. + 1. Run the following command to install Python environment dependencies \(gcc, g++, make, zlib, libffi\): - Alternatively, install the Python package management tool as instructed by the official website. Specifically, run the **curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py** command to download the script **get-pip.py** and run the **python get-pip.py** command to install this script. + ``` + sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib* && sudo apt-get install libffi-dev + ``` + 1. Obtain the [Python3.8.5 installation package](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz), save it to the Linux server, and run the following command: -3. Run the **pip3 install setuptools** command to install setuptools module of Python. -4. Run the **sudo pip3 install kconfiglib** command to install **GUI menuconfig**. The Kconfiglib 13.2.0 or later is recommended. \(The root/sudo permission is required.\) + ``` + tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && sudo make && sudo make install + ``` - Alternatively, you can do as follows to install Kconfiglib 13.2.0 or later: - 1. Download the .whl file, for example, **kconfiglib-13.2.0-py2.py3-none-any.whl**, from [https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files). - 1. Run the **sudo pip3 install kconfiglib-xxx.whl** command to install the .whl file, for example, **sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl**\). \(The root/sudo permission is required.\) - 2. If you encounter the error **subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1.**, run the **find / -name lsb\_release** command, for example, **rm -rf /usr/bin/lsb\_release** to locate and delete it **sb\_release**. +3. After Python is installed, run the following command to link the Python path to **/usr/bin/python**: + + ``` + which python3.8 + cd /usr/bin && sudo rm python && sudo ln -s /usr/local/bin/python3.8 python && python --version + ``` + +4. Install and upgrade the Python package management tool \(pip3\) using either of the following methods: + - **Command line:** + + ``` + sudo apt-get install python3-setuptools python3-pip -y + sudo pip3 install --upgrade pip + ``` + + - **Installation package:** + + ``` + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + python get-pip.py + ``` + + +5. Run the following command to install setuptools: + + ``` + pip3 install setuptools + ``` + +6. Install the GUI menuconfig tool \(Kconfiglib\). You are advised to install Kconfiglib 13.2.0 or later. + - **Command line:** + + ``` + sudo pip3 install kconfiglib + ``` + + + - **Installation package:** + 1. Download the .whl file \(for example, **kconfiglib-13.2.0-py2.py3-none-any.whl**\). + + Download path: [https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files) + + + 1. Run the following command to install the .whl file: + + ``` + sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl + ``` + + ## Installing a File Packing Tool 1. Start a Linux server. -2. Run **mkfs.vfat**. If the command is not found, run **sudo apt-get install dosfstools**. -3. Run **mcopy**. If the command is not found, run **sudo apt-get install mtools**. +2. Run the following command to install **dosfstools**. + + ``` + sudo apt-get install dosfstools + ``` + +3. Run the following command to install **mtools**. + + ``` + sudo apt-get install mtools + ``` + ## Installing Compilation Tools @@ -261,31 +328,115 @@ sudo ln -s /bin/bash /bin/sh 1. Start a Linux server. 2. Download [gn](https://repo.huaweicloud.com/harmonyos/compiler/gn/1523/linux/gn.1523.tar). -3. Decompress the **gn** installation package to the **tar -xvf gn.1523.tar -C \~/** in **\~/gn** directory. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/gn:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. +3. Run the following command to decompress the gn installation package to **\~/gn**. + + ``` + tar -xvf gn.1523.tar -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/gn:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + ## Installing ninjah 1. Start a Linux server. 2. Download [ninja](https://repo.huaweicloud.com/harmonyos/compiler/ninja/1.9.0/linux/ninja.1.9.0.tar). -3. Decompress the **ninja** installation package to **tar -xvf ninja.1.9.0.tar -C \~/** in **\~/ninja** directory. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/ninja:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. +3. Run the following command to decompress the ninja installation package to **\~/ninja**. + + ``` + tar -xvf ninja.1.9.0.tar -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/ninja:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + ## Installing the LLVM Toolchain 1. Start a Linux server. 2. Download [LLVM](http://tools.harmonyos.com/mirrors/clang/9.0.0-34042/linux/llvm-linux-9.0.0-34042.tar). -3. Decompress the LLVM installation package to the **\~/llvm** path by running **"tar -xvf llvm-linux-9.0.0-34042.tar -C \~/"**. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/llvm/bin:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. +3. Run the following command to decompress the LLVM installation package to **\~/llvm**. + + ``` + tar -xvf llvm-linux-9.0.0-34042.tar -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/llvm/bin:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + ## Installing hc-gen 1. Start a Linux server. 2. Download [hc-gen](https://repo.huaweicloud.com/harmonyos/compiler/hc-gen/0.65/linux/hc-gen-0.65-linux.tar). -3. Decompress the hc-gen installation package to **tar -xvf hc-gen-0.65-linux.tar -C \~/** in **\~/hc-gen** on the Linux. -4. Open the **\~/.bashrc** file in Vim and add a line of **export PATH=\~/hc-gen:$PATH** to set an environment variable. -5. Run **source \~/.bashrc** to validate the environment variable. +3. Run the following command to decompress the hc-gen installation package to **\~/hc-gen** on the Linux server. + + ``` + tar -xvf hc-gen-0.65-linux.tar -C ~/ + ``` + +4. Set an environment variable by performing the following steps. Open the **\~/.bashrc** file in Vim first. + + ``` + vim ~/.bashrc + ``` + + Copy the following command to the last line of the **.bashrc** file, save the file, and exit. + + ``` + export PATH=~/hc-gen:$PATH + ``` + +5. Run the following command to validate the environment variable. + + ``` + source ~/.bashrc + ``` + diff --git a/docs-en/quick-start/the-first-example-program-running-on-the-hi3861-development-board.md b/docs-en/quick-start/the-first-example-program-running-on-the-hi3861-development-board.md deleted file mode 100755 index 9e102c96ae415635932755315073507b4f8c8a09..0000000000000000000000000000000000000000 --- a/docs-en/quick-start/the-first-example-program-running-on-the-hi3861-development-board.md +++ /dev/null @@ -1,110 +0,0 @@ -# The First Example Program Running on the Hi3861 Development Board - -This example demonstrates how to use AT commands to complete the network configuration of the Wi-Fi module. - -## Source Code Acquisition - -You need to download a set of source code from the Linux server. For details, see [Source Code Acquisition](en-us_topic_0000001050769927.md). - -## Compiling the Source Code - -This section describes how to compile the Wi-Fi module version on a Linux server. The entire process includes the following steps: - -1. Use a terminal tool to go to the root directory of the Linux code. -2. Run the python3 build.py wifiiot command to start compilation. -3. If "BUILD SUCCESS" is displayed after the compilation is complete, the compilation is successful, as shown in the following figure. - - **Figure 1** Successful compilation - ![](figures/successful-compilation.png "successful-compilation") - -4. After the build is successful, the following files are generated in the out/wifiiot directory. The compilation and build process is complete. - - **Figure 2** Directory for storing compilation files - - - ![](figures/en-us_image_0000001054218563.png) - - -## Image burning - -The images of the Hi3861 Wi-Fi module can be burnt by using the IDE tool DevEco \(for details about how to use the tool, see the related guide\). The burning procedure is as follows: - -1. Use a USB cable to connect the Windows workbench to the Wi-Fi module. \(You need to install the USB-to-serial driver before the installation. To download the driver, visit http://www.wch.cn/search?q=ch340g&t=downloads.\) Query the COM port on the device manager, for example, CH340 \(COM11\). The serial port integrates the burning, log printing, and Functions such as AT commands. - - **Figure 3** COM port of the device manager - ![](figures/com-port-of-the-device-manager.png "com-port-of-the-device-manager") - -2. Complete the basic burning configuration in the IDE and specify the path of HiBurn.exe in step 4. - - **Figure 4** Burning basic configurations - ![](figures/burning-basic-configurations.png "burning-basic-configurations") - -3. Other Key Configurations - - 1. Set Baud rate to a proper baud rate. A higher baud rate indicates a faster burning speed. - 2. Select a data bit from Data bit. The default value of Data bit for the Wi-Fi module is 8. - 3. Select the version package path ./out/wifiiot/Hi3861\_wifiiot\_app\_allinone.bin. - 4. Click Save. - - **Figure 5** Baud rate and data bit configuration - ![](figures/baud-rate-and-data-bit-configuration.png "baud-rate-and-data-bit-configuration") - - **Figure 6** Path of the burning package - - - ![](figures/en-us_image_0000001054087868.png) - -4. On the DecEco interface, click Burn and select the COM11 serial port. - - **Figure 7** Burning and starting - ![](figures/burning-and-starting.png "burning-and-starting") - -5. After the serial port is selected, Connecting, please reset device... is displayed in the TERMINAL dialog box of the IDE. The module enters the to-be-burnt state. - - **Figure 8** Burning process - ![](figures/burning-process.png "burning-process") - -6. Press the RST button on the module to start burning the version. If "Execution Successful" is displayed in the TERMINAL dialog box, the burning is complete. - - **Figure 9** Burning completed - - - ![](figures/en-us_image_0000001054802306.png) - - -## WLAN module networking - -After version building and burning are complete, the following describes how to run AT commands on the terminal to connect the Wi-Fi module to the network. - -1. Use a USB cable to connect the Windows workbench to the Wi-Fi module. \(You need to install the USB-to-serial driver before the installation. To download the driver, visit http://www.wch.cn/search?q=ch340g&t=downloads.\) Query the COM port on the device manager, for example, CH340 \(COM11\). The serial port integrates the burning, log printing, and Functions such as AT commands. - - **Figure 10** COM port of the device manager - ![](figures/com-port-of-the-device-manager-0.png "com-port-of-the-device-manager-0") - -2. On the Windows workbench, use a serial port terminal tool \(such as IPOP\) to connect to the serial port \(COM11\) of the Wi-Fi module, set the baud rate of the serial port to 115200, and select Newline. - - **Figure 11** IPOP configuration - ![](figures/ipop-configuration.png "ipop-configuration") - -3. Reset the Wi-Fi module. The message "ready to OS start" is displayed on the UE, indicating that the Wi-Fi module is started successfully. - - **Figure 12** Resetting the Wi-Fi module - ![](figures/resetting-the-wi-fi-module.png "resetting-the-wi-fi-module") - -4. Run the following AT commands in sequence to start the STA, connect to the specified AP, and enable DHCP. - - ``` - AT+STARTSTA - Start the STA mode. - AT+SCAN - Scan for Neighboring APs - AT+SCANRESULT - Display the scanning result - AT+CONN="SSID",2,"PASSWORD" - Connect to the specified AP. SSID and PASSWORD indicate the name and password of the hotspot to be connected. - AT+STASTAT - View the Connection Result - AT+DHCP=wlan0,1 - To request the IP address of wlan0 from the AP through DHCP - ``` - -5. An IP address has been assigned to the interface and the interface is properly connected to the gateway. - - **Figure 13** Successful networking of the Wi-Fi module - ![](figures/successful-networking-of-the-wi-fi-module.png "successful-networking-of-the-wi-fi-module") - - diff --git a/docs-en/readme/application-framework.md b/docs-en/readme/application-framework.md old mode 100755 new mode 100644 diff --git "a/docs-en/readme/figures/bms\347\255\226\347\225\245\344\270\276\344\276\213.png" "b/docs-en/readme/figures/bms\347\255\226\347\225\245\344\270\276\344\276\213.png" old mode 100644 new mode 100755 diff --git "a/docs-en/readme/figures/\345\205\250\345\261\200\347\255\226\347\225\2452.png" "b/docs-en/readme/figures/\345\205\250\345\261\200\347\255\226\347\225\2452.png" old mode 100644 new mode 100755 diff --git "a/docs-en/readme/figures/\347\255\226\347\225\245\347\261\273\345\236\2132.png" "b/docs-en/readme/figures/\347\255\226\347\225\245\347\261\273\345\236\2132.png" old mode 100644 new mode 100755 diff --git a/docs-en/readme/x-test-suite-certification-subsystem.md b/docs-en/readme/x-test-suite-certification-subsystem.md old mode 100755 new mode 100644 diff --git "a/get-code/\346\272\220\347\240\201\350\216\267\345\217\226.md" "b/get-code/\346\272\220\347\240\201\350\216\267\345\217\226.md" index ac206a61a3f2a3e77b58e05c5ec9d815ffad4002..8dd62d30e2bcd737fd1a0f800c11e93cbd43e2e0 100755 --- "a/get-code/\346\272\220\347\240\201\350\216\267\345\217\226.md" +++ "b/get-code/\346\272\220\347\240\201\350\216\267\345\217\226.md" @@ -8,7 +8,7 @@ OpenHarmony是HarmonyOS的开源版,由华为捐赠给开放原子开源基金 ## 源码获取概述 -本文档将介绍如何获取OpenHarmony源码并说明OpenHarmony的源码目录结构。OpenHarmony的代码以[组件](../bundles/概述-0.md)的形式开放,开发者可以通过如下其中一种方式获取: +本文档将介绍如何获取OpenHarmony源码并说明OpenHarmony的源码目录结构。OpenHarmony的代码以[组件](../bundles/概述.md)的形式开放,开发者可以通过如下其中一种方式获取: - **获取方式1**:从镜像站点下载压缩文件(推荐) - **获取方式2**:从hpm网站组件式获取。通过[HPM](https://hpm.harmonyos.com),查找满足需求的解决方案,挑选/裁剪组件后下载。 @@ -47,7 +47,7 @@ OpenHarmony是HarmonyOS的开源版,由华为捐赠给开放原子开源基金 -

Development Tool

Description

+

Description

How to Obtain

+

How to Obtain

Visual Studio Code

Edits code.

+

Edits code.

https://code.visualstudio.com/

+

https://code.visualstudio.com/

HiTool

Burns the images and the U-boot.

+

Burns the images and the U-boot.

http://www.hihope.org/download

+

http://www.hihope.org/download/AllDocuments

+

Tool package: Hi3516-Hitool.zip

USB-serial adapter driver

http://www.hihope.org/download

-

USB-to-Serial Comm Port.exe

+

http://www.hihope.org/download/AllDocuments

+

Driver name: USB-to-Serial Comm Port.exe

Burns the images and the U-boot.

http://www.hihope.org/download

+

http://www.hihope.org/download/AllDocuments

+

Tool package: Hi3516-Hitool.zip

站点

SHA256 校验码

+

SHA256 校验码

Hi3518解决方案(二进制)

@@ -234,13 +234,13 @@ OpenHarmony是HarmonyOS的开源版,由华为捐赠给开放原子开源基金 方式一(推荐):通过repo下载 ``` -repo init -u https://gitee.com/openharmony/manifest.git -b master +repo init -u https://gitee.com/openharmony/manifest.git -b master --no-repo-verify repo sync -c ``` 方式二:通过git clone单个代码仓库 -进入代码仓库主页:[https://gitee.com/openharmony](https://gitee.com/openharmony),选择需要克隆的代码仓库,执行命令,如: +进入代码仓库主页:https://gitee.com/openharmony,选择需要克隆的代码仓库,执行命令,如: ``` git clone https://gitee.com/openharmony/manifest.git -b master diff --git "a/get-code/\350\216\267\345\217\226\345\267\245\345\205\267.md" "b/get-code/\350\216\267\345\217\226\345\267\245\345\205\267.md" index 820fc481049f43773ceac25d13dc7d9024f12e47..8dbd60f3cd4e0935d74d14e6e5cfdc1fe2c3f52d 100755 --- "a/get-code/\350\216\267\345\217\226\345\267\245\345\205\267.md" +++ "b/get-code/\350\216\267\345\217\226\345\267\245\345\205\267.md" @@ -2,64 +2,76 @@ ## 编译工具链获取路径 -编译工具链从镜像站点下载,具体见下表。编译工具链的安装,请参考“快速入门”具体开发板的“环境搭建”部分。 +从镜像站点下载的编译工具链如下表所示。全部编译工具链的下载以及安装,请参考“快速入门”具体开发板的“环境搭建”部分。 **表 1** 编译工具链获取路径 -

下载内容

+ - - - + - + - - - - + - - - - - - - + - + - - - - + - - - diff --git "a/guide/figures/\344\277\256\346\224\271\346\250\241\346\235\277.png" "b/guide/figures/\344\277\256\346\224\271\346\250\241\346\235\277.png" deleted file mode 100755 index 9c05b8dd365bf0bad7f46eb526d248dd2116bf98..0000000000000000000000000000000000000000 Binary files "a/guide/figures/\344\277\256\346\224\271\346\250\241\346\235\277.png" and /dev/null differ diff --git "a/guide/figures/\345\220\257\345\212\250\347\244\272\344\276\213-0.png" "b/guide/figures/\345\220\257\345\212\250\347\244\272\344\276\213-0.png" deleted file mode 100755 index 881c86b3f1862cd09f013a154d3882060ba8bcba..0000000000000000000000000000000000000000 Binary files "a/guide/figures/\345\220\257\345\212\250\347\244\272\344\276\213-0.png" and /dev/null differ diff --git "a/guide/figures/\346\237\245\347\234\213\346\226\207\344\273\266\345\233\276-2.png" "b/guide/figures/\346\237\245\347\234\213\346\226\207\344\273\266\345\233\276-2.png" deleted file mode 100755 index 9d0d42f82c0e2a877c01397f038561bea07430e7..0000000000000000000000000000000000000000 Binary files "a/guide/figures/\346\237\245\347\234\213\346\226\207\344\273\266\345\233\276-2.png" and /dev/null differ diff --git "a/guide/figures/\350\276\223\345\205\245\345\275\225\345\203\217\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-3.png" "b/guide/figures/\350\276\223\345\205\245\345\275\225\345\203\217\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-3.png" deleted file mode 100755 index 5d9aea614e521f2a2c5e634917f16cebf24ae80d..0000000000000000000000000000000000000000 Binary files "a/guide/figures/\350\276\223\345\205\245\345\275\225\345\203\217\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-3.png" and /dev/null differ diff --git "a/guide/figures/\350\276\223\345\205\245\346\213\215\347\205\247\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-1.png" "b/guide/figures/\350\276\223\345\205\245\346\213\215\347\205\247\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-1.png" deleted file mode 100755 index 1dd037de6e414565b5c1707fd0aa8bf8ff611f38..0000000000000000000000000000000000000000 Binary files "a/guide/figures/\350\276\223\345\205\245\346\213\215\347\205\247\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-1.png" and /dev/null differ diff --git "a/guide/figures/\350\276\223\345\205\245\351\242\204\350\247\210\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-4.png" "b/guide/figures/\350\276\223\345\205\245\351\242\204\350\247\210\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-4.png" deleted file mode 100755 index c4ec2b5dfbeb02b3d6c5469dbe848debfe30d188..0000000000000000000000000000000000000000 Binary files "a/guide/figures/\350\276\223\345\205\245\351\242\204\350\247\210\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-4.png" and /dev/null differ diff --git "a/guide/figures/\350\276\223\345\207\272\351\200\200\345\207\272\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-6.png" "b/guide/figures/\350\276\223\345\207\272\351\200\200\345\207\272\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-6.png" deleted file mode 100755 index 8a4415351ff8f04e33bcd2d5dbb2b78a213bcbf2..0000000000000000000000000000000000000000 Binary files "a/guide/figures/\350\276\223\345\207\272\351\200\200\345\207\272\346\214\207\344\273\244\345\220\216\344\270\262\345\217\243\346\211\223\345\215\260\346\227\245\345\277\227-6.png" and /dev/null differ diff --git "a/guide/figures/\351\242\204\350\247\210\346\225\210\346\236\234-5.jpg" "b/guide/figures/\351\242\204\350\247\210\346\225\210\346\236\234-5.jpg" deleted file mode 100755 index cce3217d3793a18521540e1a8e6ba349949ad765..0000000000000000000000000000000000000000 Binary files "a/guide/figures/\351\242\204\350\247\210\346\225\210\346\236\234-5.jpg" and /dev/null differ diff --git "a/guide/\345\272\224\347\224\250\345\256\236\344\276\213-6.md" "b/guide/\345\272\224\347\224\250\345\256\236\344\276\213-6.md" deleted file mode 100755 index e0b481164aa28a3e93275cb82a77ac9de0b06c0e..0000000000000000000000000000000000000000 --- "a/guide/\345\272\224\347\224\250\345\256\236\344\276\213-6.md" +++ /dev/null @@ -1,47 +0,0 @@ -# 应用实例 - -- 开发板介绍及编译烧录、运行镜像基本流程参考对应开发板快速入门手册:Hi3516快速入门/Hi3518快速入门,编译结果包含camera\_sample示例。 -- 相机示例代码为applications/sample/camera/media/camera\_sample.cpp。 - - >![](public_sys-resources/icon-notice.gif) **须知:** - >实例运行拍照和录像功能需要插入TF卡\(最大容量支持128GB\),系统启动后时自动将TF卡挂载至/sdcard目录,如果在启动后插入则需要手动挂载。查看拍照和录像内容可将TF卡中内容复制到电脑中进行查看,预览功能无需TF卡。 - - -1. 启动camera\_sample - - **图 1** 启动示例 - ![](figures/启动示例-0.png "启动示例-0") - - 运行后的控制命令如串口打印所示,按s键停止当前操作(包括录像和预览),按q键退出示例程序。 - -2. 按1进行拍照,拍照的文件格式为jpg,存储在/sdcard,文件名Capture\* - - **图 2** 输入拍照指令后串口打印日志 - ![](figures/输入拍照指令后串口打印日志-1.png "输入拍照指令后串口打印日志-1") - - 若想查看保存文件,可在退出程序后进入文件系统查看,退出后重新进入请回到步骤1。 - - **图 3** 查看文件图 - ![](figures/查看文件图-2.png "查看文件图-2") - -3. 按2进行录像,录像的文件格式为mp4,存储在/sdcard,文件名Record\*,按s键停止 - - **图 4** 输入录像指令后串口打印日志 - ![](figures/输入录像指令后串口打印日志-3.png "输入录像指令后串口打印日志-3") - -4. 按3进行预览,预览图像直接送至显示屏,按s键停止,没有显示模块的平台(如Hi3518开发板)不支持预览 - - **图 5** 输入预览指令后串口打印日志 - ![](figures/输入预览指令后串口打印日志-4.png "输入预览指令后串口打印日志-4") - - 预览效果如下 - - **图 6** 预览效果 - ![](figures/预览效果-5.jpg "预览效果-5") - -5. 按q键退出 - - **图 7** 输出退出指令后串口打印日志 - ![](figures/输出退出指令后串口打印日志-6.png "输出退出指令后串口打印日志-6") - - diff --git "a/guide/\345\274\200\345\217\221\345\207\206\345\244\207.md" "b/guide/\345\274\200\345\217\221\345\207\206\345\244\207.md" index 68d25afb41eac9f2967bca33cbca41620df5e180..45f984b337a776f3031f94e1a5ab13c6aa0e9aca 100755 --- "a/guide/\345\274\200\345\217\221\345\207\206\345\244\207.md" +++ "b/guide/\345\274\200\345\217\221\345\207\206\345\244\207.md" @@ -19,8 +19,27 @@ 1. 将“deviceType”的取值修改为"smartVision"。 2. 在abilities数组内新增“visible”属性,取值设置为“true”。 - **图 2** 修改模板 - ![](figures/修改模板.png "修改模板") + ``` + "deviceConfig": {}, + "module": { + "deviceType": [ + "smartVision" //将deviceType 改为"smartVision" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry" + }, + "abilities": [ + {"name": + "default","icon": + "$media:icon", + "label": "music", + "visible": true, //新增“visible”属性,值为“true” + "type": "page" + } + ], + ``` diff --git "a/guide/\345\274\200\345\217\221\345\216\206\345\217\262\351\241\265.md" "b/guide/\345\274\200\345\217\221\345\216\206\345\217\262\351\241\265.md" index 409f67e82eaa2b7072c2e356a16a18fbdc3e6426..e6adbd590e884d93f84a0b68ad6cb910330f394d 100755 --- "a/guide/\345\274\200\345\217\221\345\216\206\345\217\262\351\241\265.md" +++ "b/guide/\345\274\200\345\217\221\345\216\206\345\217\262\351\241\265.md" @@ -5,35 +5,35 @@ ## history.hml ``` - + -
+
{{historicalData}}
- -
-
+ +
+
{{$item}} -
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- +
- + ``` @@ -42,24 +42,24 @@ ``` .div-info-location{ - color:#dcdcdc; - width:321px; - height:40px; + color: #dcdcdc; + width: 321px; + height: 40px; } .div-info-historical-data{ - color:#f5fffa; - width:321px; - height:40px; + color: #f5fffa; + width: 321px; + height: 40px; } .gas-name{ - color:#f0ffff; - text-align:right; - width:321px; - height:32px; + color: #f0ffff; + text-align: right; + width: 321px; + height: 32px; } .info-list-item{ - width:321px; - height:80px; + width: 321px; + height: 80px; } ``` @@ -69,8 +69,8 @@ import router from'@system.router' module.exports = { data: { - historicalData:"historicalData", - datasets:["CO","O3","NO2","NO","PM25","SO2"] + historicalData: 'historicalData', + datasets: ['CO','O3','NO2','NO','PM25','SO2'] }, onInit(){ //国际化信息处理 @@ -78,7 +78,7 @@ module.exports = { }, backDetail(){ router.replace({ //返回详情页 - uri:'pages/detail/detail' + uri: 'pages/detail/detail' }); } } diff --git "a/guide/\345\274\200\345\217\221\350\257\246\346\203\205\351\241\265.md" "b/guide/\345\274\200\345\217\221\350\257\246\346\203\205\351\241\265.md" index e95930d24ebf8f5211eec308f378c5af6ab2bbd0..abb7d820e2768adf28b3aac9b8dc052ff763c89c 100755 --- "a/guide/\345\274\200\345\217\221\350\257\246\346\203\205\351\241\265.md" +++ "b/guide/\345\274\200\345\217\221\350\257\246\346\203\205\351\241\265.md" @@ -5,45 +5,45 @@ ## detail.hml ``` - - -
- {{location}} + + +
+ {{location}}
- -
+ +
CO {{airData[0]}}
-
+
NO2 {{airData[1]}}
-
+
PM10 {{airData[2]}}
-
+
PM2.5 {{airData[3]}}
-
+
SO2 {{airData[4]}}
- -
- + +
+ {{updated}}:{{updateStatus}} - + {{dataSourceName}}:{{dataSource}}
- -
- - + +
+ +
@@ -54,30 +54,30 @@ ``` .line-div{ - background-color:#f5fffa; - height:2px; - width:454px; + background-color: #f5fffa; + height: 2px; + width: 454px; } .info-div-width-height{ - width:321px; - height:60px; + width: 321px; + height: 60px; margin-top: 20px; } .gas-name{ - color:#f5fffa; - width:160px; - height:30px; + color: #f5fffa; + width: 160px; + height: 30px; } .gas-value{ - text-align:right; - color:#00fa9a; - width:160px; - height:30px; + text-align: right; + color: #00fa9a; + width: 160px; + height: 30px; } .config-info { - height:40px; - width:321px; - color:#f5fffa; + height: 40px; + width: 321px; + color: #f5fffa; } ``` @@ -87,15 +87,15 @@ import router from'@system.router' export default { data:{ //初始化信息 - location:"HangZhou", - udpateStatus:"1h ago", - dataSource:"tianqi.com", - updateTime:"15:13", - updated:'Updated', - dataSourceName:'Data Source', - sampledOn:'Sampled on', - cityIndex:0, - airData:['100', '90', '120', '50', '150', '40', '25'] + location: 'HangZhou', + udpateStatus: '1h ago', + dataSource: 'tianqi.com', + updateTime: '15:13', + updated: 'Updated', + dataSourceName: 'Data Source', + sampledOn: 'Sampled on', + cityIndex: 0, + airData: ['100', '90', '120', '50', '150', '40', '25'] }, onInit(){ //国际化信息处理 @@ -104,19 +104,20 @@ export default { this.dataSourceName = this.$t("dataSourceName"); this.sampledOn = this.$t("sampledOn"); this.monitoringStation = this.$t("monitoringStation"); - if(this.selectedCityIndex != null){ //保存主页传过来的城市信息 - this.cityIndex= this.selectedCityIndex; + //保存主页传过来的城市信息 + if(this.selectedCityIndex != null){ + this.cityIndex = this.selectedCityIndex; } }, openHistroy(){ //跳转到历史页 router.replace({ - uri:'pages/history/history' + uri: 'pages/history/history' }); }, backMain(){ //返回主页,并将选择的城市信息带回 router.replace({ - uri:'pages/index/index', - params:{selectedCityIndex:this.cityIndex} + uri: 'pages/index/index', + params: {selectedCityIndex:this.cityIndex} }); } } diff --git "a/guide/\345\274\200\345\217\221\351\246\226\351\241\265.md" "b/guide/\345\274\200\345\217\221\351\246\226\351\241\265.md" index 1a003a1a7f8d101dea9ed0a275e553c3824caadc..ccbb700353e3e2cda6d5ac1bcf012daa6f3ccf79 100755 --- "a/guide/\345\274\200\345\217\221\351\246\226\351\241\265.md" +++ "b/guide/\345\274\200\345\217\221\351\246\226\351\241\265.md" @@ -7,7 +7,7 @@ 在hml文件中添加一个根节点swiper,注意每个hml文件中有且只能有一个根节点,代码片段如下: ``` - + ``` @@ -33,7 +33,8 @@ export default { //定义参数 data: { - swiperPage:0 //默认是第一页 + //默认是第一页 + swiperPage: 0 }, onInit () { }, @@ -51,7 +52,7 @@ ``` - + ------城市 @@ -79,14 +80,14 @@ ``` - + - {{airData[0].airQuality}} + {{airData[0].airQuality}} {{airData[0].location}} - + {{airData[0].detailData}} @@ -94,10 +95,10 @@ - {{airData[1].airQuality}} + {{airData[1].airQuality}} {{airData[1].location}} - - + + {{airData[1].detailData}} AQI @@ -110,52 +111,52 @@ ``` .pm25-value{ - text-align:center; - font-size:38px; - color:#f0ffff; - width:454px; - height:50px; - top:275px; + text-align: center; + font-size: 38px; + color: #f0ffff; + width: 454px; + height: 50px; + top: 275px; } .pm25-name{ - text-align:center; - color:#a2c4a2; - width:454px; - height:50px; - top:335px; + text-align: center; + color: #a2c4a2; + width: 454px; + height: 50px; + top: 335px; } .location-text{ - text-align:center; - color:#f0ffff; - width:454px; - height:50px; - top:20px; + text-align: center; + color: #f0ffff; + width: 454px; + height: 50px; + top: 20px; } .container{ height: 454px; - width: 454px;; + width: 454px; } .circleProgress{ - centerX:227px; - centerY:250px; - radius:180px; - startAngle:198; - totalAngle:320; - strokeWidth:45; - width:454px; - height:454px; + centerX: 227px; + centerY: 250px; + radius: 180px; + startAngle: 198; + totalAngle: 320; + strokeWidth: 45px; + width: 454px; + height: 454px; } .image{ - top:390px; - left:217px; - width:32px; - height:32px; + top: 390px; + left: 217px; + width: 32px; + height: 32px; } .airquality{ - top:220px; + top: 220px; text-align: center; - width:454px; - height:40px; + width: 454px; + height: 40px; } ``` @@ -170,15 +171,18 @@ export default { //定义参数 data: { - textColor1:'#00ff00',//文字颜色 - textColor2:'#00ff00', - bgColor1:'#669966',//背景颜色 - bgColor2:'#669966', - swiperPage:0, - percent1:0,//进度条进度 - percent2:0, - src1:'common/cloud_green.png', - src2:'common/cloud_green.png', + //文字颜色 + textColor1: '#00ff00', + textColor2: '#00ff00', + //背景颜色 + bgColor1: '#669966', + bgColor2: '#669966', + swiperPage: 0, + //进度条进度 + percent1: 0, + percent2: 0, + src1: 'common/cloud_green.png', + src2: 'common/cloud_green.png', airData: [{ location: 'HangZhou', airQuality: 'Good', @@ -186,7 +190,7 @@ }, { location: 'ShangHai', airQuality: 'Unhealth', - detailData:90 + detailData: 90 }] }, onInit () { @@ -195,21 +199,24 @@ this.airData[1].location = this.$t(this.airData[1].location); this.airData[0].airQuality = this.$t(this.airData[0].airQuality); this.airData[1].airQuality = this.$t(this.airData[1].airQuality); - if(this.airData[0].detailData > 100){ //根据指标值显示不同的颜色问题和图片 - this.src1 = "common/cloud_red.png"; - this.textColor1 = '#ff0000';//显示红色文字 + //根据指标值显示不同的颜色问题和图片 + if(this.airData[0].detailData > 100){ + this.src1 = 'common/cloud_red.png'; + //显示红色文字 + this.textColor1 = '#ff0000'; this.bgColor1 = '#9d7462'; } else if(50 < this.airData[0].detailData && this.airData[0].detailData <= 100){ - this.src1 = "common/cloud_yellow.png"; - this.textColor1 = '#ecf19a';//显示黄色文字 + this.src1 = 'common/cloud_yellow.png'; + //显示黄色文字 + this.textColor1 = '#ecf19a'; this.bgColor1 = '#9d9d62'; } if(this.airData[1].detailData > 100){ - this.src2 = "common/cloud_red.png"; + this.src2 = 'common/cloud_red.png'; this.textColor2 = '#ff0000'; this.bgColor2 = '#9d7462'; } else if(50 < this.airData[1].detailData && this.airData[1].detailData <= 100){ - this.src2 = "common/cloud_yellow.png"; + this.src2 = 'common/cloud_yellow.png'; this.textColor2 = '#ecf19a'; this.bgColor2 = '#9d9d62'; } @@ -217,9 +224,11 @@ this.swiperPage = this.selectedCityIndex; } }, - onShow () { //页面显示的时候一些处理逻辑 + //页面显示的时候一些处理逻辑 + onShow () { var self = this; - var time = 1000/(self.airData[self.swiperPage].detailData);//1s播放完动画 + //1s播放完动画 + var time = 1000/(self.airData[self.swiperPage].detailData); if(time == 0){ time = 100; } @@ -239,8 +248,9 @@ //跳转到详情页 openDetail () { router.replace({ - uri:'pages/detail/detail', - params:{selectedCityIndex:this.swiperPage}//选中的城市 + uri: 'pages/detail/detail', + //选中的城市 + params: {selectedCityIndex:this.swiperPage} }); }, //swiper滑动回调事件,保存当前swiper的index值,从详情页返回直接跳转到swiper的指定页 diff --git "a/guide/\346\246\202\350\277\260-0.md" "b/guide/\346\246\202\350\277\260-0.md" index 9a2ebcbe636f32e53fcac626811af6ed8e1e99f3..017dcaf7da2fee772f403d210d2d620c6b696be3 100755 --- "a/guide/\346\246\202\350\277\260-0.md" +++ "b/guide/\346\246\202\350\277\260-0.md" @@ -4,3 +4,7 @@ 开发者可通过执行示例应用,对开发板的外设控制有了更深入了解后,可使用开发板完成“监控摄像头”等设备。 +若开发者想先查看示例效果,请进入[应用实例](应用实例.md)。如需自定义应用行为,可参考[示例开发](示例开发.md)对示例代码进行修改。 + +相机开发基本概念可参考:[相机开发概述](../subsystems/相机开发概述.md)。 + diff --git "a/guide/\346\246\202\350\277\260-1.md" "b/guide/\346\246\202\350\277\260-1.md" index 4be67ad94b688fe40de85353a25d78bee8bb9ea2..9e3348f25d7fa4f7371a02fb44a6014f431b3264 100755 --- "a/guide/\346\246\202\350\277\260-1.md" +++ "b/guide/\346\246\202\350\277\260-1.md" @@ -4,3 +4,7 @@ 开发者可通过执行示例应用,对开发板的外设控制有了更深入了解后,可使用开发板完成“智能猫眼”、“智能后视镜”、“智能带屏音箱”等设备。 +若开发者想先查看示例效果,请进入[应用实例](应用实例-5.md)。如需自定义应用行为,可参考[示例开发](示例开发-2.md)对示例代码进行修改。 + +相机开发基本概念可参考:[相机开发概述](../subsystems/相机开发概述.md)。 + diff --git "a/guide/\346\246\202\350\277\260-7.md" "b/guide/\346\246\202\350\277\260-7.md" deleted file mode 100755 index 6c5272ecacb525148239659de73db6e96df923e5..0000000000000000000000000000000000000000 --- "a/guide/\346\246\202\350\277\260-7.md" +++ /dev/null @@ -1,13 +0,0 @@ -# 概述 - -本文档介绍如何快速搭建基于OpenHarmony系统的轻车机开发环境,并基于一个Demo示例逐步展示应用的创建、开发和调试等流程。本文以在轻车机系统上开发空气质量监测(AirQuality)App为例进行说明。 - -## 效果展示 - -空气质量监测App是一款展示城市空气质量信息的应用,应用有三个页面:首页(空气质量概况)、详情页和历史页,空气质量监测App在DevEco Studio模拟器中的显示效果如下图所示。 - -**图 1** 空气质量监测 App显示效果图 - - -![](figures/Video_2020-07-25_173141.gif) - diff --git "a/guide/\347\242\260\344\270\200\347\242\260.md" "b/guide/\347\242\260\344\270\200\347\242\260.md" deleted file mode 100755 index 8013e668f321b75f666f1e17f453b2d6b9242fb6..0000000000000000000000000000000000000000 --- "a/guide/\347\242\260\344\270\200\347\242\260.md" +++ /dev/null @@ -1,5 +0,0 @@ -# 碰一碰 - -- **[碰一碰场景开发指导](碰一碰场景开发指导.md)** - - diff --git "a/guide/\347\242\260\344\270\200\347\242\260\345\234\272\346\231\257\345\274\200\345\217\221\346\214\207\345\257\274.md" "b/guide/\347\242\260\344\270\200\347\242\260\345\234\272\346\231\257\345\274\200\345\217\221\346\214\207\345\257\274.md" deleted file mode 100755 index b9f8d798541d205134aecf7638e2a1775ebbfeb6..0000000000000000000000000000000000000000 --- "a/guide/\347\242\260\344\270\200\347\242\260\345\234\272\346\231\257\345\274\200\345\217\221\346\214\207\345\257\274.md" +++ /dev/null @@ -1,20 +0,0 @@ -# 碰一碰场景开发指导 - -开发碰一碰拉起FA体验的WLAN连接类产品需遵循以下开发步骤。 - -1. 购买Hi3861开发板,用WLAN模组做硬件开发。 - - >![](public_sys-resources/icon-note.gif) **说明:** - >如果需要做驱动开发或者芯片移植,可以到OpenHarmony开源社区获取代码自行修改,参考HDF驱动子系统开发指导进行开发。芯片移植指引预计10月提供。 - -2. 用HarmonyOS应用开发IDE编写PA/FA应用,下载HUAWEI DevEco Studio工具。 - - >![](public_sys-resources/icon-note.gif) **说明:** - >可在HUAWEI DevEco Studio编写应用程序。暂时可支持智能表/智慧屏的模拟器查看PA/FA界面模拟运行效果。 - -3. 准备EMUI 11.0及以上版本的手机。 - - >![](public_sys-resources/icon-note.gif) **说明:** - >碰一碰分布式拉起FA能力包预计与EMUI 11的手机同步开放,希望率先体验的开发者发邮件到harmonytc@huawei.com提出申请。 - - diff --git "a/guide/\347\244\272\344\276\213\345\274\200\345\217\221-2.md" "b/guide/\347\244\272\344\276\213\345\274\200\345\217\221-2.md" index f468854771f1d7def8baaa038e8d66c6f74275da..eb01bec406aeb05019daeeb14148eaf702bfc598 100755 --- "a/guide/\347\244\272\344\276\213\345\274\200\345\217\221-2.md" +++ "b/guide/\347\244\272\344\276\213\345\274\200\345\217\221-2.md" @@ -1,9 +1,5 @@ # 示例开发 -相机开发基本概念可参考:[相机开发概述](../subsystems/相机开发概述.md)。 - -若开发者想先查看示例效果,可先跳过本节,进入[应用实例](应用实例-5.md)。如需自定义应用行为,可参考本节描述对示例代码进行修改。 - - **[拍照开发指导](拍照开发指导-3.md)** - **[录像开发指导](录像开发指导-4.md)** diff --git "a/guide/\347\244\272\344\276\213\345\274\200\345\217\221.md" "b/guide/\347\244\272\344\276\213\345\274\200\345\217\221.md" index 02a8888db1eea3313a4dec00aa714d84e40060e2..e8128ecaaca0f0b5311d51b7e8f1cd8dc01c00a0 100755 --- "a/guide/\347\244\272\344\276\213\345\274\200\345\217\221.md" +++ "b/guide/\347\244\272\344\276\213\345\274\200\345\217\221.md" @@ -1,9 +1,5 @@ # 示例开发 -相机开发基本概念可参考:[相机开发概述](../subsystems/相机开发概述.md)。 - -若开发者想先查看示例效果,可先跳过本节,进入[应用实例](应用实例.md)。如需自定义应用行为,可参考本节描述对示例代码进行修改。 - - **[拍照开发指导](拍照开发指导.md)** - **[录像开发指导](录像开发指导.md)** diff --git "a/guide/\350\256\276\345\244\207\350\231\232\346\213\237\345\214\226.md" "b/guide/\350\256\276\345\244\207\350\231\232\346\213\237\345\214\226.md" deleted file mode 100755 index 955af1929a02aee2ee5abba26e8fb64a4318ecb3..0000000000000000000000000000000000000000 --- "a/guide/\350\256\276\345\244\207\350\231\232\346\213\237\345\214\226.md" +++ /dev/null @@ -1,5 +0,0 @@ -# 设备虚拟化 - -- **[设备虚拟化场景开发指导](设备虚拟化场景开发指导.md)** - - diff --git "a/guide/\350\256\276\345\244\207\350\231\232\346\213\237\345\214\226\345\234\272\346\231\257\345\274\200\345\217\221\346\214\207\345\257\274.md" "b/guide/\350\256\276\345\244\207\350\231\232\346\213\237\345\214\226\345\234\272\346\231\257\345\274\200\345\217\221\346\214\207\345\257\274.md" deleted file mode 100755 index 058f6603691e6f69949c2895fe63f5a98fc90f9d..0000000000000000000000000000000000000000 --- "a/guide/\350\256\276\345\244\207\350\231\232\346\213\237\345\214\226\345\234\272\346\231\257\345\274\200\345\217\221\346\214\207\345\257\274.md" +++ /dev/null @@ -1,20 +0,0 @@ -# 设备虚拟化场景开发指导 - -开发设备虚拟化类产品需遵循以下开发步骤。 - -1. 购买Hi3518开发板,开始HiLink SDK接口适配。 - - >![](public_sys-resources/icon-note.gif) **说明:** - >HiLink SDK预计12月提供。 - -2. DVKit SDK接口适配,通过开发板的媒体接口,获取音视频数据流。 - - >![](public_sys-resources/icon-note.gif) **说明:** - >DVKit SDK预计12月提供。 - -3. 准备EMUI 11.0及以上版本的手机。 - - >![](public_sys-resources/icon-note.gif) **说明:** - >希望率先体验的开发者发邮件到harmonytc@huawei.com提出申请。 - - diff --git "a/guide/\351\233\206\346\210\220\344\270\211\346\226\271SDK.md" "b/guide/\351\233\206\346\210\220\344\270\211\346\226\271SDK.md" index 55ec8a7d341bebf4865e73ce67aa8f53068f34f8..ffa0ce0f6362f5f00392bc06394c030f957a2570 100755 --- "a/guide/\351\233\206\346\210\220\344\270\211\346\226\271SDK.md" +++ "b/guide/\351\233\206\346\210\220\344\270\211\346\226\271SDK.md" @@ -284,7 +284,7 @@ lite_component("link") { include_dirs = [ "//utils/native/lite/include", - "//vendor/hisi/wifi-iot/hi3861/3rd_sdk/demolink" + "//domains/iot/link/libbuild" ] } ``` diff --git "a/guide/\351\242\204\350\247\210\345\274\200\345\217\221\346\214\207\345\257\274-5.md" "b/guide/\351\242\204\350\247\210\345\274\200\345\217\221\346\214\207\345\257\274-5.md" deleted file mode 100755 index fd7d107ff818f208ea1edd8c2a4423a9b55228b9..0000000000000000000000000000000000000000 --- "a/guide/\351\242\204\350\247\210\345\274\200\345\217\221\346\214\207\345\257\274-5.md" +++ /dev/null @@ -1,38 +0,0 @@ -# 预览开发指导 - -## 使用场景 - -使用camera产生视频流并播放。 - -## 接口说明 - -参考[接口说明](拍照开发指导-3.md#zh-cn_topic_0000001052170554_section56549532016)。 - -## 约束与限制 - -无。 - -## 开发步骤 - -1. 参考[拍照开发指导](拍照开发指导-3.md#section7152245183619)中步骤1、步骤2、步骤3、步骤4。 -2. 设置预览显示的区域。 - - ``` - Surface *surface = Surface::CreateSurface(); - /* 设置显示区域 */ - surface->SetUserData("region_position_x", "480"); // 矩形左上角横坐标 - surface->SetUserData("region_position_y", "270"); // 矩形左上角纵坐标 - surface->SetUserData("region_width", "960"); // 宽 - surface->SetUserData("region_height", "540"); // 高 - - fc->AddSurface(*surface); - ``` - -3. 开始和结束预览。 - - ``` - stateCallback->camera_->TriggerLoopingCapture(*fc); // start previewing - stateCallback->camera_->StopLoopingCapture(); // stop previewing - ``` - - diff --git "a/guide/\351\252\214\350\257\201.md" "b/guide/\351\252\214\350\257\201.md" index b4c5645802dd41f91948d7b70db3da2450c4e859..d3239ead0ccc47ffc8326765d5e69080ba17e529 100755 --- "a/guide/\351\252\214\350\257\201.md" +++ "b/guide/\351\252\214\350\257\201.md" @@ -1,6 +1,6 @@ # 验证 -编译过程请参考《[Hi3861快速入门-源码编译](../quick-start/Hi3861开发板第一个示例程序.md#section1736014117148)》,烧录过程请参考《[Hi3861快速入门-镜像烧录](../quick-start/Hi3861开发板第一个示例程序.md#section1610612214150)》。 +编译过程请参考《[Hi3861快速入门-源码编译](../quick-start/Hi3861开发板第一个示例程序.md)》,烧录过程请参考《[Hi3861快速入门-镜像烧录](../quick-start/Hi3861开发板第一个示例程序.md)》。 完成以上两步后,按下RST键复位模组,可发现LED在周期性闪烁,与预期相符,验证完毕。 diff --git "a/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\344\273\213\347\273\215.md" "b/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\344\273\213\347\273\215.md" index 15ea8c7a511baf11b254088d99020d1f920b7d5c..82a271c017c37b9d872b924afc791c3d6c164ef8 100755 --- "a/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\344\273\213\347\273\215.md" +++ "b/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\344\273\213\347\273\215.md" @@ -11,7 +11,7 @@ Hi3861 WLAN模组是一片大约2cm\*5cm大小的开发板,是一款高度集 另外,Hi3861 WLAN模组还可以通过与Hi3861底板连接,扩充自身的外设能力,底板如下图所示。 -**图 2** Hi3861底板外观图 +**图 2** Hi3861底板外观图 ![](figures/Hi3861底板外观图.png "Hi3861底板外观图") - RF电路包括功率放大器PA(Power Amplifier)、低噪声放大器LNA(Low Noise Amplifier)、RF Balun、天线开关以及电源管理等模块;支持20MHz标准带宽和5MHz/10MHz窄带宽,提供最大72.2Mbit/s物理层速率。 @@ -34,44 +34,44 @@ Hi3861 WLAN模组资源十分有限,整板共2MB FLASH,352KB RAM。在编写 **表 1** Hi3861 WLAN模组规格清单 -

适用开发板

版本信息

+

下载内容

下载站点

+

版本信息

SHA256校验码

+

下载站点

+

SHA256校验码

llvm

+

Hi3861

+

gcc_riscv32

9.0.0-34042

+

7.3.0

站点

+

站点

64a518b50422b6f1ba8f6f56a5e303fb8448a311211ba10c385ad307a1d2546f

+

966fd4fda68d9886b828e6eef3ac3620806a34d3bccba4020a2ef07d9b8b8826

gcc_riscv32

+

Hi3861、Hi3516、Hi3518

+

gn

7.3.0

+

1523

站点

+

站点

614ee086ead1a4fd7384332b85dd62707801f323de60dfdb61503f473d470a24

+

50a5a5ba5877dd0ec8afcb23d3dd4d966a16403c29cd80a4002230241d32ef34

gn

+

Hi3861、Hi3516、Hi3518

1523

+

ninja

站点

+

1.9.0

50a5a5ba5877dd0ec8afcb23d3dd4d966a16403c29cd80a4002230241d32ef34

+

站点

+

b4a4ba21e94ff77634e1f88697a00b6f498fdbc0b40d7649df1b246b285874f9

ninja

+

Hi3516、Hi3518

+

llvm

1.9.0

+

9.0.0-34042

站点

+

站点

b4a4ba21e94ff77634e1f88697a00b6f498fdbc0b40d7649df1b246b285874f9

+

64a518b50422b6f1ba8f6f56a5e303fb8448a311211ba10c385ad307a1d2546f

hc-gen

+

Hi3516、Hi3518

+

hc-gen

0.65

+

0.65

站点

+

站点

fcfee489371947a464fe41a4b45a897b9a44155891a957f15bad2e157c750162

+

fcfee489371947a464fe41a4b45a897b9a44155891a957f15bad2e157c750162

规格类型

+ - - - - - - - - - - - - - @@ -137,7 +137,7 @@ OpenHarmony基于Hi3861平台提供了多种开放能力,提供的关键组件 -

规格类型

规格清单

+

规格清单

通用规格

+

通用规格

  • 1×1 2.4GHz频段(ch1~ch14)
  • PHY支持IEEE 802.11b/g/n
  • MAC支持IEEE802.11 d/e/h/i/k/v/w
+
  • 1×1 2.4GHz频段(ch1~ch14)
  • PHY支持IEEE 802.11b/g/n
  • MAC支持IEEE802.11 d/e/h/i/k/v/w
  • 内置PA和LNA,集成TX/RX Switch、Balun等
  • 支持STA和AP形态,作为AP时最大支持6 个STA接入
  • 支持WFA WPA/WPA2 personal、WPS2.0
  • 支持与BT/BLE芯片共存的2/3/4 线PTA方案
  • 电源电压输入范围:2.3V~3.6V
  • IO电源电压支持1.8V和3.3V
  • 支持RF自校准方案
  • 低功耗:
    • Ultra Deep Sleep模式:5μA@3.3V
    • DTIM1:1.5mA@3.3V
    • DTIM3:0.8mA@3.3V

PHY特性

+

PHY特性

  • 支持IEEE802.11b/g/n单天线所有的数据速率
  • 支持最大速率:72.2Mbps@HT20 MCS7
  • 支持标准20MHz带宽和5M/10M窄带宽
  • 支持STBC
  • 支持Short-GI
+
  • 支持IEEE802.11b/g/n单天线所有的数据速率
  • 支持最大速率:72.2Mbps@HT20 MCS7
  • 支持标准20MHz带宽和5M/10M窄带宽
  • 支持STBC
  • 支持Short-GI

MAC特性

+

MAC特性

  • 支持A-MPDU,A-MSDU
  • 支持Blk-ACK
  • 支持QoS,满足不同业务服务质量需求
+
  • 支持A-MPDU,A-MSDU
  • 支持Blk-ACK
  • 支持QoS,满足不同业务服务质量需求

CPU子系统

+

CPU子系统

  • 高性能 32bit微处理器,最大工作频率160MHz
  • 内嵌SRAM 352KB、ROM 288KB
  • 内嵌 2MB Flash
+
  • 高性能 32bit微处理器,最大工作频率160MHz
  • 内嵌SRAM 352KB、ROM 288KB
  • 内嵌 2MB Flash

外围接口

+

外围接口

  • 1个SDIO接口、2个SPI接口、2个I2C接口、3个UART接口、15个GPIO接口、7路ADC输入、6路PWM、1个I2S接口(注:上述接口通过复用实现)
  • 外部主晶体频率40M或24M
+
  • 1个SDIO接口、2个SPI接口、2个I2C接口、3个UART接口、15个GPIO接口、7路ADC输入、6路PWM、1个I2S接口(注:上述接口通过复用实现)
  • 外部主晶体频率40M或24M

其他信息

+

其他信息

  • 封装:QFN-32,5mm×5mm
  • 工作温度:-40℃ ~ +85℃
+
  • 封装:QFN-32,5mm×5mm
  • 工作温度:-40℃ ~ +85℃

DFX

提供DFX能力。包括:流水日志、事件打点等。

+

提供DFX能力。包括:流水日志、时间打点等。

XTS

diff --git "a/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\347\254\254\344\270\200\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" "b/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\347\254\254\344\270\200\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" index 38f43f223e4217d404ce2c9e1334474df683179d..9469aa05da24ffb7d3354cb454e24325933db7ee 100755 --- "a/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\347\254\254\344\270\200\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" +++ "b/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\347\254\254\344\270\200\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" @@ -77,7 +77,7 @@ Hi3861 WLAN模组的镜像烧录可以通过OpenHarmony IDE工具DevEco完成, ![](figures/zh-cn_image_0000001055427138.png) -4. 在DecEco工具界面中单击“烧录”按钮![](figures/zh-cn_image_0000001054443694.png),然后选择烧录串口“COM11”。 +4. 在DevEco工具界面中单击“烧录”按钮![](figures/zh-cn_image_0000001054443694.png),然后选择烧录串口“COM11”。 **图 10** 烧录启动示意图 ![](figures/烧录启动示意图.png "烧录启动示意图") diff --git "a/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\347\254\254\344\272\214\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" "b/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\347\254\254\344\272\214\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" index 0e69c58d5a60dab29cb95285ec53e43d880fc2c0..1520c4144e0676bd9f45c45c44a30e75612d2e82 100755 --- "a/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\347\254\254\344\272\214\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" +++ "b/quick-start/Hi3861\345\274\200\345\217\221\346\235\277\347\254\254\344\272\214\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" @@ -29,7 +29,7 @@ bugfix和新增业务两种情况,涉及源码修改。下面以新增业务 在hello\_world.c中新建业务入口函数HelloWorld,并实现业务逻辑。并在代码最下方,使用OpenHarmony启动恢复模块接口SYS\_RUN\(\)启动业务。(SYS\_RUN定义在ohos\_init.h文件中) ``` - #include + #include #include "ohos_init.h" #include "ohos_types.h" diff --git "a/quick-start/figures/Hi3516\345\215\225\346\235\277\350\203\214\351\235\242\345\244\226\350\247\202\345\233\276.png" "b/quick-start/figures/Hi3516\345\215\225\346\235\277\350\203\214\351\235\242\345\244\226\350\247\202\345\233\276.png" deleted file mode 100755 index 2e2dc3b4ae6c08c044068545b23062955d8272f8..0000000000000000000000000000000000000000 Binary files "a/quick-start/figures/Hi3516\345\215\225\346\235\277\350\203\214\351\235\242\345\244\226\350\247\202\345\233\276.png" and /dev/null differ diff --git "a/quick-start/figures/Hi3861WLAN\346\250\241\347\273\204\345\244\226\350\247\202\345\233\276.png" "b/quick-start/figures/Hi3861WLAN\346\250\241\347\273\204\345\244\226\350\247\202\345\233\276.png" deleted file mode 100755 index 508041f2c1f9795def76a85f56c6209677fa0d03..0000000000000000000000000000000000000000 Binary files "a/quick-start/figures/Hi3861WLAN\346\250\241\347\273\204\345\244\226\350\247\202\345\233\276.png" and /dev/null differ diff --git "a/quick-start/figures/Hi3861\345\212\237\350\203\275\346\241\206\345\233\276.png" "b/quick-start/figures/Hi3861\345\212\237\350\203\275\346\241\206\345\233\276.png" deleted file mode 100755 index b66822e6b6c924e01f8136227c408fc4fc54271d..0000000000000000000000000000000000000000 Binary files "a/quick-start/figures/Hi3861\345\212\237\350\203\275\346\241\206\345\233\276.png" and /dev/null differ diff --git "a/quick-start/figures/Hi3861\345\272\225\346\235\277\345\244\226\350\247\202\345\233\276.png" "b/quick-start/figures/Hi3861\345\272\225\346\235\277\345\244\226\350\247\202\345\233\276.png" index a31883d73e33ff8cf10e7d6c2de4beb1aa169f97..faf3755c54f22a8b4959e9c91f24d341b9d4e8e3 100755 Binary files "a/quick-start/figures/Hi3861\345\272\225\346\235\277\345\244\226\350\247\202\345\233\276.png" and "b/quick-start/figures/Hi3861\345\272\225\346\235\277\345\244\226\350\247\202\345\233\276.png" differ diff --git "a/quick-start/figures/WLAN\346\250\241\347\273\204\350\201\224\347\275\221\346\210\220\345\212\237\347\244\272\346\204\217\345\233\276.png" "b/quick-start/figures/WLAN\346\250\241\347\273\204\350\201\224\347\275\221\346\210\220\345\212\237\347\244\272\346\204\217\345\233\276.png" deleted file mode 100755 index fb08d131d721f7f9850ee4a53b379554fc3181c0..0000000000000000000000000000000000000000 Binary files "a/quick-start/figures/WLAN\346\250\241\347\273\204\350\201\224\347\275\221\346\210\220\345\212\237\347\244\272\346\204\217\345\233\276.png" and /dev/null differ diff --git "a/quick-start/figures/putty\345\256\211\345\205\250\345\221\212\350\255\246\347\225\214\351\235\242\345\233\276.png" "b/quick-start/figures/putty\345\256\211\345\205\250\345\221\212\350\255\246\347\225\214\351\235\242\345\233\276.png" deleted file mode 100755 index 283a1d1afc63077a302df0288ae686ae2a54be87..0000000000000000000000000000000000000000 Binary files "a/quick-start/figures/putty\345\256\211\345\205\250\345\221\212\350\255\246\347\225\214\351\235\242\345\233\276.png" and /dev/null differ diff --git a/quick-start/figures/zh-cn_image_0000001054087868.png b/quick-start/figures/zh-cn_image_0000001054087868.png deleted file mode 100755 index 4b697b65879025fafe5b6133df971a11773cbf14..0000000000000000000000000000000000000000 Binary files a/quick-start/figures/zh-cn_image_0000001054087868.png and /dev/null differ diff --git a/quick-start/figures/zh-cn_image_0000001054099459.png b/quick-start/figures/zh-cn_image_0000001054099459.png deleted file mode 100755 index 8f3708a8a658a3c9edcd0131115ded0c3a4a4840..0000000000000000000000000000000000000000 Binary files a/quick-start/figures/zh-cn_image_0000001054099459.png and /dev/null differ diff --git a/quick-start/figures/zh-cn_image_0000001055674130.png b/quick-start/figures/zh-cn_image_0000001055012855.png old mode 100755 new mode 100644 similarity index 100% rename from quick-start/figures/zh-cn_image_0000001055674130.png rename to quick-start/figures/zh-cn_image_0000001055012855.png diff --git a/quick-start/figures/zh-cn_image_0000001055073889.png b/quick-start/figures/zh-cn_image_0000001055172843.png old mode 100755 new mode 100644 similarity index 100% rename from quick-start/figures/zh-cn_image_0000001055073889.png rename to quick-start/figures/zh-cn_image_0000001055172843.png diff --git a/quick-start/figures/zh-cn_image_0000001054713955.png b/quick-start/figures/zh-cn_image_0000001055372855.png old mode 100755 new mode 100644 similarity index 100% rename from quick-start/figures/zh-cn_image_0000001054713955.png rename to quick-start/figures/zh-cn_image_0000001055372855.png diff --git "a/quick-start/figures/\344\270\262\345\217\243\345\217\202\346\225\260\351\205\215\347\275\256\347\244\272\346\204\217\345\233\276.png" "b/quick-start/figures/\344\270\262\345\217\243\345\217\202\346\225\260\351\205\215\347\275\256\347\244\272\346\204\217\345\233\276.png" deleted file mode 100755 index a934f82bf50dd36c605fed836761d33cf72b87b6..0000000000000000000000000000000000000000 Binary files "a/quick-start/figures/\344\270\262\345\217\243\345\217\202\346\225\260\351\205\215\347\275\256\347\244\272\346\204\217\345\233\276.png" and /dev/null differ diff --git "a/quick-start/figures/\345\244\215\344\275\215WLAN\346\250\241\347\273\204\347\244\272\346\204\217\345\233\276.png" "b/quick-start/figures/\345\244\215\344\275\215WLAN\346\250\241\347\273\204\347\244\272\346\204\217\345\233\276.png" deleted file mode 100755 index 164623da06187f49983420a660dadae683273705..0000000000000000000000000000000000000000 Binary files "a/quick-start/figures/\345\244\215\344\275\215WLAN\346\250\241\347\273\204\347\244\272\346\204\217\345\233\276.png" and /dev/null differ diff --git "a/quick-start/figures/\346\211\223\345\274\200DevEco\344\270\262\345\217\243\347\273\210\347\253\257\347\244\272\346\204\217\345\233\276.png" "b/quick-start/figures/\346\211\223\345\274\200DevEco\344\270\262\345\217\243\347\273\210\347\253\257\347\244\272\346\204\217\345\233\276.png" deleted file mode 100755 index 77ee650774eccadccd6e23dd7d950f5d5cdbfcbe..0000000000000000000000000000000000000000 Binary files "a/quick-start/figures/\346\211\223\345\274\200DevEco\344\270\262\345\217\243\347\273\210\347\253\257\347\244\272\346\204\217\345\233\276.png" and /dev/null differ diff --git "a/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230-1.md" "b/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230-1.md" index 1f48e478aed84eb45ad53316ee26e9b5a3964d5f..433883c8f921bef482c57c4baa02730d04129394 100755 --- "a/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230-1.md" +++ "b/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230-1.md" @@ -1,19 +1,19 @@ # 常见问题 -**问题 1:烧写选择串口后提示失败。** +1. 烧写选择串口后提示失败**。** + - **现象描述** -- **现象描述** + 点击烧写并选择串口后,出现Error: Opening COMxx: Access denied。 - 点击烧写并选择串口后,出现Error: Opening COMxx: Access denied。 + **图 1** 打开串口失败图 + ![](figures/打开串口失败图.png "打开串口失败图") - **图 1** 打开串口失败图 - ![](figures/打开串口失败图.png "打开串口失败图") + - **可能原因** -- **可能原因** + 串口已经被占用。 - 串口已经被占用。 + - **解决办法** -- **解决办法** 1. 按图依次选择下拉框,查找带有serial-xx的终端 @@ -31,7 +31,7 @@ ![](figures/重新启动烧写任务.png "重新启动烧写任务") -**问题 2:Windows电脑与单板网络连接失败。** +2. Windows电脑与单板网络连接失败。 - **现象描述** @@ -70,7 +70,7 @@ ![](figures/允许Visual-Studio-Code应用访问网络.png "允许Visual-Studio-Code应用访问网络") -**问题 3:烧写失败** +3. 烧写失败 - **现象描述** @@ -85,14 +85,14 @@ 重启IDE。 -**问题 4:编译构建过程中,提示找不到“pyhton”。** +4. 编译构建过程中,提示找不到“python”。 - **现象描述** ![](figures/zh-cn_image_0000001055035538.png) -- **可能原因**1 +- **可能原因1** 没有装python。 @@ -120,19 +120,19 @@ ![](figures/zh-cn_image_0000001054875562.png) -**问题 5:串口无回显。** +5. 串口无回显。 - **现象描述** 串口显示已连接,重启单板后,回车无任何回显。 -- **可能原因** - - 串口连接错误。 - - 单板U-boot被损坏。 +- **可能原因1** + + 串口连接错误。 - **解决办法** - **解决办法1:修改串口号** + 修改串口号。 请查看设备管理器,确认连接单板的串口与终端中连接串口是否一致,若不一致,请按步骤修改串口号。 @@ -145,9 +145,16 @@ 3. 在弹框中修改串口号并点击OK。 4. 连接后在对话框中输入回车查看是否存在回显。 -**解决办法2:烧写U-boot** +- **可能原因2** + + 单板U-boot被损坏。 + +- **解决办法** + + 烧写U-boot。 + + 若上述步骤依旧无法连接串口,可能由于单板U-boot损坏,按下述步骤烧写U-boot。 -若上述步骤依旧无法连接串口,可能由于单板U-boot损坏,按下述步骤烧写U-boot。 1. 获取引导文件U-boot。 diff --git "a/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230-3.md" "b/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230-3.md" index 1c25ec9d9b047933c8567ede7e9541fe3bd315f4..dfe0c8767e2f21f9bdd0be2f032c36128dcd5d86 100755 --- "a/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230-3.md" +++ "b/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230-3.md" @@ -1,112 +1,121 @@ # 常见问题 -**问题 1:串口无回显。** +1. 串口无回显**。** + - **现象描述** -- **现象描述** + 串口显示已连接,重启单板后,回车无任何回显。 - 串口显示已连接,重启单板后,回车无任何回显。 + - **可能原因1** -- **可能原因** - - 串口连接错误。 - - 单板U-boot被损坏。 + 串口连接错误。 -- **解决办法** + - **解决办法** - **解决办法1:修改串口号** + 修改串口号。 - 请查看设备管理器,确认连接单板的串口与终端中连接串口是否一致,若不一致,请按步骤修改串口号。 + 请查看设备管理器,确认连接单板的串口与终端中连接串口是否一致,若不一致,请按步骤修改串口号。 - **图 1** 修改串口号图示 - ![](figures/修改串口号图示-3.png "修改串口号图示-3") + **图 1** 修改串口号图示 + ![](figures/修改串口号图示-3.png "修改串口号图示-3") + 1. 断开当前串口。 + 2. 点击设置按钮。 + 3. 在弹框中修改串口号并点击OK。 + 4. 连接后在对话框中输入回车查看是否存在回显。 -1. 断开当前串口。 -2. 点击设置按钮。 -3. 在弹框中修改串口号并点击OK。 -4. 连接后在对话框中输入回车查看是否存在回显。 + - **可能原因2** -**解决办法2:烧写U-boot** + 单板U-boot被损坏。 -若上述步骤依旧无法连接串口,可能由于单板U-boot损坏,按下述步骤烧写U-boot。 + - **解决办法** -1. 获取引导文件U-boot。 + 烧写U-boot。 - >![](public_sys-resources/icon-notice.gif) **须知:** - >单板的U-boot文件请在开源包中获取,路径为vendor\\hisi\\hi35xx\\hi3518ev300\\uboot\\out\\boot\\u-boot-hi3518ev300.bin + 若上述步骤依旧无法连接串口,可能由于单板U-boot损坏,按下述步骤烧写U-boot。 -2. 使用HiTool工具按照标号选择U-boot烧写选项,点击烧写按钮。 + 1. 获取引导文件U-boot。 - **图 2** HiTool工具U-boot烧写步骤图 - ![](figures/HiTool工具U-boot烧写步骤图.png "HiTool工具U-boot烧写步骤图") + >![](public_sys-resources/icon-notice.gif) **须知:** + >1. 单板的U-boot文件请在开源包中获取,路径为vendor\\hisi\\hi35xx\\hi3518ev300\\uboot\\out\\boot\\u-boot-hi3518ev300.bin - 1. 选择单板串口COM7。 - 2. 选择Transfer Mode为Serial。 - 3. 选择Burn Fastboot标签。 - 4. 选择Flash Type为spi nor。 - 5. 选择Browse,找到对应U-boot文件。 - 6. 点击Burn开始烧写。 + 2. 使用HiTool工具按照标号选择U-boot烧写选项,点击烧写按钮。 -3. 提示下电并给单板重新上电,烧写完成后,连接串口,如下图所示。 + **图 2** HiTool工具U-boot烧写步骤图 + ![](figures/HiTool工具U-boot烧写步骤图.png "HiTool工具U-boot烧写步骤图") - **图 3** HiTool工具U-boot烧写完成串口显示 - ![](figures/HiTool工具U-boot烧写完成串口显示-4.png "HiTool工具U-boot烧写完成串口显示-4") + 1. 选择单板串口COM7。 + 2. 选择Transfer Mode为Serial。 -**问题 2:HiTool工具烧写时上报如下错误。** + 3. 选择Burn Fastboot标签。 -- **现象描述** + 4. 选择Flash Type为spi nor。 - **图 4** 烧写上报错误图例 - + 5. 选择Browse,找到对应U-boot文件。 - ![](figures/zh-cn_image_0000001054421058.png) + 6. 点击Burn开始烧写。 -- **可能原因** + 3. 提示下电并给单板重新上电,烧写完成后,连接串口,如下图所示。 - HiTool工具选择FLASH器件类型错误。 + **图 3** HiTool工具U-boot烧写完成串口显示 + ![](figures/HiTool工具U-boot烧写完成串口显示-4.png "HiTool工具U-boot烧写完成串口显示-4") -- **解决方法** - 请在图中红色标号1处切换器件类型。 - **图 5** FLASH器件类型错误图例 - +2. HiTool工具烧写时上报如下错误。 + - **现象描述** - ![](figures/zh-cn_image_0000001053941057.png) + **图 4** 烧写上报错误图例 + + ![](figures/zh-cn_image_0000001054421058.png) -**问题 3:编译构建过程中,提示找不到“pyhton”。** + - **可能原因** -- **现象描述** + HiTool工具选择FLASH器件类型错误。 - ![](figures/zh-cn_image_0000001054476115.png) + - **解决方法** + 请在图中红色标号1处切换器件类型。 -- **可能原因**1 + **图 5** FLASH器件类型错误图例 + - 没有装python。 + ![](figures/zh-cn_image_0000001053941057.png) -- **解决办法** - 请按照[安装Python环境](搭建环境-2.md#section918195118487)安装python。 +3. 编译构建过程中,提示找不到“python”。 + - **现象描述** -- **可能原因2** + ![](figures/zh-cn_image_0000001054476115.png) - ![](figures/zh-cn_image_0000001054876092.png) -- **解决办法** + - **可能原因**1 - usr/bin目录下没有python软链接,请运行以下命令: + 没有装python。 - ``` - # cd /usr/bin/ - # which python3 - # ln -s /usr/local/bin/python3 python - # python --version - ``` + - **解决办法** - 例: + 请按照[安装Python环境](搭建环境-2.md#section918195118487)安装python。 + + - **可能原因2** + + ![](figures/zh-cn_image_0000001054876092.png) + + - **解决办法** + + usr/bin目录下没有python软链接,请运行以下命令: + + ``` + # cd /usr/bin/ + # which python3 + # ln -s /usr/local/bin/python3 python + # python --version + ``` + + 例: + + ![](figures/zh-cn_image_0000001055194682.png) - ![](figures/zh-cn_image_0000001055194682.png) diff --git "a/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230.md" "b/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230.md" index cb7ec5029838868e20e72df23186c8980eb90a29..d238679442d48abc14080be761b8c651793d772d 100755 --- "a/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230.md" +++ "b/quick-start/\345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -1,223 +1,230 @@ # 常见问题 -**问题 1:安装python3过程中,提示“configure: error: no acceptable C compiler found in $PATH”。** +1. 安装python3过程中,提示“configure: error: no acceptable C compiler found in $PATH”。 + - **现象描述** -- **现象描述** + ![](figures/zh-cn_image_0000001053782588.png) - ![](figures/zh-cn_image_0000001053782588.png) + - **可能原因** -- **可能原因** + 环境中未安装“gcc”。 - 环境中未安装“gcc”。 + - **解决办法** -- **解决办法** + 1、通过命令“apt-get install gcc”在线安装。 - 1、通过命令“apt-get install gcc”在线安装。 + 2、完成后,重新安装python3。 - 2、完成后,重新安装python3。 +2. 安装python3过程中,提示“-bash: make: command not found”。 + - **现象描述** -**问题 2:安装python3过程中,提示“-bash: make: command not found”。** + ![](figures/zh-cn_image_0000001053302604.png) -- **现象描述** + - **可能原因** - ![](figures/zh-cn_image_0000001053302604.png) + 环境中未安装“make”。 -- **可能原因** + - **解决办法** - 环境中未安装“make”。 + 1、通过命令“apt-get install make”在线安装。 -- **解决办法** + 2、完成后,重新安装python3。 - 1、通过命令“apt-get install make”在线安装。 - 2、完成后,重新安装python3。 +3. 安装python3过程中,提示“zlib not available”。 + - **现象描述** + ![](figures/zh-cn_image_0000001053183929.png) -**问题 3:安装python3过程中,提示“zlib not available”。** + - **可能原因** -- **现象描述** + 环境中未安装“zlib”。 - ![](figures/zh-cn_image_0000001053183929.png) + - **解决办法** -- **可能原因** + 方法1:通过命令“apt-get install zlib”在线安装。 - 环境中未安装“zlib”。 + 方法2:如果软件源中没有该软件,请从“www.zlib.net”下载版本代码,并离线安装。 -- **解决办法** + ![](figures/10.png) - 方法1:通过命令“apt-get install zlib”在线安装。 + 完成下载后,通过以下命令安装: - 方法2:如果软件源中没有该软件,请从“www.zlib.net”下载版本代码,并离线安装。 + ``` + # tar xvf zlib-1.2.11.tar.gz + # cd zlib-1.2.11 + # ./configure + # make && make install + ``` - ![](figures/10.png) + 完成后,重新安装python3。 - 安装命令 - tar xvf zlib-1.2.11.tar.gz +4. 安装python3过程中,提示“No module named '\_ctypes'”**。** + - **现象描述** - cd zlib-1.2.11 + ![](figures/zh-cn_image_0000001052623895.png) - ./configure - make && make install + - **可能原因** - 3、完成后,重新安装python3 + 环境中未安装“libffi”和“libffi-devel”。 -**问题 4:安装python3过程中,提示“No module named '\_ctypes'”。** + - **解决办法** -- **现象描述** + 1、通过命令“apt-get install libffi\* -y”,在线安装。 - ![](figures/zh-cn_image_0000001052623895.png) + 2、完成后,重新安装python3。 -- **可能原因** +5. 编译构建过程中,提示“No module named 'Crypto'”。 + - **现象描述** - 环境中未安装“libffi”和“libffi-devel”。 + ![](figures/zh-cn_image_0000001052983874.png) -- **解决办法** + - **可能原因** - 1、通过命令“apt-get install libffi\* -y”,在线安装。 + 环境中未安装“Crypto”。 - 2、完成后,重新安装python3。 + - **解决办法** -**问题 5:编译构建过程中,提示“No module named 'Crypto'”。** + 方法1:通过命令“pip3 install Crypto”,在线安装。 -- **现象描述** + 方法2:离线安装 - ![](figures/zh-cn_image_0000001052983874.png) + 通过网页[https://pypi.org/project/pycrypto/\#files](https://pypi.org/project/pycrypto/#files),下载源码。 + ![](figures/zh-cn_image_0000001053462612.png) -- **可能原因** + 将源码放置在Linux服务器中,解压,并安装“python3 setup.py install”。 - 环境中未安装“Crypto”。 + 完成上述安装后,重新构建。 -- **解决办法** +6. 编译构建过程中,提示“No module named 'ecdsa'”。 + - **现象描述** - 方法1:通过命令“pip3 install Crypto”,在线安装。 + ![](figures/18.png) - 方法2:离线安装 - 通过网页[https://pypi.org/project/pycrypto/\#files](https://pypi.org/project/pycrypto/#files),下载源码。 + - **可能原因** - ![](figures/zh-cn_image_0000001053462612.png) + 环境中未安装“ecdsa”。 - 将源码放置在Linux服务器中,解压,并安装“python3 setup.py install”。 - 完成上述安装后,重新构建。 + - **解决办法** + 方法1:通过命令“pip3 install ecdsa”,在线安装。 -**问题 6:编译构建过程中,提示“No module named 'ecdsa'”。** + 方法2:离线安装 -- **现象描述** + 通过网页[https://pypi.org/project/ecdsa/\#files](https://pypi.org/project/ecdsa/#files),下载安装包。 - ![](figures/18.png) + ![](figures/zh-cn_image_0000001053022609.png) + 将安装包放置Linux服务器中,并安装“pip3 install ecdsa-0.15-py2.py3-none-any.whl”。 -- **可能原因** + 完成上述安装后,重新构建。 - 环境中未安装“ecdsa”。 +7. 编译构建过程中,提示“Could not find a version that satisfies the requirement six\>=1.9.0”。 + - **现象描述** -- **解决办法** + ![](figures/zh-cn_image_0000001052862621.png) - 方法1:通过命令“pip3 install ecdsa”,在线安装。 - 方法2:离线安装 + - **可能原因** - 通过网页[https://pypi.org/project/ecdsa/\#files](https://pypi.org/project/ecdsa/#files),下载安装包。 + 环境中未安装合适的“six”。 - ![](figures/zh-cn_image_0000001053022609.png) - 将安装包放置Linux服务器中,并安装“pip3 install ecdsa-0.15-py2.py3-none-any.whl”。 + - **解决办法** - 完成上述安装后,重新构建。 + 方法1:通过命令“pip3 install six”,在线安装。 + 方法2:离线安装 -**问题 7:编译构建过程中,提示“Could not find a version that satisfies the requirement six\>=1.9.0”。** + 通过网页[https://pypi.org/project/six/\#files](https://pypi.org/project/six/#files),下载安装包。 -- **现象描述** + ![](figures/zh-cn_image_0000001052742629.png) - ![](figures/zh-cn_image_0000001052862621.png) + 将源码放置在Linux服务器中,并安装“pip3 install six-1.14.0-py2.py3-none-any.whl”。 + 完成上述安装后,重新构建。 -- **可能原因** - 环境中未安装合适的“six”。 +8. 编译构建过程中,提示找不到“-lgcc”。 + - **现象描述** + ![](figures/zh-cn_image_0000001053142611.png) -- **解决办法** - 方法1:通过命令“pip3 install six”,在线安装。 + - **可能原因** - 方法2:离线安装 + 交叉编译器gcc\_riscv32的PATH添加错误,如图。多添加了一个“/”,应该删除。 - 通过网页[https://pypi.org/project/six/\#files](https://pypi.org/project/six/#files),下载安装包。 + ![](figures/zh-cn_image_0000001055682654.png) - ![](figures/zh-cn_image_0000001052742629.png) - 将源码放置在Linux服务器中,并安装“pip3 install six-1.14.0-py2.py3-none-any.whl”。 + - **解决办法** - 完成上述安装后,重新构建。 + 重新修改gcc\_riscv32的PATH,将多余的“/”删除。 + ![](figures/zh-cn_image_0000001054804171.png) -**问题 8:编译构建过程中,提示找不到“-lgcc”。** -- **现象描述** +9. 编译构建过程中,提示找不到“python”。 + - **现象描述** - ![](figures/zh-cn_image_0000001053142611.png) + ![](figures/zh-cn_image_0000001055172843.png) -- **可能原因** + - **可能原因**1 - 交叉编译器gcc\_riscv32的PATH添加错误,如图。多添加了一个“/”,应该删除。 + 没有装python。 - ![](figures/zh-cn_image_0000001055682654.png) + - **解决办法** + 请按照[安装Python环境](搭建环境.md#section126831816258)安装python。 -- **解决办法** + - **可能原因2** - 重新修改gcc\_riscv32的PATH,将多余的“/”删除。 + ![](figures/zh-cn_image_0000001055372855.png) - ![](figures/zh-cn_image_0000001054804171.png) + - **解决办法** + usr/bin目录下没有python软链接,请运行以下命令: -**问题 9:编译构建过程中,提示找不到“pyhton”。** + ``` + # cd /usr/bin/ + # which python3 + # ln -s /usr/local/bin/python3 python + # python --version + ``` -- **现象描述** + 例: - ![](figures/zh-cn_image_0000001055073889.png) + ![](figures/zh-cn_image_0000001055012855.png) -- **可能原因**1 +10. 安装 kconfiglib时,遇到lsb\_release错误。 + - **现象描述** - 没有装python。 + 安装kconfiglib过程中,遇到如下错误打印: -- **解决办法** + "subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1." - 请按照[安装Python环境](搭建环境.md#section126831816258)安装python。 + - **可能原因** -- **可能原因2** + lsb\_release模块基于的python版本与现有python版本不一致 - ![](figures/zh-cn_image_0000001054713955.png) + - **解决办法** -- **解决办法** + 执行"find / -name lsb\_release",找到lsb\_release位置并删除,如:"sudo rm -rf /usr/bin/lsb\_release" - usr/bin目录下没有python软链接,请运行以下命令: - - ``` - # cd /usr/bin/ - # which python3 - # ln -s /usr/local/bin/python3 python - # python --version - ``` - - 例: - - ![](figures/zh-cn_image_0000001055674130.png) diff --git "a/quick-start/\345\274\200\345\217\221Hi3516\347\254\254\344\270\200\344\270\252\345\272\224\347\224\250\347\250\213\345\272\217\347\244\272\344\276\213.md" "b/quick-start/\345\274\200\345\217\221Hi3516\347\254\254\344\270\200\344\270\252\345\272\224\347\224\250\347\250\213\345\272\217\347\244\272\344\276\213.md" index 7f9664f156b0067ff3641768d3869c06cecc0d3d..9fc51db64aa417a6a731438d903dcc6c022c3de8 100755 --- "a/quick-start/\345\274\200\345\217\221Hi3516\347\254\254\344\270\200\344\270\252\345\272\224\347\224\250\347\250\213\345\272\217\347\244\272\344\276\213.md" +++ "b/quick-start/\345\274\200\345\217\221Hi3516\347\254\254\344\270\200\344\270\252\345\272\224\347\224\250\347\250\213\345\272\217\347\244\272\344\276\213.md" @@ -47,7 +47,7 @@ python build.py ipcamera_hi3516dv300 -b debug ![](figures/驱动安装成功图.png "驱动安装成功图") 1. 为单板上电,并将单板串口线连接Windows工作台。 - 2. 安装驱动,驱动[获取链接](http://www.hihope.org/download)。 + 2. 安装驱动。 3. 打开电脑的设备管理器,查看并记录“Prolific USB-to-Serial Comm Port”串口号,此处为COM11。 驱动安装成功后,若设备图标存在警示图标,请右键点击设备并卸载驱动后,重新安装驱动,并按提示重启电脑。 @@ -131,32 +131,42 @@ python build.py ipcamera_hi3516dv300 -b debug >![](public_sys-resources/icon-notice.gif) **须知:** >U-boot引导程序默认会有2秒的等待时间,用户可使用回车打断等待并显示"hisillicon",通过**reset**命令可再次启动系统。 - **表 1** U-boot启动参数 + **表 1** U-boot修改命令 - -

执行命令

+ + - + + + + - - + + + + + + +

执行命令

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

saveenv

-

reset

+

命令解释

+

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

+

读取FLASH起始地址为0x800(单位为512B,即1MB),大小为0x4800(单位为512B,即9MB)的内容到0x80000000的内存地址。

命令解释

-

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800;go 0x80000000";

-

表示选择FLASH器件0,读取FLASH起始地址为0x800(单位为512B,即1MB),大小为0x4800(单位为512B,即9MB)的内容到0x80000000的内存地址。

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

表示设置启动参数,输出模式为串口输出,波特率为115200,数据位8,rootfs挂载于emmc器件,文件系统类型为vfat,

-

“rootaddr=10M rootsize=15M rw”处对应填入rootfs.img的烧写起始位置与长度,此处与IDE中新增rootfs.img文件时所填大小必须相同

-

saveenv;表示保存当前配置。

-

reset;表示复位单板

-

[可选]“go 0x80000000”默认配置已将指令固化在启动参数中,单板复位后可自动启动。若想切换为手动启动,可在U-boot启动倒数阶段使用"回车"打断自动启动。

+

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

+

表示设置启动参数,输出模式为串口输出,波特率为115200,数据位8,rootfs挂载于emmc器件,文件系统类型为vfat,

+

“rootaddr=10M rootsize=15M rw”处对应填入rootfs.img的烧写起始位置与长度,此处与IDE中新增rootfs.img文件时所填大小必须相同

+

saveenv

+

表示保存当前配置。

+

reset

+

表示复位单板。

+ >![](public_sys-resources/icon-notice.gif) **须知:** + >**“go 0x80000000”**为可选指令,默认配置已将该指令固化在启动参数中,单板复位后可自动启动。若想切换为手动启动,可在U-boot启动倒数阶段使用"回车"打断自动启动。 + 3. 输入**“reset”**指令并回车,重启单板,启动成功如下图,输入回车串口显示OHOS字样。 **图 10** 系统启动图 diff --git "a/quick-start/\345\274\200\345\217\221Hi3516\347\254\254\344\270\200\344\270\252\351\251\261\345\212\250\347\250\213\345\272\217\347\244\272\344\276\213.md" "b/quick-start/\345\274\200\345\217\221Hi3516\347\254\254\344\270\200\344\270\252\351\251\261\345\212\250\347\250\213\345\272\217\347\244\272\344\276\213.md" index 8d272e49f9ca04d73d66feac68cc5e6aca3533b8..d8040393a0aa4bd52ae299c04f0f3e6775dba38e 100755 --- "a/quick-start/\345\274\200\345\217\221Hi3516\347\254\254\344\270\200\344\270\252\351\251\261\345\212\250\347\250\213\345\272\217\347\244\272\344\276\213.md" +++ "b/quick-start/\345\274\200\345\217\221Hi3516\347\254\254\344\270\200\344\270\252\351\251\261\345\212\250\347\250\213\345\272\217\347\244\272\344\276\213.md" @@ -64,17 +64,26 @@ ``` // 绑定UART驱动接口到HDF框架 - static int32_t HdfUartSampleBind(struct HdfDeviceObject *device) + static int32_t SampleUartDriverBind(struct HdfDeviceObject *device) { + struct UartHost *uartHost = NULL; + if (device == NULL) { return HDF_ERR_INVALID_OBJECT; } HDF_LOGI("Enter %s:", __func__); - return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; + + uartHost = UartHostCreate(device); + if (uartHost == NULL) { + HDF_LOGE("%s: UartHostCreate failed", __func__); + return HDF_FAILURE; + } + uartHost->service.Dispatch = SampleDispatch; + return HDF_SUCCESS; } // 从UART驱动的HCS中获取配置信息 - static uint32_t UartDeviceGetResource( + static uint32_t GetUartDeviceResource( struct UartDevice *device, const struct DeviceResourceNode *resourceNode) { struct UartResource *resource = &device->resource; @@ -84,7 +93,7 @@ HDF_LOGE("DeviceResourceIface is invalid"); return HDF_FAILURE; } - + if (dri->GetUint32(resourceNode, "num", &resource->num, 0) != HDF_SUCCESS) { HDF_LOGE("uart config read num fail"); return HDF_FAILURE; @@ -93,7 +102,7 @@ HDF_LOGE("uart config read base fail"); return HDF_FAILURE; } - resource->physBase = (unsigned long) OsalIoRemap(resource->base, 0x48); + resource->physBase = (unsigned long)OsalIoRemap(resource->base, 0x48); if (resource->physBase == 0) { HDF_LOGE("uart config fail to remap physBase"); return HDF_FAILURE; @@ -126,7 +135,7 @@ } // 将UART驱动的配置和接口附加到HDF驱动框架 - static int32_t SampleAttach(struct UartHost *host, struct HdfDeviceObject *device) + static int32_t AttachUartDevice(struct UartHost *host, struct HdfDeviceObject *device) { int32_t ret; struct UartDevice *uartDevice = NULL; @@ -134,28 +143,28 @@ HDF_LOGE("%s: property is NULL", __func__); return HDF_FAILURE; } - uartDevice = (struct UartDevice *) OsalMemCalloc(sizeof(struct UartDevice)); + uartDevice = (struct UartDevice *)OsalMemCalloc(sizeof(struct UartDevice)); if (uartDevice == NULL) { HDF_LOGE("%s: OsalMemCalloc uartDevice error", __func__); return HDF_ERR_MALLOC_FAIL; } - ret = UartDeviceGetResource(uartDevice, device->property); + ret = GetUartDeviceResource(uartDevice, device->property); if (ret != HDF_SUCCESS) { - (void) OsalMemFree(uartDevice); + (void)OsalMemFree(uartDevice); return HDF_FAILURE; } host->num = uartDevice->resource.num; host->priv = uartDevice; - UartSampleAddDev(host); // 添加用户态UART设备节点,具体实现见源码uart_dev_sample - return UartDeviceInit(uartDevice); // 初始化UART PL011,具体实现见源码uart_pl011_sample + AddUartDevice(host); + return InitUartDevice(uartDevice); } // 初始化UART驱动 - static int32_t HdfUartSampleInit(struct HdfDeviceObject *device) + static int32_t SampleUartDriverInit(struct HdfDeviceObject *device) { int32_t ret; struct UartHost *host = NULL; - + if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_OBJECT; @@ -166,71 +175,71 @@ HDF_LOGE("%s: host is NULL", __func__); return HDF_FAILURE; } - ret = SampleAttach(host, device); + ret = AttachUartDevice(host, device); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: attach error", __func__); return HDF_FAILURE; } - host->method = &g_uartSampleHostMethod; + host->method = &g_sampleUartHostMethod; return ret; } - static void UartDeviceDeinit(struct UartDevice *device) + static void DeinitUartDevice(struct UartDevice *device) { - struct UartRegisterMap *regMap = (struct UartRegisterMap *) device->resource.physBase; + struct UartRegisterMap *regMap = (struct UartRegisterMap *)device->resource.physBase; /* wait for uart enter idle. */ while (UartPl011IsBusy(regMap)); UartPl011ResetRegisters(regMap); uart_clk_cfg(0, false); - OsalIoUnmap((void *) device->resource.physBase); + OsalIoUnmap((void *)device->resource.physBase); device->state = UART_DEVICE_UNINITIALIZED; } // 解绑并释放UART驱动 - static void SampleDetach(struct UartHost *host) + static void DetachUartDevice(struct UartHost *host) { struct UartDevice *uartDevice = NULL; - + if (host->priv == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return; } uartDevice = host->priv; - UartDeviceDeinit(uartDevice); - (void) OsalMemFree(uartDevice); + DeinitUartDevice(uartDevice); + (void)OsalMemFree(uartDevice); host->priv = NULL; } // 释放UART驱动 - static void HdfUartSampleRelease(struct HdfDeviceObject *device) + static void SampleUartDriverRelease(struct HdfDeviceObject *device) { struct UartHost *host = NULL; HDF_LOGI("Enter %s:", __func__); - + if (device == NULL) { - HDF_LOGE("%s: device is null", __func__); + HDF_LOGE("%s: device is NULL", __func__); return; } host = UartHostFromDevice(device); if (host == NULL) { - HDF_LOGE("%s: host is null", __func__); + HDF_LOGE("%s: host is NULL", __func__); return; } if (host->priv != NULL) { - SampleDetach(host); + DetachUartDevice(host); } UartHostDestroy(host); } - struct HdfDriverEntry g_hdfUartSample = { + struct HdfDriverEntry g_sampleUartDriverEntry = { .moduleVersion = 1, .moduleName = "UART_SAMPLE", - .Bind = HdfUartSampleBind, - .Init = HdfUartSampleInit, - .Release = HdfUartSampleRelease, + .Bind = SampleUartDriverBind, + .Init = SampleUartDriverInit, + .Release = SampleUartDriverRelease, }; - HDF_INIT(g_hdfUartSample); + HDF_INIT(g_sampleUartDriverEntry); ``` 3. 注册UART驱动接口。 @@ -238,7 +247,7 @@ HDF框架提供了UART驱动接口的模板方法UartHostMethod,实现UART驱动接口的代码如下: ``` - static int32_t SampleInit(struct UartHost *host) + static int32_t SampleUartHostInit(struct UartHost *host) { HDF_LOGI("%s: Enter", __func__); if (host == NULL) { @@ -248,7 +257,7 @@ return HDF_SUCCESS; } - static int32_t SampleDeinit(struct UartHost *host) + static int32_t SampleUartHostDeinit(struct UartHost *host) { HDF_LOGI("%s: Enter", __func__); if (host == NULL) { @@ -259,48 +268,47 @@ } // 向UART中写入数据 - static int32_t SampleWrite(struct UartHost *host, uint8_t *data, uint32_t size) + static int32_t SampleUartHostWrite(struct UartHost *host, uint8_t *data, uint32_t size) { HDF_LOGI("%s: Enter", __func__); uint32_t idx; struct UartRegisterMap *regMap = NULL; struct UartDevice *device = NULL; - + if (host == NULL || data == NULL || size == 0) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } - device = (struct UartDevice *) host->priv; + device = (struct UartDevice *)host->priv; if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_PARAM; } - regMap = (struct UartRegisterMap *) device->resource.physBase; + regMap = (struct UartRegisterMap *)device->resource.physBase; for (idx = 0; idx < size; idx++) { - while (UartPl011IsBusy(regMap)); UartPl011Write(regMap, data[idx]); } return HDF_SUCCESS; } // 设置UART的波特率 - static int32_t SampleSetBaud(struct UartHost *host, uint32_t baudRate) + static int32_t SampleUartHostSetBaud(struct UartHost *host, uint32_t baudRate) { HDF_LOGI("%s: Enter", __func__); struct UartDevice *device = NULL; struct UartRegisterMap *regMap = NULL; UartPl011Error err; - + if (host == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } - device = (struct UartDevice *) host->priv; + device = (struct UartDevice *)host->priv; if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_PARAM; } - regMap = (struct UartRegisterMap *) device->resource.physBase; + regMap = (struct UartRegisterMap *)device->resource.physBase; if (device->state != UART_DEVICE_INITIALIZED) { return UART_PL011_ERR_NOT_INIT; } @@ -315,16 +323,16 @@ } // 获取UART的波特率 - static int32_t SampleGetBaud(struct UartHost *host, uint32_t *baudRate) + static int32_t SampleUartHostGetBaud(struct UartHost *host, uint32_t *baudRate) { HDF_LOGI("%s: Enter", __func__); struct UartDevice *device = NULL; - + if (host == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } - device = (struct UartDevice *) host->priv; + device = (struct UartDevice *)host->priv; if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_PARAM; @@ -334,13 +342,13 @@ } // 在HdfUartSampleInit方法中绑定 - struct UartHostMethod g_uartSampleHostMethod = { - .Init = SampleInit, - .Deinit = SampleDeinit, + struct UartHostMethod g_sampleUartHostMethod = { + .Init = SampleUartHostInit, + .Deinit = SampleUartHostDeinit, .Read = NULL, - .Write = SampleWrite, - .SetBaud = SampleSetBaud, - .GetBaud = SampleGetBaud, + .Write = SampleUartHostWrite, + .SetBaud = SampleUartHostSetBaud, + .GetBaud = SampleUartHostGetBaud, .SetAttribute = NULL, .GetAttribute = NULL, .SetTransMode = NULL, @@ -436,32 +444,42 @@ >![](public_sys-resources/icon-notice.gif) **须知:** >U-boot引导程序默认会有2秒的等待时间,用户可使用回车打断等待并显示"hisillicon",通过**reset**命令可再次启动系统。 - **表 1** U-boot启动参数 + **表 1** U-boot修改命令 - -

执行命令

+ + + - + + + - - + + + + + + +

执行命令

+

命令解释

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

saveenv

-

reset

+

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";

+

读取FLASH起始地址为0x800(单位为512B,即1MB),大小为0x4800(单位为512B,即9MB)的内容到0x80000000的内存地址。

命令解释

-

setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800;go 0x80000000";

-

表示选择FLASH器件0,读取FLASH起始地址为0x800(单位为512B,即1MB),大小为0x4800(单位为512B,即9MB)的内容到0x80000000的内存地址。

-

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

-

表示设置启动参数,输出模式为串口输出,波特率为115200,数据位8,rootfs挂载于emmc器件,文件系统类型为vfat,

-

“rootaddr=10M rootsize=15M rw”处对应填入rootfs.img的烧写起始位置与长度,此处与IDE中新增rootfs.img文件时所填大小必须相同

-

saveenv;表示保存当前配置。

-

reset;表示复位单板

-

[可选]“go 0x80000000”默认配置已将指令固化在启动参数中,单板复位后可自动启动。若想切换为手动启动,可在U-boot启动倒数阶段使用"回车"打断自动启动。

+

setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw";

+

表示设置启动参数,输出模式为串口输出,波特率为115200,数据位8,rootfs挂载于emmc器件,文件系统类型为vfat,

+

“rootaddr=10M rootsize=15M rw”处对应填入rootfs.img的烧写起始位置与长度,此处与IDE中新增rootfs.img文件时所填大小必须相同

+

saveenv

+

表示保存当前配置。

+

reset

+

表示复位单板。

+ >![](public_sys-resources/icon-notice.gif) **须知:** + >**“go 0x80000000”**为可选指令,默认配置已将该指令固化在启动参数中,单板复位后可自动启动。若想切换为手动启动,可在U-boot启动倒数阶段使用"回车"打断自动启动。 + 3. 输入**“reset”**指令并回车,重启单板,启动成功如下图,输入回车串口显示OHOS字样。 **图 2** 系统启动图 @@ -469,7 +487,7 @@ ![](figures/qi1.png) -4. 根目录下,在命令行输入指令“**./bin/hello\_uart**”执行写入的demo程序,显示成功结果如下图所示。 +4. 根目录下,在命令行输入指令“**./bin/hello\_uart**”执行写入的demo程序,显示成功结果如下所示。 ``` OHOS # ./bin/hello_uart diff --git "a/quick-start/\345\274\200\345\217\221Hi3518\347\254\254\344\270\200\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" "b/quick-start/\345\274\200\345\217\221Hi3518\347\254\254\344\270\200\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" index 5129d1789185ba4dd177f4b5e4a6d46c2f2eac5e..28b61425961c38e653ba9f30eab6a6429a8fe2e5 100755 --- "a/quick-start/\345\274\200\345\217\221Hi3518\347\254\254\344\270\200\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" +++ "b/quick-start/\345\274\200\345\217\221Hi3518\347\254\254\344\270\200\344\270\252\347\244\272\344\276\213\347\250\213\345\272\217.md" @@ -78,32 +78,46 @@ Hi3518EV300单板请使用串口烧写。 2. (初次烧写必选)修改U-boot的bootcmd及bootargs内容:该步骤为固化操作,可保存执行结果,但U-boot重新烧入,则需要再次执行下述步骤。 - **表 1** Hi3518EV300 U-boot修改参数解释 + **表 1** U-boot修改命令 - - - @@ -156,7 +156,8 @@ Linux服务器通用环境配置需要的工具及其获取途径如下表所示 - @@ -193,7 +194,7 @@ Linux服务器通用环境配置需要的工具及其获取途径如下表所示 ls -l /bin/sh ``` -如果为显示为/bin/sh -\> bash则为正常,否则请按以下方式修改: +如果显示为“/bin/sh -\> bash”则为正常,否则请按以下方式修改: **方法一**:在终端运行如下命令,然后选择 no。 @@ -204,66 +205,117 @@ sudo dpkg-reconfigure dash **方法二**:先删除sh,再创建软链接。 ``` -sudo rm /bin/sh +sudo rm -rf /bin/sh sudo ln -s /bin/bash /bin/sh ``` ## 安装Python环境 1. 打开Linux编译服务器终端。 -2. 输入命令“python3 --version”,查看Python版本号。需使用python3.7以上版本,否则请按以下步骤执行,以python3.8为例: - - 如果Ubuntu 版本为18,按以下方式安装python: - 1. 运行`sudo apt-get install python3.8` - - 2. 运行`python3 -V`查看版本,如果是3.8.x版本,则安装成功, 否则执行第3步 +2. 输入如下命令,查看Python版本号,需使用python3.7以上版本。 - 3. 运行以下命令, 修改python3软链接 - ``` - which python3 - cd /usr/bin //进入上一命令输出的路径, 以/usr/bin为例 - sudo rm python3 - sudo ln -s python3.8 python3 - ``` + ``` + python3 --version + ``` + + 以python3.8为例,按照以下步骤安装python。 + + 1. 运行如下命令,查看Ubuntu版本: + + ``` + cat /etc/issue + ``` + + 1. 根据Ubuntu不同版本,安装python。 + - 如果Ubuntu 版本为18+,运行如下命令。 + + ``` + sudo apt-get install python3.8 + ``` + + - 如果Ubuntu版本为16,请以下载包的方式安装python。 + + 1. python安装环境依赖\(gcc, g++, make, zlib, libffi\)请运行如下命令: + + ``` + sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib* && sudo apt-get install libffi-dev + ``` + + 1. 获取[python3.8.5安装包](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz),将其放至linux服务器中,运行如下命令 : + + ``` + tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && sudo make && sudo make install + ``` + + + +3. 确定Python安装好后,运行如下命令,将python路径链接到"/usr/bin/python"。 - - 如果Ubuntu 版本为16,请以下载包的方式安装python: + ``` + which python3.8 + cd /usr/bin && sudo rm python && sudo ln -s /usr/local/bin/python3.8 python && python --version + ``` - 1. python安装环境依赖\(gcc, g++, make, zlib, libffi\)请运行: +4. 安装并升级Python包管理工具(pip3),任选如下一种方式。 + - **命令行方式:** - "sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib\* && sudo apt-get install libffi-dev " + ``` + sudo apt-get install python3-setuptools python3-pip -y + sudo pip3 install --upgrade pip + ``` - 2. 获取[python3.8.5安装包](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz),将其放至linux服务器中,运行 : + - **安装包方式:** + + ``` + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + python get-pip.py + ``` - "tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && sudo make && sudo make install" - 3. 确定Python-3.8.5安装好后,运行"which python3.8",将回显路径链接到"/usr/bin/python3",示例: +5. 运行如下命令,安装python模块setuptools。 - "cd /usr/bin && sudo rm python3 && ln -s /usr/local/bin/python3.8 python3 && python3 --version" + ``` + pip3 install setuptools + ``` +6. 安装GUI menuconfig工具(Kconfiglib),建议安装Kconfiglib 13.2.0+版本,任选如下一种方式。 + - **命令行方式:** - - 安装Python包管理工具,运行“sudo apt-get install python3-setuptools python3-pip -y”命令。(需root/sudo权限安装),升级pip3运行"sudo pip3 install --upgrade pip"。 + ``` + sudo pip3 install kconfiglib + ``` - 或按官网方式安装Python包管理工具:下载"curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py"后,再执行"python get-pip.py" + - **安装包方式:** + 1. 下载.whl文件(例如:kconfiglib-13.2.0-py2.py3-none-any.whl)。 -3. 安装python模块setuptools,运行"pip3 install setuptools" -4. 运行“sudo pip3 install kconfiglib”命令,安装GUI menuconfig工具,建议安装Kconfiglib 13.2.0+版本。(需root/sudo权限安装)。 + 下载路径:“[https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files)” - 或者按以下方法安装Kconfiglib 13.2.0+。 - 1. 下载.whl文件(例如:kconfiglib-13.2.0-py2.py3-none-any.whl)。 + 1. 运行如下命令,安装.whl文件。 - 下载路径:“[https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files)” + ``` + sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl + ``` - 1. 运行“sudo pip3 install kconfiglib-xxx.whl”命令(例如:sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl),安装.whl文件(需root/sudo权限安装)。 - 2. 可能会遇到错误:"subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1.",解决方法:"find / -name lsb\_release",找到lsb\_release位置并删除,如:"sudo rm /usr/bin/lsb\_release" ## 安装文件打包工具 1. 打开Linux编译服务器终端。 -2. 运行"mkfs.vfat",如果未找到该命令,请运行"sudo apt-get install dosfstools" -3. 运行"mcopy ",如果未找到该命令,请运行"sudo apt-get install mtools" +2. 运行如下命令,安装dosfstools。 + + ``` + sudo apt-get install dosfstools + ``` + +3. 运行如下命令,安装mtools。 + + ``` + sudo apt-get install mtools + ``` + ## 安装编译工具环境 @@ -276,31 +328,115 @@ sudo ln -s /bin/bash /bin/sh 1. 打开Linux编译服务器终端。 2. [下载gn工具](https://repo.huaweicloud.com/harmonyos/compiler/gn/1523/linux/gn.1523.tar)。 -3. 解压gn安装包至\~/gn路径下:"tar -xvf gn.1523.tar -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc", 新增:"export PATH=\~/gn:$PATH"。 -5. 生效环境变量:"source \~/.bashrc"。 +3. 解压gn安装包至\~/gn路径下。 + + ``` + tar -xvf gn.1523.tar -C ~/ + ``` + +4. 设置环境变量。 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/gn:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + ## 安装ninja -1. 打开Linux编译服务器终端 +1. 打开Linux编译服务器终端。 2. [下载ninja工具](https://repo.huaweicloud.com/harmonyos/compiler/ninja/1.9.0/linux/ninja.1.9.0.tar)。 -3. 解压ninja安装包至\~/ninja路径下:"tar -xvf ninja.1.9.0.tar -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc", 新增:"export PATH=\~/ninja:$PATH"。 -5. 生效环境变量:"source \~/.bashrc"。 +3. 解压ninja安装包至\~/ninja路径下。 + + ``` + tar -xvf ninja.1.9.0.tar -C ~/ + ``` + +4. 设置环境变量。 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/ninja:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + ## 安装LLVM编译工具链 1. 打开Linux编译服务器终端。 2. [下载LLVM工具](https://repo.huaweicloud.com/harmonyos/compiler/clang/9.0.0-34042/linux/llvm-linux-9.0.0-34042.tar)。 -3. 解压LLVM安装包至\~/llvm路径下:"tar -xvf llvm-linux-9.0.0-34042.tar -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc", 新增:export PATH=\~/llvm/bin:$PATH。 -5. 生效环境变量:"source \~/.bashrc"。 +3. 解压LLVM安装包至\~/llvm路径下。 + + ``` + tar -xvf llvm-linux-9.0.0-34042.tar -C ~/ + ``` + +4. 设置环境变量。 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/llvm/bin:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + ## 安装hc-gen 1. 打开Linux编译服务器终端。 2. [下载hc-gen工具](https://repo.huaweicloud.com/harmonyos/compiler/hc-gen/0.65/linux/hc-gen-0.65-linux.tar)。 -3. 解压hc-gen安装包到Linux服务器\~/hc-gen路径下:"tar -xvf hc-gen-0.65-linux.tar -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc", 新增:export PATH=\~/hc-gen:$PATH。 -5. 生效环境变量:"source \~/.bashrc"。 +3. 解压hc-gen安装包到Linux服务器\~/hc-gen路径下。 + + ``` + tar -xvf hc-gen-0.65-linux.tar -C ~/ + ``` + +4. 设置环境变量。 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/hc-gen:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + diff --git "a/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203-2.md" "b/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203-2.md" index e02f3360008c32e4f315bfb306e9a13692c19212..479313c33582875abd1771d0ff6bd790be9b5e43 100755 --- "a/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203-2.md" +++ "b/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203-2.md" @@ -125,24 +125,25 @@ Linux服务器通用环境配置需要的工具及其获取途径如下表所示

执行命令

+ + - + + + + - - + + + + + + + + + +

执行命令

setenv bootcmd "sf probe 0;sf read 0x40000000 0x100000 0x600000;go 0x40000000";

-

setenv bootargs "console=ttyAMA0,115200n8 root=flash fstype=jffs2 rw rootaddr=7M rootsize=8M"; saveenv;

-

reset

+

命令解释

+

setenv bootcmd "sf probe 0;sf read 0x40000000 0x100000 0x600000;go 0x40000000";

+

设置bootcmd内容,选择FLASH器件0,读取FLASH起始地址为0x100000,大小为0x600000的内容到0x40000000的内存地址。

命令解释

-

setenv bootcmd "sf probe 0;sf read 0x40000000 0x100000 0x600000;go 0x40000000";

-

设置bootcmd内容,选择FLASH器件0,读取FLASH起始地址为0x100000,大小为0x600000的内容到0x40000000的内存地址。

-

(可选)“go 0x40000000”默认配置已将指令固化在启动参数中,单板复位后可自动启动。若想切换为手动启动,可在U-boot启动倒数阶段使用"回车"打断自动启动。

-

setenv bootargs "console=ttyAMA0,115200n8 root=flash fstype=jffs2 rw rootaddr=7M rootsize=8M";

-

表示设置bootargs参数为串口输出,波特率为115200,数据位8,rootfs挂载于FLASH上,文件系统类型为jffs2 rw,以支持可读写JFFS2文件系统。

-

“rootaddr=7M rootsize=8M”处对应填入实际rootfs.img的烧写起始位置与长度,此处应与HiTool新增文件时所填大小相同。

-

saveenv;表示保存当前配置。

-

reset;表示复位单板。

-

pri表示查看显示参数。

+

setenv bootargs "console=ttyAMA0,115200n8 root=flash fstype=jffs2 rw rootaddr=7M rootsize=8M";

+

表示设置bootargs参数为串口输出,波特率为115200,数据位8,rootfs挂载于FLASH上,文件系统类型为jffs2 rw,以支持可读写JFFS2文件系统。“rootaddr=7M rootsize=8M”处对应填入实际rootfs.img的烧写起始位置与长度,此处应与HiTool新增文件时所填大小相同。

+

saveenv

+

表示保存当前配置。

+

reset

+

表示复位单板。

+

pri

+

表示查看显示参数。

+ >![](public_sys-resources/icon-notice.gif) **须知:** + >**“go 0x40000000”**为可选指令,默认配置已将该指令固化在启动参数中,单板复位后可自动启动。若想切换为手动启动,可在U-boot启动倒数阶段使用"回车"打断自动启动。 + **图 7** 启动参数设置图 ![](figures/启动参数设置图.png "启动参数设置图") diff --git "a/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203-0.md" "b/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203-0.md" index 8dd38908f6418117bfd476588505d4379176d8dc..051cefbb6b5af505f0af16f97c4850478b0494d0 100755 --- "a/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203-0.md" +++ "b/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203-0.md" @@ -49,8 +49,8 @@

USB转串口驱动

http://www.hihope.org/download

-

USB-to-Serial Comm Port.exe

+

http://www.hihope.org/download/AllDocuments

+

驱动软件名:USB-to-Serial Comm Port.exe

U-boot,镜像文件烧写工具

http://www.hihope.org/download

+

http://www.hihope.org/download/AllDocuments

+

工具软件名:Hi3516-Hitool.zip

- - - - - - @@ -179,7 +180,7 @@ Linux服务器通用环境配置需要的工具及其获取途径如下表所示 ls -l /bin/sh ``` -如果为显示为/bin/sh -\> bash则为正常,否则请按以下方式修改: +如果显示为“/bin/sh -\> bash”则为正常,否则请按以下方式修改: **方法一**:在终端运行如下命令,然后选择 no。 @@ -190,66 +191,117 @@ sudo dpkg-reconfigure dash **方法二**:先删除sh,再创建软链接。 ``` -sudo rm /bin/sh +sudo rm -rf /bin/sh sudo ln -s /bin/bash /bin/sh ``` ## 安装Python环境 1. 打开Linux编译服务器终端。 -2. 输入命令“python3 --version”,查看Python版本号。需使用python3.7以上版本,否则请按以下步骤执行,以python3.8为例: - - 如果Ubuntu 版本为18,按以下方式安装python: - 1. 运行`sudo apt-get install python3.8` - - 2. 运行`python3 -V`查看版本,如果是3.8.x版本,则安装成功, 否则执行第3步 +2. 输入如下命令,查看Python版本号,需使用python3.7以上版本。 - 3. 运行以下命令, 修改python3软链接 - ``` - which python3 - cd /usr/bin //进入上一命令输出的路径, 以/usr/bin为例 - sudo rm python3 - sudo ln -s python3.8 python3 - ``` + ``` + python3 --version + ``` + + 以python3.8为例,按照以下步骤安装python。 + + 1. 运行如下命令,查看Ubuntu版本: + + ``` + cat /etc/issue + ``` + + 1. 根据Ubuntu不同版本,安装python。 + - 如果Ubuntu 版本为18+,运行如下命令。 + + ``` + sudo apt-get install python3.8 + ``` + + - 如果Ubuntu版本为16,请以下载包的方式安装python。 + + 1. python安装环境依赖\(gcc, g++, make, zlib, libffi\)请运行如下命令: + + ``` + sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib* && sudo apt-get install libffi-dev + ``` + + 1. 获取[python3.8.5安装包](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz),将其放至linux服务器中,运行如下命令 : + + ``` + tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && sudo make && sudo make install + ``` + + + +3. 确定Python安装好后,运行如下命令,将python路径链接到"/usr/bin/python"。 - - 如果Ubuntu 版本为16,请以下载包的方式安装python: + ``` + which python3.8 + cd /usr/bin && sudo rm python && sudo ln -s /usr/local/bin/python3.8 python && python --version + ``` - 1. python安装环境依赖\(gcc, g++, make, zlib, libffi\)请运行: +4. 安装并升级Python包管理工具(pip3),任选如下一种方式。 + - **命令行方式:** - "sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib\* && sudo apt-get install libffi-dev " + ``` + sudo apt-get install python3-setuptools python3-pip -y + sudo pip3 install --upgrade pip + ``` - 2. 获取[python3.8.5安装包](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz),将其放至linux服务器中,运行 : + - **安装包方式:** + + ``` + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + python get-pip.py + ``` - "tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && sudo make && sudo make install" - 3. 确定Python-3.8.5安装好后,运行"which python3.8",将回显路径链接到"/usr/bin/python3",示例: +5. 运行如下命令,安装python模块setuptools。 - "cd /usr/bin && sudo rm python3 && ln -s /usr/local/bin/python3.8 python3 && python3 --version" + ``` + pip3 install setuptools + ``` +6. 安装GUI menuconfig工具(Kconfiglib),建议安装Kconfiglib 13.2.0+版本,任选如下一种方式。 + - **命令行方式:** - - 安装Python包管理工具,运行“sudo apt-get install python3-setuptools python3-pip -y”命令。(需root/sudo权限安装),升级pip3运行"sudo pip3 install --upgrade pip"。 + ``` + sudo pip3 install kconfiglib + ``` - 或按官网方式安装Python包管理工具:下载"curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py"后,再执行"python get-pip.py" + - **安装包方式:** + 1. 下载.whl文件(例如:kconfiglib-13.2.0-py2.py3-none-any.whl)。 -3. 安装python模块setuptools,运行"pip3 install setuptools" -4. 运行“sudo pip3 install kconfiglib”命令,安装GUI menuconfig工具,建议安装Kconfiglib 13.2.0+版本。(需root/sudo权限安装)。 + 下载路径:“[https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files)” - 或者按以下方法安装Kconfiglib 13.2.0+。 - 1. 下载.whl文件(例如:kconfiglib-13.2.0-py2.py3-none-any.whl)。 + 1. 运行如下命令,安装.whl文件。 - 下载路径:“[https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files)” + ``` + sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl + ``` - 1. 运行“sudo pip3 install kconfiglib-xxx.whl”命令(例如:sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl),安装.whl文件(需root/sudo权限安装)。 - 2. 可能会遇到错误:"subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1.",解决方法:"find / -name lsb\_release",找到lsb\_release位置并删除,如:"sudo rm /usr/bin/lsb\_release" ## 安装文件打包工具 1. 打开Linux编译服务器终端。 -2. 运行"mkfs.vfat",如果未找到该命令,请运行"sudo apt-get install dosfstools" -3. 运行"mcopy ",如果未找到该命令,请运行"sudo apt-get install mtools" +2. 运行如下命令,安装dosfstools。 + + ``` + sudo apt-get install dosfstools + ``` + +3. 运行如下命令,安装mtools。 + + ``` + sudo apt-get install mtools + ``` + ## 安装编译工具环境 @@ -262,31 +314,115 @@ sudo ln -s /bin/bash /bin/sh 1. 打开Linux编译服务器终端。 2. [下载gn工具](https://repo.huaweicloud.com/harmonyos/compiler/gn/1523/linux/gn.1523.tar)。 -3. 解压gn安装包至\~/gn路径下:"tar -xvf gn.1523.tar -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc", 新增:"export PATH=\~/gn:$PATH"。 -5. 生效环境变量:"source \~/.bashrc"。 +3. 解压gn安装包至\~/gn路径下。 + + ``` + tar -xvf gn.1523.tar -C ~/ + ``` + +4. 设置环境变量。 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/gn:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + ## 安装ninja -1. 打开Linux编译服务器终端 +1. 打开Linux编译服务器终端。 2. [下载ninja工具](https://repo.huaweicloud.com/harmonyos/compiler/ninja/1.9.0/linux/ninja.1.9.0.tar)。 -3. 解压ninja安装包至\~/ninja路径下:"tar -xvf ninja.1.9.0.tar -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc", 新增:"export PATH=\~/ninja:$PATH"。 -5. 生效环境变量:"source \~/.bashrc"。 +3. 解压ninja安装包至\~/ninja路径下。 + + ``` + tar -xvf ninja.1.9.0.tar -C ~/ + ``` + +4. 设置环境变量。 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/ninja:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + ## 安装LLVM编译工具链 1. 打开Linux编译服务器终端。 2. [下载LLVM工具](https://repo.huaweicloud.com/harmonyos/compiler/clang/9.0.0-34042/linux/llvm-linux-9.0.0-34042.tar)。 -3. 解压LLVM安装包至\~/llvm路径下:"tar -xvf llvm-linux-9.0.0-34042.tar -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc", 新增:export PATH=\~/llvm/bin:$PATH。 -5. 生效环境变量:"source \~/.bashrc"。 +3. 解压LLVM安装包至\~/llvm路径下。 + + ``` + tar -xvf llvm-linux-9.0.0-34042.tar -C ~/ + ``` + +4. 设置环境变量。 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/llvm/bin:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + ## 安装hc-gen 1. 打开Linux编译服务器终端。 2. [下载hc-gen工具](https://repo.huaweicloud.com/harmonyos/compiler/hc-gen/0.65/linux/hc-gen-0.65-linux.tar)。 -3. 解压hc-gen安装包到Linux服务器\~/hc-gen路径下:"tar -xvf hc-gen-0.65-linux.tar -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc", 新增:export PATH=\~/hc-gen:$PATH。 -5. 生效环境变量:"source \~/.bashrc"。 +3. 解压hc-gen安装包到Linux服务器\~/hc-gen路径下。 + + ``` + tar -xvf hc-gen-0.65-linux.tar -C ~/ + ``` + +4. 设置环境变量。 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/hc-gen:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + diff --git "a/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203.md" "b/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203.md" old mode 100644 new mode 100755 index 30c16cc32e345182ceaa82c27b501ca50010437a..71c9aea54ab3eeb587c563360d21fd8be20eec30 --- "a/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203.md" +++ "b/quick-start/\346\220\255\345\273\272\347\216\257\345\242\203.md" @@ -192,7 +192,7 @@ Linux服务器通用环境配置需要的工具及其获取途径如下表所示 ls -l /bin/sh ``` -如果为显示为/bin/sh -\> bash则为正常,否则请按以下方式修改: +如果显示为“/bin/sh -\> bash”则为正常,否则请按以下方式修改: **方法一**:在终端运行如下命令,然后选择 no。 @@ -203,110 +203,194 @@ sudo dpkg-reconfigure dash **方法二**:先删除sh,再创建软链接。 ``` -sudo rm /bin/sh +sudo rm -rf /bin/sh sudo ln -s /bin/bash /bin/sh ``` ## 安装Python环境 1. 打开Linux编译服务器终端。 -2. 输入命令“python3 --version”,查看Python版本号。需使用python3.7以上版本,否则请按以下步骤执行,以python3.8为例: - - 如果Ubuntu 版本为18,按以下方式安装python: - 1. 运行`sudo apt-get install python3.8` - - 2. 运行`python3 -V`查看版本,如果是3.8.x版本,则安装成功, 否则执行第3步 +2. 输入如下命令,查看Python版本号,需使用python3.7以上版本。 - 3. 运行以下命令, 修改python3软链接 + ``` + python3 --version + ``` + + 以python3.8为例,按照以下步骤安装python。 + + 1. 运行如下命令,查看Ubuntu版本: + + ``` + cat /etc/issue + ``` + + 1. 根据Ubuntu不同版本,安装python。 + - 如果Ubuntu 版本为18+,运行如下命令。 + + ``` + sudo apt-get install python3.8 + ``` + + - 如果Ubuntu版本为16,请以下载包的方式安装python。 + + 1. python安装环境依赖\(gcc, g++, make, zlib, libffi\)请运行如下命令: + + ``` + sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib* && sudo apt-get install libffi-dev + ``` + + 1. 获取[python3.8.5安装包](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz),将其放至linux服务器中,运行如下命令 : + + ``` + tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && sudo make && sudo make install + ``` + + + +3. 确定Python安装好后,运行如下命令,将python路径链接到"/usr/bin/python"。 + + ``` + which python3.8 + cd /usr/bin && sudo rm python && sudo ln -s /usr/local/bin/python3.8 python && python --version + ``` + +4. 安装并升级Python包管理工具(pip3),任选如下一种方式。 + - **命令行方式:** + + ``` + sudo apt-get install python3-setuptools python3-pip -y + sudo pip3 install --upgrade pip ``` - which python3 - cd /usr/bin //进入上一命令输出的路径, 以/usr/bin为例 - sudo rm python3 - sudo ln -s python3.8 python3 + + - **安装包方式:** + + ``` + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + python get-pip.py ``` - - 如果Ubuntu 版本为16,请以下载包的方式安装python: - 1. python安装环境依赖\(gcc, g++, make, zlib, libffi\)请运行: +5. 运行如下命令,安装python模块setuptools。 - "sudo apt-get install gcc && sudo apt-get install g++ && sudo apt-get install make && sudo apt-get install zlib\* && sudo apt-get install libffi-dev " + ``` + pip3 install setuptools + ``` - 2. 获取[python3.8.5安装包](https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz),将其放至linux服务器中,运行 : +6. 安装GUI menuconfig工具(Kconfiglib),建议安装Kconfiglib 13.2.0+版本,任选如下一种方式。 + - **命令行方式:** - "tar -xvzf Python-3.8.5.tgz && cd Python-3.8.5 && sudo ./configure && sudo make && sudo make install" + ``` + sudo pip3 install kconfiglib + ``` - 3. 确定Python-3.8.5安装好后,运行"which python3.8",将回显路径链接到"/usr/bin/python3",示例: - "cd /usr/bin && sudo rm python3 && ln -s /usr/local/bin/python3.8 python3 && python3 --version" + - **安装包方式:** + 1. 下载.whl文件(例如:kconfiglib-13.2.0-py2.py3-none-any.whl)。 + 下载路径:“[https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files)” - - 安装Python包管理工具,运行“sudo apt-get install python3-setuptools python3-pip -y”命令。(需root/sudo权限安装),升级pip3运行"sudo pip3 install --upgrade pip"。 - 或按官网方式安装Python包管理工具:下载"curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py"后,再执行"python get-pip.py" + 1. 运行如下命令,安装.whl文件。 + ``` + sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl + ``` -3. 安装python模块setuptools,运行"pip3 install setuptools" -4. 运行“sudo pip3 install kconfiglib”命令,安装GUI menuconfig工具,建议安装Kconfiglib 13.2.0+版本。(需root/sudo权限安装)。 - 或者按以下方法安装Kconfiglib 13.2.0+。 - 1. 下载.whl文件(例如:kconfiglib-13.2.0-py2.py3-none-any.whl)。 +7. 安装pycryptodome,任选如下一种方式。 - 下载路径:“[https://pypi.org/project/kconfiglib\#files](https://pypi.org/project/kconfiglib#files)” + 安装升级文件签名依赖的Python组件包,包括:pycryptodome、six、ecdsa。安装ecdsa依赖six,请先安装six,再安装ecdsa。 + - **命令行方式:** - 1. 运行“sudo pip3 install kconfiglib-xxx.whl”命令(例如:sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl),安装.whl文件(需root/sudo权限安装)。 - 2. 可能会遇到错误:"subprocess.CalledProcessError: Command '\('lsb\_release', '-a'\)' returned non-zero exit status 1.",解决方法:"find / -name lsb\_release",找到lsb\_release位置并删除,如:"sudo rm /usr/bin/lsb\_release" + ``` + sudo pip3 install pycryptodome + ``` -5. 安装pycryptodome。运行“sudo pip3 install pycryptodome”命令,或者按照以下方式安装: + - **安装包方式:** + 1. 下载.whl文件(例如:pycryptodome-3.7.3-cp37-cp37m-manylinux1\_x86\_64.whl)。 - >![](public_sys-resources/icon-note.gif) **说明:** - >- 安装升级文件签名依赖的Python组件包,包括:pycryptodome、six、ecdsa。 - >- 安装ecdsa依赖six,请按文档要求,先安装six,再安装ecdsa。 + 下载路径:“[https://pypi.org/project/pycryptodome/\#files](https://pypi.org/project/pycryptodome/#files)”。 - 1. 下载.whl文件(例如:pycryptodome-3.7.3-cp37-cp37m-manylinux1\_x86\_64.whl)。 - 下载路径:“[https://pypi.org/project/pycryptodome/\#files](https://pypi.org/project/pycryptodome/#files)”。 + 1. 运行如下命令,安装.whl文件。 + ``` + sudo pip3 install pycryptodome-3.7.3-cp37-cp37m-manylinux1_x86_64.whl + ``` - 1. 运行“sudo pip3 install pycryptodome-xxx.whl”命令(例如:sudo pip3 install pycryptodome-3.7.3-cp37-cp37m-manylinux1\_x86\_64.whl),安装.whl文件(需root/sudo权限安装)。 - 安装完成后界面会提示“Successfully installed pycryptodome-3.7.3”。 -6. 安装six。运行"sudo pip3 install six --upgrade --ignore-installed six"或者按照以下方式安装: +8. 安装six,任选如下一种方式。 + - **命令行方式:** - 1. 下载.whl文件(例如:six-1.12.0-py2.py3-none-any.whl)。 + ``` + sudo pip3 install six --upgrade --ignore-installed six + ``` - 下载路径:“[https://pypi.org/project/six/\#files](https://pypi.org/project/six/#files)” + - **安装包方式:** + 1. 下载.whl文件(例如:six-1.12.0-py2.py3-none-any.whl)。 - 1. 运行“sudo pip3 install six-xxx.whl”命令(例如:sudo pip3 install six-1.12.0-py2.py3-none-any.whl),安装.whl文件(需root/sudo权限安装)。 + 下载路径:“[https://pypi.org/project/six/\#files](https://pypi.org/project/six/#files)” + + + 1. 运行如下命令,安装.whl文件。 + + ``` + sudo pip3 install six-1.12.0-py2.py3-none-any.whl + ``` + + + +9. 安装ecdsa,任选如下一种方式。 + - **命令行方式:** + + ``` + sudo pip3 install ecdsa + ``` - 安装完成后界面会提示“Successfully installed six-1.12.0”。 + - **安装包方式:** + 1. 下载.whl文件(例如:ecdsa-0.14.1-py2.py3-none-any.whl)。 -7. 安装ecdsa。运行"sudo pip3 install ecdsa"或者按照以下方式安装: + 下载路径:“[https://pypi.org/project/ecdsa/\#files](https://pypi.org/project/ecdsa/#files)” - 1. 下载.whl文件(例如:ecdsa-0.14.1-py2.py3-none-any.whl)。 - 下载路径:“[https://pypi.org/project/ecdsa/\#files](https://pypi.org/project/ecdsa/#files)” + 1. 运行如下命令,安装.whl文件。 + ``` + sudo pip3 install ecdsa-0.14.1-py2.py3-none-any.whl + ``` - 1. 运行“sudo pip3 install ecdsa-xxx.whl”命令(例如:sudo pip3 install ecdsa-0.14.1-py2.py3-none-any.whl),安装.whl文件(需root/sudo权限安装)。 - 安装完成后界面会提示“Successfully installed ecdsa-0.14.1”。 ## 安装Scons 1. 打开Linux编译服务器终端。 -2. 安装SCons安装包(需root/sudo权限安装)。 +2. 运行如下命令,安装SCons安装包。 + + ``` + sudo apt-get install scons -y + ``` - 运行命令:“sudo apt-get install scons -y”。如果软件源中无法找到安装包,请按以下步骤处理: + 如果软件源中无法找到安装包,请按以下步骤处理: 1. 下载源码包(下载路径:“ [https://scons.org/pages/download.html](https://scons.org/pages/download.html) ”,推荐SCons版本是3.0.4+)。 2. 解压源码包到任意目录。 - 3. 安装源码包:进入解压目录,运行命令“sudo python3 setup.py install”(需root/sudo权限安装),等待安装完毕。 + 3. 安装源码包:进入解压目录,运行如下命令: + + ``` + sudo python3 setup.py install + ``` -3. 输入命令“scons -v”,查看是否安装成功。如果安装成功,查询结果下图所示。 + +3. 运行如下命令,查看是否安装成功。如果安装成功,查询结果下图所示。 + + ``` + scons -v + ``` **图 5** SCons安装成功界面 ![](figures/SCons安装成功界面.png "SCons安装成功界面") @@ -323,17 +407,59 @@ sudo ln -s /bin/bash /bin/sh 1. 打开Linux编译服务器终端。 2. [下载gn工具](https://repo.huaweicloud.com/harmonyos/compiler/gn/1523/linux/gn.1523.tar)。 -3. 解压gn安装包至\~/gn路径下:"tar -xvf gn.1523.tar -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc", 新增:"export PATH=\~/gn:$PATH"。 -5. 生效环境变量:"source \~/.bashrc"。 +3. 解压gn安装包至\~/gn路径下。 + + ``` + tar -xvf gn.1523.tar -C ~/ + ``` + +4. 设置环境变量。 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/gn:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + ## 安装ninja -1. 打开Linux编译服务器终端 +1. 打开Linux编译服务器终端。 2. [下载ninja工具](https://repo.huaweicloud.com/harmonyos/compiler/ninja/1.9.0/linux/ninja.1.9.0.tar)。 -3. 解压ninja安装包至\~/ninja路径下:"tar -xvf ninja.1.9.0.tar -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc", 新增:"export PATH=\~/ninja:$PATH"。 -5. 生效环境变量:"source \~/.bashrc"。 +3. 解压ninja安装包至\~/ninja路径下。 + + ``` + tar -xvf ninja.1.9.0.tar -C ~/ + ``` + +4. 设置环境变量。 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/ninja:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + ## 安装gcc\_riscv32(WLAN模组类编译工具链) @@ -342,8 +468,34 @@ sudo ln -s /bin/bash /bin/sh 1. 打开Linux编译服务器终端。 2. [下载gcc\_riscv32工具](https://repo.huaweicloud.com/harmonyos/compiler/gcc_riscv32/7.3.0/linux/gcc_riscv32-linux-7.3.0.tar.gz)。 -3. 解压gcc\_riscv32安装包至\~/gcc\_riscv32路径下:"tar -xvf gcc\_riscv32-linux-7.3.0.tar.gz -C \~/"。 -4. 设置环境变量:"vim \~/.bashrc",新增:"export PATH=\~/gcc\_riscv32/bin:$PATH"。 -5. 生效环境变量:"source \~/.bashrc"。 -6. Shell命令行中输入“riscv32-unknown-elf-gcc -v”,如果能正确显示编译器版本号,表明编译器安装成功。 +3. 解压gcc\_riscv32安装包至\~/gcc\_riscv32路径下。 + + ``` + tar -xvf gcc_riscv32-linux-7.3.0.tar.gz -C ~/ + ``` + +4. 设置环境变量 + + ``` + vim ~/.bashrc + ``` + + 将以下命令拷贝到.bashrc文件的最后一行,保存并退出。 + + ``` + export PATH=~/gcc_riscv32/bin:$PATH + ``` + +5. 生效环境变量。 + + ``` + source ~/.bashrc + ``` + +6. Shell命令行中输入如下命令,如果能正确显示编译器版本号,表明编译器安装成功。 + + ``` + riscv32-unknown-elf-gcc -v + ``` +

开发工具

用途

+

用途

获取途径

+

获取途径

Visual Studio Code

代码编辑工具

+

代码编辑工具

https://code.visualstudio.com/

+

https://code.visualstudio.com/

HiTool工具

U-boot,镜像文件烧写工具

+

U-boot,镜像文件烧写工具

http://www.hihope.org/download

+

http://www.hihope.org/download/AllDocuments

+

工具软件名:Hi3516-Hitool.zip