专业的C头文件设计和重构指南
头文件设计要点:
1、 头文件注释
2、 guard define
3、 尽量不要在头文件中暴露数据结构
4、 要自包含,保证头文件独立编译和功能正确
5、 函数声明前加XXX_API利于拓展
6、 宏的定义
7、 对外提供的头文件放于指定的目录结构
1. 文件头注释
应该加在每个头文件的顶部,必须包含版权许可、功能说明、作者和创建日期。
例如:
a) /*
b) * Copyright (c) Huawei Technologies Co., Ltd. XXXX. All rights reserved.
c) * Description: 文件功能描述
d) * Author: 王二
e) * Create: 2012-12-22
f) */
2. guard define
整个头文件应该在guard define之间, 为防止头文件被多重包含,所有头文件都应当使用 #define 作为包含保护;
定义包含保护符时,应该遵守如下规则:
ü 保护符使用唯一名称;
ü 建议考虑项目源代码树顶层以下的文件路径 不要在受保护部分的前后放置代码或者注释,文件头注释除外。
假定 VOS 工程的 timer 模块的 timer.h,其目录为 vos/include/timer.h 。其保护符若使用 'TIME_H' 很容易不唯 一,所以使用项目源代码树的全路径,如:
如:
1) #ifndef VOS_INCLUDE_TIMER_H
2) #define VOS_INCLUDE_TIMER_H
3) //声明对外的接口
4) #endif
另外,如果这个头文件可能给c++使用,要加上
1) #ifdef __cplusplus
2) extern "C" {
3) #endif
4) //声明对外的接口
5) #ifdef __cplusplus
6) }
7) #endif
注:禁止在 extern "C" 中包含头文件
3. 尽量不要在头文件中暴露数据结构
这样可以用户降低对你的实现的依赖,也减少了用户的编译时间
1) typedef struct lua_State lua_State;
2) LUA_API lua_State *lua_open (void);
3) LUA_API void lua_close (lua_State *L);
可以看到虽然用户会一直使用lua_State,但是并不知道lua_State的结构是什么
从一个使用lua的例子程序可以看出:
1) #include "lua.h"
2) #include "lauxlib.h"
3) #include "lualib.h"
4)
5) int main(int argc, char *argv[])
6) {
7) lua_State *L = lua_open();
8) const char *buf = "var = 100";
9) int var ;
10) luaopen_base(L);
11) luaopen_io(L);
12) lua_dostring(L, buf);
13) lua_getglobal(L, "var");
14) var = lua_tonumber(L, -1);
15) lua_close(L);
16) return 0;
17)}
4. 要自包含,保证头文件独立编译和功能正确
i. 能独立编译
要保证本头文件在任何使用场景下,都能独立编译。其中,相对路径引入就是一个案例。如:
#include <comm_type.h>不是标准库情况下,可能无法引入相关文件,可改成:
#include “../../XXX/XXX/ comm_type.h”
ii. 对外提供功能正确
头文件能独立编译了,但不一定能对外提供的功能是正确的。对外提供的功能可能还需要代码适配,以保证功能正确。
5. 下面是一个私有宏暴露给模块外部,致使相关模块使用时能编译通过,但功能却不正确。
解决办法就是不要暴露数据本身,通过接口反馈给外部模块数据值即可。(这个问题,也是尽量不要在头文件中暴露数据结构的一个典型案例)
案例:
1) 功能失败点,从图1看到rsaM.Datalen = RSA_LEN,再从图2看到RSA_LEN != pData->dataLen的应该是相等,但实际结果却是错误的,这就是问题暴露点

图1

图2
2) 继续追查RSA_LEN,它是RSA_KEY_LEN的重定义,见图3。而从图4可以看出RSA_KEY_LEN的值由一个开关控制,编译环境不一样,值不同

图3

图4
3) 问题在于本模块对外提供能力时,没有考虑到编译环境的要求,应用方也没有明白这点,应用方无法掌控这个宏的编译环境,从而无法得到正确值引发了这个问题。解决办法就是通过接口对外提供值,而不是数据结构本身。

5. 函数声明前加XXX_API利于拓展
iii. Lua的例子,如果定义了LUA_API就是给LUA内部使用的,如果没定义LUA_API就是for user的
下面是内部使用的例子:
1) #ifndef LUA_API
2) #define LUA_API
3) #endif
4)
5) LUA_API lua_State *lua_open (void);
下面是外部使用的例子:
6) #ifndef LUA_H
7) #define LUA_H
8) #endif
9)
10)LUA_H lua_State *lua_close (void);
6. 宏的定义时,尽量使用括号来包住所定义的对象
i. 宏定义嵌入某个表达式使用,用括号将其包住。此方法如果用于语句环境将会出语法错误
1) #define LUA_TNONE (-1)
2)
3) #define lua_register(L,n,f) \
4) (lua_pushstring(L, n), \
5) lua_pushcfunction(L, f), \
6) lua_settable(L, LUA_GLOBALSINDEX))
ii. 宏定义当语句使用,用do while(0)将其包住。此方法如果用于表达式环境将会出语法错误
7) #define DOSOMETHING () \
8) do{
9) foo1();
10) foo2();
11) }while(0)
7. 对外提供的头文件放于指定的目录结构
一般应该使用一个单独的include目录来包含要发布的头文件,但不应该把内部使用的头文件包含进去。例如:Lua的include目录只包含了三个头文件lauxlib.h , lua.h, lualib.h,很简洁
专业的C头文件设计和重构指南的更多相关文章
- c++ 私有函数 头文件设计
clock.h #ifndef CLOCK_H_INCLUDED #define CLOCK_H_INCLUDED class Clock {public: static void HandleExd ...
- 让QT编译快一点(增加基础头文件)
姚冬,中老年程序员 进藤光.杨个毛.欧阳修 等人赞同 我是来反对楼上某些答案的.我曾经用MFC写了金山词霸(大约20多万行),又用Qt写了YY语音(大约100多万行),算是对两种框架都比较有经验.纠正 ...
- ArcCore重构-头文件引用问题的初步解决
基于官方arc-stable-9c57d86f66be,AUTOSAR版本3.1.5 基本问题 1. 头文件引用混乱,所有头文件通过从搜索路径(-I)中引用,存在名称污染问题,需加入路径信息: ...
- C++ 头文件系列(array)
注意,该头文件仅在C++11中标准才开始出现. 简介 与语言内置的数组一样, array类模版支持几乎所有内置数组包含的特性: 顺序的(sequence) 内存连续的(contiguous stora ...
- C++解析头文件-Qt自动生成信号声明
目录 一.瞎白话 二.背景 三.思路分析 四.代码讲解 1.类图 2.内存结构声明 3.QtHeaderDescription 4.私有函数讲解 五.分析结果 六.下载 一.瞎白话 时间过的ZTMK, ...
- minix2.0内核组织结构与公用头文件说明
Minix2.0操作系统的源代码由两个目录组成:include/目录和src/目录 include/目录包含了操作系统所有的头文件(即.h文件) src/目录下包含了操作系统所有的源文件(.c或.s文 ...
- Objective-C声明在头文件和实现文件中的区别
Objective-C声明在头文件和实现文件中的区别 转自codecloud(有整理) 调试程序的时候,突然想到这个问题,百度一下发现有不少这方面的问答,粗略总结一下: 属性写在.h文件中和在.m文件 ...
- c++ 头文件包含问题-include&class
http://blog.csdn.net/jiajia4336/article/details/8996254 前向声明概念(forward declaration) 在程序中引入了类类型的B.在声明 ...
- 基于WDF的PCI/PCIe接口卡Windows驱动程序(3)- 驱动程序代码(头文件)
原文出处:http://www.cnblogs.com/jacklu/p/4679304.html 在WDF的PCIe驱动程序中,共有四个.h文件(Public.h Driver.h Device ...
- C/C++关于string.h头文件和string类
学习C语言时,用字符串的函数例如stpcpy().strcat().strcmp()等,要包含头文件string.h 学习C++后,C++有字符串的标准类string,string类也有很多方法,用s ...
随机推荐
- 为什么Index Only Scan却还需要访问表
在实际SQL优化工作中,我们经常会发现SQL 执行计划明明是 "Index Only Scan",但执行计划后面却有 "Heap Fetches: x" ,也就 ...
- [CG] 顶点动画贴图 (Vertex Animation Texture, VAT)
什么是顶点动画? 简单来说,通过改变网格顶点的位置,使网格变形从而做成的动画.顶点动画的灵活度要远远高于骨骼动画.骨骼动画是靠骨骼(一堆有层级结构的节点,数量应该是远远小于网格顶点的数量的)的变化来驱 ...
- [Python]-torchvision.transforms模块-图像预处理
PyTorch框架中常用torchvision模块来辅助计算机视觉算法的搭建,transforms用于图像的预处理. from torchvision import transforms 预处理操作集 ...
- 在终端启动Python时报错的解决
最近,在终端启动Python时,报了一个错误: 1 Failed calling sys.__interactivehook__ 2 Traceback (most recent call last) ...
- 【学习笔记】Vins-Mono论文阅读笔记(二)
估计器初始化简述 单目紧耦合VIO是一个高度非线性的系统,需要在一开始就进行准确的初始化估计.通过将IMU预积分与纯视觉结构进行松耦合对齐,我们得到了必要的初始值. 理解:这里初始化是指通过之前imu ...
- kubernetes为容器定义环境变量
示例Pod 的配置文件 envars.yaml Copy envars.yaml to clipboard apiVersion: v1 kind: Pod metadata: name: envar ...
- 在 Linux 上为特定的用户或用户组启用或禁用 SSH
由于你的公司标准规定,你可能只能允许部分人访问 Linux 系统.或者你可能只能够允许几个用户组中的用户访问 Linux 系统.那么如何实现这样的要求呢?最好的方法是什么呢?如何使用一个简单的方法去实 ...
- 常量的定义(const和#define)
定义常量的方法 //均要在调用前(区别全局变量!!) 1.使用#define预处理器 2.使用const关键字 1.#define #define 常量名 常量值 //定义形式,常量名不可以是数字开头 ...
- 改善C#程序的方法-1 操作字符串
正确操作字符串 引言: 字符串是使用很频繁的一种数据类型. 如果使用不慎,则会为一次字符串操作所带来的额外性能开销而付出代价. 下面从这几个方面来探讨如何正确操作字符串: 1.确保尽量少的装箱,尽可能 ...
- 洛谷P4197 Peaks (Kruskal重构树)
读题,只经过困难值小于等于x的路径,容易想到用Kruskal重构树:又要查询第k高的山峰,我们选择用主席树求解. 先做一棵重构树,跑一遍dfs,重构树中每一个非叶子节点对应一段区间,我们开range[ ...