C语言编码规范

0 头文件注释

原则
用于说明本文件的用途,作者,协议等信息

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//******************************************************************************************
//!
//! \file FIFO.h
//! \brief Genernal FIFO Model Interface.
//! You can use uniform FIFO Model to manager Any type of data element.
//! \author cedar
//! \date 2013-12-16
//! \email xuesong5825718@gmail.com
//!
//! \license
//!
//! Copyright (c) 2013 Cedar MIT License
//!
//! Permission is hereby granted, free of charge, to any person obtaining a copy
//! of this software and associated documentation files (the "Software"), to deal
//! in the Software without restriction, including without limitation the rights to
//! use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
//! the Software, and to permit persons to whom the Software is furnished to do so,
//! subject to the following conditions:
//!
//! The above copyright notice and this permission notice shall be included in all
//! copies or substantial portions of the Software.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
//! IN THE SOFTWARE.
///
//******************************************************************************************

1 头文件防冲突宏

原则
用于防止头文件被多次包含,从而导致编译,链接错误

示例

1
2
3
4
5
6
#ifndef __XXX_H__    // XXX是模块名字
#ifdef __XXX_H__

// 头文件实现代码

#endif //__XXX_H__

2 C++保护宏

原则
每个头文件中,必须包含该保护宏,从而避免C++的重命名机制导致C链接失败

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef __XXX_H__    // XXX是模块名字
#ifdef __XXX_H__

#ifdef __cpluscplus
extern "C"
{
#endif

// 头文件实现代码

#ifdef __cpluscplus
{
#endif

#endif //__XXX_H__

3 宏函数

原则
一般情况下,建议用内联函数代替宏函数,如果必须使用,则

  • 代码中使用到的任何入口参数,都必须包含()
  • 如果代码行数大于一行,则必须用do{ }while(0)包裹

示例1

1
2

#define MAX(a, b) (a)>(b)?(a):(b)

示例2

1
2
3
4
5
6
7

#define RESET_CPU() \
do \
{ \
__disable_fault_irq(); \
NVIC_SystemReset(); \
}while(0)

4 函数命名规范

原则

  • 包含模块名
  • 包含功能类型

模块名全部大写,功能类型采用驼峰式命名法

示例

1
2
void LED_SetState(eState state);
eState LED_GetState(void);

这里LED表示模块的名称,SetStateGetState分别表示对应的操作

5 函数注释模板

原则
采用标准Doxygen注释风格,必须包含

  • 函数名字
  • 函数功能
  • 函数入口参数
  • 函数返回值

可适当包含下面信息

  • 函数使用条件
  • 函数注意事项

注: 如果没有入口参数或者出口参数,则写

示例

1
2
3
4
5
6
7
8
9
10
11
//******************************************************************************************
//
//! \brief Get an element from FIFO.
//!
//! \param [in] pFIFO is the pointer of valid FIFO.
//! \param [out] pElement is the address of element you want to get
//!
//! \retval 0 if operate successfully, otherwise return -1.
//
//******************************************************************************************
extern int FIFO_Get(FIFO_t* pFIFO, void* pElement);

6 代码注释

原则

  • 函数注释,使用标准Markdown注释风格
  • 宏变量后,使用//注释
  • 头文件中,同类符号的注释起始位置,需保持一致,从而保证代码整洁

示例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//! FIFO Memory Model
typedef struct
{
uint8_t* pStartAddr; //!< FIFO Memory Pool Start Address
uint8_t* pEndAddr; //!< FIFO Memory Pool End Address
uint32_t Free; //!< The capacity of FIFO
uint32_t Used; //!< The number of elements in FIFO
uint8_t UnitSize; //!< FIFO Element Size(Unit: Byte)
uint8_t* pReadIndex; //!< FIFO Data Read Index Pointer
uint8_t* pWriteIndex; //!< FIFO Data Write Index Pointer
}FIFO_t;

//! FIFO Memory Model (Single Byte Mode)
typedef struct
{
uint8_t* pStartAddr; //!< FIFO Memory Pool Start Address
uint8_t* pEndAddr; //!< FIFO Memory Pool End Address
uint32_t Free; //!< The capacity of FIFO
uint32_t Used; //!< The number of elements in FIFO
uint8_t* pReadIndex; //!< FIFO Data Read Index Pointer
uint8_t* pWriteIndex; //!< FIFO Data Write Index Pointer
}FIFO_S_t;

7 switch

原则
在switch中,一般情况下,

  • 条件分支中必须包含break
  • 条件分支中,无论语句块,都必须包含完整的{}
  • 最后一个必须包含default

从而避免因为忘记break造成的bug,减轻调试负担

如下所示

示例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

switch(Status)
{
case POWER_ON:
{
// 上电自检
}
break;
case POWER_OFF:
{
// 掉电处理代码
}
break;
case EVENT_TIMER:
{
// 定时器事件处理代码
}
break;
default:
{
// 默认处理规则
}
}

在某些情况下,需要将多个分支合并,这时要求:

  • 在原分支包含break处,编写特殊注释,说明这里是特意省去break

如下所示
上电自检后,进入系统初始化,初始化各种外设,所以这里不需要break
直接从POWER_ON进入SYS_INIT

示例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

switch(Status)
{
case POWER_ON:
{
// 上电自检
}
//break; NOTE:上电自检后,进入系统初始化,初始化各种外设,所以这里不需要break
case SYS_INIT:
{
// 初始化代码
}
break;
case POWER_OFF:
{
// 掉电处理代码
}
break;
case EVENT_TIMER:
{
// 定时器事件处理代码
}
break;
default:
{
// 默认处理规则
}
}

8 if

原则
在任何情况下,如果出现ifelseelse if,其后面无论语句块的长短,都必须包含完整的{}
从而有效避免一些潜在bug

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// 例子1
if(LED_OFF == LedStatus)
{
LedStatus = LED_ON;
}

// 例子2
if(LED_OFF == LedStatus)
{
LedStatus = LED_ON;
}
else
{
LedStatus = LED_OFF;
}

9 goto

原则
一般情况下,不允许使用goto语句,因为复杂的跳转会导致:

  • 可读性变差
  • 后期维护困难

但在错误处理时,goto可以极大的简化编程逻辑,所以我们规定
只能在错误处理中使用goto

同时要求:

  • 标号必须大写
  • 标号第一个字母为L

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

if(步骤1)
{
//发生错误
goto L_ERROR:
}

if(步骤2)
{
//发生错误
goto L_ERROR:
}

if(步骤3)
{
//发生错误
goto L_ERROR:
}

L_ERROR:
//错误处理代码
//退出函数