华为C语言编程规范(精华总结)_华为编程规范 c语言

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

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

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

11、禁止在 extern “C” 中包含头文件

在extern "C"中包含头文件,会导致extern "C"嵌套,Visual Studio对extern "C"嵌套层次有限制,嵌套层次太多会编译错误。
在extern "C"中包含头文件,可能会导致被包含头文件的原有意图遭到破坏。

错误示例:

extern “C”
{
#include “xxx.h”
...
}

正确示例:

#include “xxx.h”
extern “C”
{
...
} 

12、一个模块通常包含多个 .c 文件,建议放在同一个目录下,目录名即为模块名。为方便外部使用者,建议每一个模块提供一个 .h ,文件名为目录名

需要注意的是,这个.h并不是简单的包含所有内部的.h,它是为了模块使用者的方便,对外整体提供的模块接口。以Google test(简称GTest)为例,GTest作为一个整体对外提供C++单元测试框架,其1.5版本的gtest工程下有6个源文件和12个头文件。但是它对外只提供一个gtest.h,只要包含gtest.h即可使用GTest提供的所有对外提供的功能,使用者不必关系GTest内部各个文件的关系,即使以后GTest的内部实现改变了,比如把一个源文件c拆成两个源文件,使用者也不必关心,甚至如果对外功能不变,连重新编译都不需要。对于有些模块,其内部功能相对松散,可能并不一定需要提供这个.h,而是直接提供各个子模块或者.c的头文件。

比如产品普遍使用的VOS,作为一个大模块,其内部有很多子模块,他们之间的关系相对比较松散,就不适合提供一个vos.h。而VOS的子模块,如Memory(仅作举例说明,与实际情况可能有所出入),其内部实现高度内聚,虽然其内部实现可能有多个.c和.h,但是对外只需要提供一个Memory.h声明接口。

13、如果一个模块包含多个子模块,则建议每一个子模块提供一个对外的 .h,文件名为子模块名

降低接口使用者的编写难度

14、头文件不要使用非习惯用法的扩展名,如 .inc

目前很多产品中使用了.inc作为头文件扩展名,这不符合c语言的习惯用法。在使用.inc作为头文件扩展名的产品,习惯上用于标识此头文件为私有头文件。但是从产品的实际代码来看,这一条并没有被遵守,一个.inc文件被多个.c包含比比皆是。

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

15、同一产品统一包含头文件排列方式

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

正确示例1:以升序方式排列头文件可以避免头文件被重复包含:

#include <a.h>
#include <b.h>
#include <c/d.h>
#include <c/e.h>
#include <f.h>

正确示例2:以稳定度排序,建议将不稳定的头文件放在前面,如把产品的头文件放在平台的头文件前面:

#include <product.h>
#include <platform.h>

相对来说,product.h修改的较为频繁,如果有错误,不必编译platform.h就可以发现product.h的错误,可以部分减少编译时间。


2、函数

函数设计的精髓:编写整洁函数,同时把代码有效组织起来。

整洁函数要求:代码简单直接、不隐藏设计者的意图、用干净利落的抽象和直截了当的控制语句将函数有机组织起来。

代码的有效组织包括:逻辑层组织和物理层组织两个方面。逻辑层,主要是把不同功能的函数通过某种联系组织起来,主要关注模块间的接口,也就是模块的架构。物理层,无论使用什么样的目录或者名字空间等,需要把函数用一种标准的方法组织起来。例如:设计良好的目录结构、函数名字、文件组织等,这样可以方便查找。

1、一个函数仅完成一件功能

一个函数实现多个功能给开发、使用、维护都带来很大的困难。

将没有关联或者关联很弱的语句放到同一函数中,会导致函数职责不明确,难以理解,难以测试和改动。

2、重复代码应该尽可能提炼成函数

重复代码提炼成函数可以带来维护成本的降低。

重复代码是我司不良代码最典型的特征之一。在“代码能用就不改”的指导原则之下,大量的烟囱式设计及其实现充斥着各产品代码之中。新需求增加带来的代码拷贝和修改,随着时间的迁移,产品中堆砌着许多类似或者重复的代码。

项目组应当使用代码重复度检查工具,在持续集成环境中持续检查代码重复度指标变化趋势,并对新增重复代码及时重构。当一段代码重复两次时,即应考虑消除重复,当代码重复超过三次时,应当立刻着手消除重复。

3、避免函数过长,新增函数不超过 50 行 (非空非注释行)

过长的函数往往意味着函数功能不单一,过于复杂。

函数的有效代码行数,即NBNC(非空非注释行)应当在[1,50]区间。

例外:某些实现算法的函数,由于算法的聚合性与功能的全面性,可能会超过50行。

延伸阅读材料: 业界普遍认为一个函数的代码行不要超过一个屏幕,避免来回翻页影响阅读;一般的代码度量工具建议都对此进行检查,例如Logiscope的函数度量:“Number of Statement” (函数中的可执行语句数)建议不超过20行,QA C建议一个函数中的所有行数(包括注释和空白行)不超过50行。

4、避免函数的代码块嵌套过深,新增函数的代码块嵌套不超过4层

函数的代码块嵌套深度指的是函数中的代码控制块(例如:if、for、while、switch等)之间互相包含的深度。每级嵌套都会增加阅读代码时的脑力消耗,因为需要在脑子里维护一个“栈”(比如,进入条件语句、进入循环„„)。应该做进一步的功能分解,从而避免使代码的阅读者一次记住太多的上下文。优秀代码参考值:[1, 4]。

错误示例:代码嵌套深度为5层:

void serial (void)
{
    if (!Received)
    {
        TmoCount = 0;
         switch (Buff)
        {
            case AISGFLG:
                if ((TiBuff.Count > 3)&& ((TiBuff.Buff[0] == 0xff) || (TiBuf.Buff[0] == CurPa.ADDR)))
                {
                    Flg7E = false;
                    Received = true;
                }
                else
                {
                    TiBuff.Count = 0;
                    Flg7D = false;
                    Flg7E = true;
                }
                break;
            default:
                break;
        }
    }
}

5、 可重入函数应避免使用共享变量;若需要使用,则应通过互斥手段(关中断、信号量)对其加以保护

可重入函数是指可能被多个任务并发调用的函数。在多任务操作系统中,函数具有可重入性是多个任务可以共用此函数的必要条件。共享变量指的全局变量和static变量。编写C语言的可重入函数时,不应使用static局部变量,否则必须经过特殊处理,才能使函数具有可重入性。

示例:函数square_exam返回g_exam平方值。那么如下函数不具有可重入性。

int g_exam;
unsigned int example( int para )
{
    unsigned int temp;
    g_exam = para; // (**)
    temp = square_exam ( );
    return temp;
}

此函数若被多个线程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的线程可能正好被激活,那么当新激活的线程执行到此函数时,将使g_exam赋于另一个不同的para值,所以当控制重新回到“temp =square_exam ( )”后,计算出的temp很可能不是预想中的结果。此函数应如下改进。

int g_exam;
unsigned int example( int para )
{
    unsigned int temp;
    [申请信号量操作] // 若申请不到“信号量”,说明另外的进程正处于
    g_exam = para; //给g_exam赋值并计算其平方过程中(即正在使用此
    temp = square_exam( ); // 信号),本进程必须等待其释放信号后,才可继
    [释放信号量操作] // 续执行。其它线程必须等待本线程释放信号量后
    // 才能再使用本信号。
    return temp;
}

6、对参数的合法性检查,由调用者负责还是由接口函数负责,应在项目组/模块内应统一规定。缺省由调用者负责。

对于模块间接口函数的参数的合法性检查这一问题,往往有两个极端现象,即:要么是调用者和被调用者对参数均不作合法性检查,结果就遗漏了合法性检查这一必要的处理过程,造成问题隐患;要么就是调用者和被调用者均对参数进行合法性检查,这种情况虽不会造成问题,但产生了冗余代码,降低了效率。

### 华为C语言软件编程规范 #### 一、排版规范 华为的C语言软件编程规范中关于排版的规定非常详细,旨在提高代码的可读性和一致性。具体包括以下几点: 1. **程序块缩进**: 根据规范¹1-1,所有的程序块都应当采用缩进风格编写,并且每层缩进使用4个空格。值得注意的是,如果代码是由开发工具自动生成的,可能允许存在一定的不一致性。 2. **空行间隔**: 根据规范¹1-2,对于相对独立的程序块之间以及变量声明之后,都必须添加空行。例如,在`if`语句的闭合大括号后,如果紧接着有其他代码,应当插入一个空行以区分不同的逻辑块。 3. **长语句的分割**: 规范¹1-3强调,当语句长度超过80个字符时,应该将其拆分为多行,以提高代码的可读性。拆分应在较低优先级的操作符处进行,确保新行的代码能够保持良好的对齐和格式化。 4. **循环与条件语句的拆分**: 如规范¹1-4所述,对于复杂的循环或条件语句中的长表达式,同样应当在低优先级操作符处分割成多行,并确保每个新行都适当地缩进,以保持良好的可读性。 5. **函数参数的分割**: 规范¹1-5提到,当函数调用中的参数列表过长时,也应该进行适当的拆分。这有助于提高代码的清晰度,尤其是在参数数量较多的情况下。 6. **单行语句限制**: 根据规范¹1-6,不允许将多个短语句写在同一行中。即使这些语句很短,也应各自单独占据一行,以提高代码的清晰度。 7. **控制语句的格式**: 根据规范¹1-7,`if`、`for`、`do`、`while`等控制语句及其后的执行语句无论多少行都必须加上花括号`{}`,即使执行语句只有一行也不例外。这有助于避免因缺少花括号而导致的错误,并提高代码的可读性。 8. **对齐规则**: 根据规范¹1-8,所有对齐都应当使用空格键来完成,而不是使用制表符(TAB键)。这是因为不同的编辑器可能会对制表符的宽度有不同的解释,从而导致代码布局的混乱。 9. **缩进规则**: 根据规范¹1-9,函数或过程的开始、结构的定义及循环、判断等语句中的代码都需要采用适当的缩进来保持代码的整洁。 以上是华为C语言软件编程规范中关于排版的一些核心要点。遵循这些规定不仅有助于提升个人的编程习惯,还能增强团队协作的效率,减少后期维护的难
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值