华为c语言编程规范学习笔记(一)

本文讲述了C语言编程中的关键原则,包括代码清晰性和简洁性,强调头文件设计的重要性,如何避免依赖混乱、循环依赖和过度包含,提倡使用接口倒置和模块化结构,以提高代码的可维护性和编译效率。
摘要由CSDN通过智能技术生成

仅作为自己学习复习用。

一、代码总体原则

1、清晰第一

清晰性是易于维护、易于重构的程序必需具备的特征。代码首先是给人读的,计算机第二。

一般来说,代码的可读性高于性能,只有确定性能是瓶颈时,才应该主动优化。

2、简洁为美

简洁就是易于理解并且易于实现。代码越长越难以看懂,写的越多越容易出错,应该通过编写简洁的代码来提升可靠性。

废弃的代码(没有被调用的函数和全局变量)要及时清除,重复代码应该尽可能提炼成函数。

3、选择合适的风格,与代码原有风格保持一致

产品所有人共同分享同一种风格所带来的好处,远远超出为了统一而付出的代价。重构/修改其他风格的代码时,比较明智的做法是根据现有代码的现有风格继续编写代码,或者使用格式转换工具进行转换成公司内部风格。

二、头文件

1、背景

对于C语言来说,头文件的设计体现了大部分的系统设计。不合理的头文件布局是编译时间过长的根因,不合理的头文件实际上不合理的设计。

2、依赖的定义

本节特指编译依赖。若x.h包含了y.h,则称作x依赖y。依赖关系会进行传导,如x.h包含y.h, 而y.h又包含了z.h,则x通过y依赖了z。

依赖将导致编译时间的上升。不良的设计会导致整个系统的依赖关系无比复杂,使得任意一个文件的修改都要重新编译整个系统,导致编译时间巨幅上升。

在一个设计良好的系统中,修改一个文件,只需要重新编译数个,甚至是一个文件。

3、合理规划头文件

原则1.1 头文件中适合放置接口的声明,不适合放置实现。

头文件是模块(Module)或单元(Unit)的对外接口。头文件中应放置对外部的声明,如对外提供的函数声明、宏定义、类型定义等。

内部使用的函数(相当于类的私有方法)声明不应放在头文件中。

内部使用的宏、枚举、结构定义不应放入头文件中。

变量定义不应放在头文件中,应放在.c文件中。

变量的声明尽量不要放在头文件中,亦即尽量不要使用全局变量作为接口。变量是模块或单元的内部 实现细节,不应通过在头文件中声明的方式直接暴露给外部,应通过函数接口的方式进行对外暴露。即使必须使用全局变量,也只应当在.c中定义全局变量,在.h中仅声明变量为全局的。

原则1.2 头文件应当职责单一。

头文件过于复杂,依赖过于复杂是导致编译时间过长的主要原因。很多现有代码中头文件过大,职责过多,再加上循环依赖的问题,可能导致为了在.c中使用一个宏,而包含十几个头文件。

原则1.3 头文件应向稳定的方向包含。

头文件的包含关系是一种依赖,一般来说,应当让不稳定的模块依赖稳定的模块,从而当不稳定的模块发生变化时,不会影响(编译)稳定的模块。

就我们的产品来说,依赖的方向应该是:产品依赖于平台,平台依赖于标准库。

除了不稳定的模块依赖于稳定的模块外,更好的方式是两个模块共同依赖于接口,这样任何一个模块的内部实现更改都不需要重新编译另外一个模块。在这里,我们假设接口本身是最稳定的。

编者推荐开发人员使用“依赖倒置”原则,即由使用者制定接口,服务提供者实现接口。

规则1.1 每一个.c文件应有一个同名.h文件,用于声明需要对外公开的接口。

如果一个.c文件不需要对外公布任何接口,则其就不应当存在,除非它是程序的入口,如main函数所在的文件。

现有某些产品中,习惯一个.c文件对应两个头文件,一个用于存放对外公开的接口,一个用于存放内部需要用到的定义、声明等,以控制.c文件的代码行数。编者不提倡这种风格。这种风格的根源在于源文件过大,应首先考虑拆分.c文件,使之不至于太大。另外,一旦把私有定义、声明放到独立的头文件中,就无法从技术上避免别人include之,难以保证这些定义最后真的只是私有的。

本规则反过来并不一定成立。有些特别简单的头文件,如命令ID定义头文件,不需要有对应的.c存在。

规则1.2 禁止头文件循环依赖。

头文件循环依赖,指a.h包含b.h,b.h包含c.h,c.h包含a.h之类。循环依赖的任何一个头文件修改,会导致所有包含了a.h/b.h/c.h的代码全部需要重新编译一遍。

规则1.3 .c/.h文件禁止包含用不到的头文件。

很多系统中头文件包含关系复杂,开发人员为了省事起见,直接包含一切想到的头文件,这种只图一时省事的做法,导致整个系统的编译时间进一步恶化,并对后来的维护造成巨大的麻烦。

规则1.4 头文件应当自包含。

简单的说,自包含就是任意一个头文件均可独立编译。如果一个文件包含某个头文件,还要包含另外一个头文件才能工作的话,就会增加交流障碍,给这个头文件的用户增添不必要的负担。

示例: 如果a.h不是自包含的,需要包含b.h才能编译,会导致每个使用a.h头文件的.c文件,为了让引入的a.h的内容编译通过,都要包含额外的头文件b.h。 额外的头文件b.h必须在a.h之前进行包含,这在包含顺序上产生了依赖。

假设a.h定义一个函数,它的参数是结构体类型,而这个结构体类型的定义在b.h,就建议a.h自己包含b.h。

注意:该规则需要与“.c/.h文件禁止包含用不到的头文件”规则一起使用,不能为了让a.h自包含, 而在a.h中包含不必要的头文件。a.h要刚刚可以自包含,不能在a.h中多包含任何满足自包含之外的其他头文件。

规则1.5 总是编写内部#include保护符(#define 保护)。

多次包含一个头文件可以通过认真的设计来避免。如果不能做到这一点,就需要采取阻止头文件内容被包含多于一次的机制。

通常的手段是为每个文件配置一个宏,当头文件第一次被包含时就定义这个宏,并在头文件被再次包含时使用它以排除文件内容。

所有头文件都应当使用#define防止头文件被多重包含,命名格式为FILENAME_H,为了保证唯一性,更好的命名是PROJECTNAME_PATH_FILENAME_H。

注:没有在宏最前面加上“_",即使用FILENAME_H代替_FILENAME_H_,是因为一般以"_"和”__"开头的标识符为系统保留或者标准库使用,在有些静态检查工具中,若全局可见的标识符以"_"开头会给出警告。

定义包含保护符时,应该遵守如下规则:

1)保护符使用唯一名称;

2)不要在受保护部分的前后放置代码或者注释。

例外情况:头文件的版权声明部分以及头文件的整体注释部分(如阐述此头文件的开发背景、使用注 意事项等)可以放在保护符(#ifndef XX_H)前面。

规则1.6 禁止在头文件中定义变量。

在头文件中定义变量,将会由于头文件被其他.c文件包含而导致变量重复定义。

规则1.7 只能通过包含头文件的方式使用其他.c提供的接口,禁止在.c中通过extern的方式使用外部 函数接口、变量。

若a.c使用了b.c定义的foo()函数,则应当在b.h中声明extern int foo(int input);并在a.c 中通过#include 来使用foo。禁止通过在a.c中直接写extern int foo(int input);来使用foo, 后面这种写法容易在foo改变时可能导致声明和定义不一致。

编译器在编译工作时会对每一个.c里的函数检查它是否存在,参数是否正确,语法是否有错误,定义的变量、结构体是不是正确,把宏展开,内敛函数的展开等。a.c里写extern foo的意思就是这个函数不需要检查了,可能导致foo函数改变时出现错误。

规则1.8 禁止在extern "C"中包含头文件。

在extern "C"中包含头文件,会导致extern "C"嵌套,Visual Studio对extern "C"嵌套层次有限制,嵌套层次太多会编译错误。

在extern "C"中包含头文件,可能会导致被包含头文件的原有意图遭到破坏。

建议1.1 一个模块通常包含多个.c文件,建议放在同一个目录下,目录名即为模块名。为方便外部使用者,建议每一个模块提供一个.h,文件名为目录名。
建议1.2 如果一个模块包含多个子模块,则建议每一个子模块提供一个对外的.h,文件名为子模块名。

说明:降低接口使用者的编写难度。

建议1.3 头文件不要使用非习惯用法的扩展名,如.inc。

目前很多产品中使用了.inc作为头文件扩展名,这不符合c语言的习惯用法。在使用.inc作为头 文件扩展名的产品,习惯上用于标识此头文件为私有头文件。但是从产品的实际代码来看,这一条并 没有被遵守,一个.inc文件被多个.c包含比比皆是。本规范不提倡将私有定义单独放在头文件中,具体见规则1.1。

除此之外,使用.inc还导致source insight、Visual stduio等IDE工具无法识别其为头文件,导致很 多功能不可用,如“跳转到变量定义处”。虽然可以通过配置,强迫IDE识别.inc为头文件,但是有些 软件无法配置,如Visual Assist只能识别.h而无法通过配置识别.inc。

建议1.4 同一产品统一包含头文件排列方式。

常见的包含头文件排列方式:功能块排序、文件名升序、稳定度排序。

以升序方式排列头文件可以避免头文件被重复包含;以稳定度排序,建议将不稳定的头文件放在前面,如把产品的头文件放在平台的头文件前面。

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值