C语言编程规范

C语言编程规范.pdf

C语言国际规范-ISO/IEC 9899:201x.pdf

1 排版

1.1 程序块缩进风格

程序块要采用缩进风格编写,缩进的空格数为 4 个,对齐只使用空格键,不使用 TAB 键。以免用不同的编辑器阅读程序时,因 TAB 键所设置的空格数目不同而造成程序布局不整齐。

1.2 程序中的空行

相对独立的程序块之间、变量说明之后必须加空行。

示例:如下例子不符合规范。

if (!valid_ni(ni))

{

        ... // program code

}

repssn_ind = ssn_data[index].repssn_index;

repssn_ni = ssn_data[index].ni;

 

应如下书写

if (!valid_ni(ni))

{

        ... // program code

}

 

repssn_ind = ssn_data[index].repssn_index;

repssn_ni = ssn_data[index].ni;

1.3 长语句多行书写

较长的语句(>80 字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。

示例:

perm_count_msg.head.len = NO7_TO_STAT_PERM_COUNT_LEN

                       + STAT_SIZE_PER_FRAM * sizeof( _UL );

 

act_task_table[frame_id * STAT_TASK_CHECK_NUMBER + index].occupied

                            = stat_poi[index].occupied;

 

act_task_table[taskno].duration_true_or_false

                            = SYS_get_sccp_statistic_state( stat_item );

 

report_or_not_flag = ((taskno < MAX_ACT_TASK_NUMBER)

                && (n7stat_stat_item_valid (stat_item))

                && (act_task_table[taskno].result_data != 0));

1.4 一行一条语句

示例:如下例子不符合规范。

rect.length = 0;    rect.width = 0;

应如下书写

rect.length = 0;

rect.width = 0;

1.5 if、for、do、while、case、switch、default

if、for、do、while、case、switch、default 等语句自占一行,且 if、for、do、while等语句的执行语句部分无论多少都要加括号{}。

示例:如下例子不符合规范。

if (pUserCR == NULL) return;

应如下书写:

if (pUserCR == NULL)

{

    return;

}

程序块的分界符(如 C/C++语言的大括号‘{’和‘}’)应各独占一行并且位于同一列,同时与引用它们的语句左对齐。在函数体的开始、类的定义、结构的定义、枚举的定义以及 if、for、do、while、switch、case 语句中的程序都要采用如上的缩进方式。

示例:如下例子不符合规范。

for (...) {

        ... // program code

}

 

if (...)  

        {

        ... // program code

        }

 

void example_fun( void )

        {

        ... // program code

        }

 

应如下书写。

for (...)  

{

        ... // program code

}

 

if (...)  

{

        ... // program code

}

 

void example_fun( void )

{

        ... // program code

}

1.6 代码行中的空格

(1) 关键字之后要留空格。象 const 、 virtual 、 inline 、 case  等关键字之后至少要留一个空格,否则无法辨析关键字。象 if 、 for 、 while 等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。

(2) 函数名之后不要留空格,紧跟左括号‘(’,以与关键字区别。

(3) ‘(’向后紧跟,‘)’、‘,’、‘ ; ’向前紧跟,紧跟处不留空格。

(4) ‘,’之后要留空格,如 Function(x, y, z) 。如果‘ ; ’不是一行的结束符号,其后要留空格,如 for (initialization; condition; update)。

(5) 赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如“ = ”、“ += ”   “ >= ”、“ <= ”、“ + ”、“ * ”、“ % ”、“ && ”、“ || ”、“ << ” , “ ^ ”等二元操作符的前后应当加空格。

(6) 一元操作符如“ ! ”、“ ~ ”、“ ++ ”、“ -- ”、“ & ”(地址运算符)等前后不加空格。

(7) 像“[]”、“.”、“->”这类操作符前后不加空格。

2 注释

2.1 文件头部注释

/************************************************************************

Copyright (C), 2007-2018, xxx技术有限公司

文件名:

作者: 软件与应用研发中心 芯片驱动与测试COS开发部

版本: 1.0.0                   

日期: 2018.07.12    

文件功能描述:

对应需求编号:

修改记录: 

    版本:    日期:          作者:          修改内容:

    1.0.0   2014.09.11          xx           初始版本

************************************************************************/

2.2 函数头部注释

/************************************************************************

函数名:

函数功能:

输入参数:

输出参数:

返回值:

***********************************************************************/

2.3 数据结构注释

数据结构声明(包括数组、结构、类、枚举等),如果其命名不是充分自注释的,必须加以注释。对数据结构的注释应放在其上方相邻位置,不可放在下面;对结构中的每个域的注释放在此域的右方。

示例:可按如下形式说明枚举/数据/联合结构。

/* sccp interface with sccp user primitive message name */

enum    SCCP_USER_PRIMITIVE

{

        N_UNITDATA_IND,   /* sccp notify sccp user unit data come */

        N_NOTICE_IND,      /* sccp notify user the No.7 network can not */

                               /* transmission this message */

        N_UNITDATA_REQ,     /* sccp user's unit data transmission request*/

};

2.4 全局变量注释

全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明。

2.5 注释的注意事项

(1) 注释的原则是有助于对程序的阅读理解,在该加的地方都加了,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。

(2) 边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。

(3) 注释的内容要清楚、明了,含义准确,防止注释二义性,错误的注释不但无益反而有害。

(4) 注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面,如放于上方则需与其上面的代码用空行隔开。

(5) 对于所有有物理含义的变量、常量,如果其命名不是充分自注释的,在声明时都必须加以注释,说明其物理含义。变量、常量、宏的注释应放在其上方相邻位置或右方。

示例:

/* active statistic task number */

#define MAX_ACT_TASK_NUMBER 1000

#define MAX_ACT_TASK_NUMBER 1000 /* active statistic task number */

(6) 注释与所描述内容进行同样的缩排。

示例:如下例子,排版不整齐,阅读稍感不方便。

void example_fun( void )

{

/* code one comments */

        CodeBlock One

 

                /* code two comments */

        CodeBlock Two

}

 

应改为如下布局。

void example_fun( void )

{

        /* code one comments */

        CodeBlock One

 

        /* code two comments */

        CodeBlock Two

}

(7) 通过对函数或过程、变量、结构等正确的命名以及合理地组织代码的结构,使代码成为自注释的。清晰准确的函数、变量等的命名,可增加代码可读性,并减少不必要的注释。

(8) 注释格式尽量统一,建议使用“/*  ⋯⋯  */”。

(9) 注释语言应该统一。出于对维护人员的考虑,建议使用中文。

3 命名规则

3.1 总体规则

(1) 函数名包含一个或者多个单词,每一个单词第一个字母大写,其他字母小写,例如Reset, SendData。

(2) 局部变量包含一个单词可以不大写,包含多个单词每一个单词第一个字母大写,其他字母小写,例如value,  DataLength

(3) 全局变量和局部变量命名相同,但是需要以g_开头,例如g_value,  g_DataLength。

(4) 宏要全部大写,单词之间用下划线_分开,例如

#define  UART0_RSTN                     ((uint32_t)0x00000010)

(5) 外设寄存器结构体定义成如下形式PPP_ TypeDef,PPP为外设名字,例如

typedef  struct

{

    __IO uint32_t   PERIOD0;

    __IO uint32_t   DUTY_CYCLE0;

    __IO uint32_t   PERIOD1;

    __IO uint32_t   DUTY_CYCLE1;

    __IO uint32_t   PERIOD2;

    __IO uint32_t   DUTY_CYCLE2;

    __IO uint32_t   PERIOD3;

    __IO uint32_t   DUTY_CYCLE3;

    __IO uint32_t   MODE_EN;

    __IO uint32_t   INTER;

    __IO uint32_t   INTER_EN;

} PWM_TypeDef;

 

typedef struct

{

  __IO uint32_t   CCR;

  __IO uint32_t   CNDTR;

  __IO uint32_t   CPAR;

  __IO uint32_t   CMAR;

} DMA_Channel_TypeDef;

3.2 外设函数命名

外设函数的命名以该外设的缩写加下划线为开头,例如:SPI_SendData。在函数名中,只允许存在一个下划线,用以分隔外设缩写和函数名的其它部分。

名为 PPP_Init 的函数,其功能是根据 PPP_InitTypeDef 中指定的参数,初始化外设 PPP,例如  TIM_Init.

名为 PPP_DeInit 的函数,其功能为复位外设 PPP 的所有寄存器至缺省值,例如  TIM_DeInit.

名为 PPP_StructInit 的函数,其功能为通过设置 PPP_InitTypeDef  结构中的各种参数来定义外设的功能,例如:UART_StructInit

名为 PPP_Cmd 的函数,其功能为使能或者失能外设 PPP,例如:SPI_Cmd.

名为 PPP_ITConfig 的函数,其功能为使能或者失能来自外设 PPP 某中断源,例如:  RCC_ITConfig.

名为 PPP_DMAConfig 的函数,其功能为使能或者失能外设 PPP 的 DMA 接口,例如:TIM1_DMAConfig.

用以配置外设功能的函数,总是以字符串“Config”结尾,例如 GPIO_PinRemapConfig.

名为 PPP_GetFlagStatus 的函数,其功能为检查外设 PPP 某标志位被设置与否,例如:I2C_GetFlagStatus.

名为 PPP_ClearFlag 的函数,其功能为清除外设 PPP 标志位,例如:I2C_ClearFlag.

名为 PPP_GetITStatus 的函数,其功能为判断来自外设 PPP 的中断发生与否,例如:I2C_GetITStatus.

名为 PPP_ClearITPendingBit 的 函 数 , 其 功 能 为 清 除 外 设 PPP 中 断 待 处 理 标 志 位 , 例 如 : I2C_ClearITPendingBit.  

4 编码规则

4.1 变量类型

建议使用stdint.h标准库中的数据类型:int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t

4.2 布尔型

typedef enum

{

    FALSE = 0,

    TRUE = !FALSE

} bool;

4.3 标志位状态类型

我们定义标志位类型(FlagStatus type)的 2 个可能值为“设置”与“重置”(SET or RESET)。

typedef enum

{

    RESET = 0,

    SET = !RESET

} FlagStatus;

4.4 功能状态类型

我们定义功能状态类型(FunctionalState  type)的 2 个可能值为“使能”与“失能”(ENABLE or DISABLE)。

typedef enum

{

    DISABLE = 0,

    ENABLE = !DISABLE

} FunctionalState;

4.5 错误状态类型

我们错误状态类型类型(ErrorStatus  type)的 2 个可能值为“成功”与“出错”(SUCCESS or ERROR)。

typedef enum

{

    ERROR = 0,

    SUCCESS = !ERROR

} ErrorStatus;

5 可读性

5.1 运算符优先级

注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级。防止阅读程序时产生误解,防止因默认的优先级与设计思想不符而导致程序出错。

示例:下列语句中的表达式

word = (high << 8) | low          (1)

if ((a | b) && (a & c))            (2)

if ((a | b) < (c & d))              (3)

如果书写为

high << 8 | low

a | b && a & c

a | b < c & d

由于

high << 8 | low = ( high << 8) | low,

a | b && a & c = (a | b) && (a & c),

(1)(2)不会出错,但语句不易理解;

a | b < c & d = a | (b < c) & d,(3)造成了判断条件出错。

5.2 避免使用数字

避免使用不易理解的数字,用有意义的标识来替代。涉及物理状态或者含有物理意义的常量,不应直接使用数字,必须用有意义的枚举或宏来代替。

示例:如下的程序可读性差。

if (Trunk[index].trunk_state == 0)

{

        Trunk[index].trunk_state = 1;

        ...    // program code

}

应改为如下形式。

#define TRUNK_IDLE 0

#define TRUNK_BUSY 1

if (Trunk[index].trunk_state == TRUNK_IDLE)

{

        Trunk[index].trunk_state = TRUNK_BUSY;

        ...    // program code

}

6 程序效率

6.2 循环体内工作量最小

应仔细考虑循环体内的语句是否可以放在循环体之外,使循环体内工作量最小,从而提高程序的时间效率。

6.3 运用汇编

要仔细地构造或直接用汇编编写调用频繁或性能要求极高的函数。

6.4 在多重循环中,应将最忙的循环放在最内层

减少 CPU 切入循环层的次数。

示例:如下代码效率不高。

for (row = 0; row < 100; row++)

{

        for (col = 0; col < 5; col++)

        {

                sum += a[row][col];

        }

}

 

可以改为如下方式,以提高效率。

for (col = 0; col < 5; col++)

{

        for (row = 0; row < 100; row++)

        {

                sum += a[row][col];

        }

}

6.5 避免循环体内含判断语句

目的是减少判断次数。循环体中的判断语句是否可以移到循环体外,要视程序的具体情况而言,一般情况,与循环变量无关的判断语句可以移到循环体外,而有关的则不可以。

示例:如下代码效率稍低。

for (ind = 0; ind < MAX_RECT_NUMBER; ind++)

{

        if (data_type == RECT_AREA)

        {

                area_sum += rect_area[ind];

        }

        else

        {

                rect_length_sum += rect[ind].length;

                rect_width_sum += rect[ind].width;

        }

}

 

因为判断语句与循环变量无关,故可如下改进,以减少判断次数。

if (data_type == RECT_AREA)

{

        for (ind = 0; ind < MAX_RECT_NUMBER; ind++)

        {

                area_sum += rect_area[ind];

        }

}

else

{

        for (ind = 0; ind < MAX_RECT_NUMBER; ind++)

        {

                rect_length_sum += rect[ind].length;

                rect_width_sum    += rect[ind].width;

        }

}

6.6 尽量用乘法、位与或其它方法代替除法

例如:remainder = count % 16;

可以改为remainder = count & 15;

7 声明与定义

7.1 函数

函数应当具有原型声明,原型的使用使得编译器能够检查函数定义和调用的完整性。如果没有原型,就不会迫使编译器检查出函数调用当中的一定错误(比如,函数体具有不同的参数数目,调用和定义之间参数类型的不匹配)。事实证明,函数接口是相当多问题的肇因,因此本规则是相当重要的。

对外部函数来说,我们建议采用如下方法,在头文件中声明函数(亦即给出其原型),并在所有需要该函数原型的代码文件中包含这个头文件。

为具有内部链接的函数给出其原型也是良好的编程实践。如果一个函数只是在同一文件中的其他地方调用,那么就用 static。使用 static 存储类标识符将确保标识符只是在声明它的文件中是可见的,并且避免了和其他文件或库中的相同标识符发生混淆的可能性。

7.2 全局变量

(1) 良好的编程实践是,在不必要的情况下避免使用全局变量。

(2) 全局变量如果作用域只是在同一文件中的其他地方调用,就用 static限定其作用域。

(3) 全局变量如果作用域是多个文件,定义写在xxx.c文件中,声明写在对应的xxx.h文件中,并且用extern 关键字声明。

8 代码编辑、编译、审查

(1) 打开编译器的所有告警开关对程序进行编译。对编译器报出的警告要认真对待,尽量消除所有的警告。

(2) 在产品软件(项目组)中,要统一编译开关选项。

(3) 通过代码走读及审查方式对代码进行检查。

(4) 代码走读主要是对程序的编程风格如注释、命名等以及编程时易出错的内容进行检查,可由开发人员自己或开发人员交叉的方式进行;代码审查主要是对程序实现的功能及程序的稳定性、安全性、可靠性等进行检查及评审,可通过自审、交叉审核或指定部门抽查等方式进行。

(5) 测试部测试产品之前,应对代码进行抽查及评审。

(6) 编写代码时要注意随时保存,并定期备份,防止由于断电、硬盘损坏等原因造成代码丢失。

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值