好记性不如烂笔头o(^▽^)o

系列的文章:
《C语言的艺术之——头文件》
《C语言的艺术之——函数》
《C语言的艺术之——标识符命令与定义》
《C语言的艺术之——变量》
C语言的艺术之——注释
C语言的艺术之——排版与格式
C语言的艺术之——安全性

编码原则:

清晰:易于维护、易于重构
简洁:易于理解并且易于实现
适合的风格:尽量与原有代码保持风格一致

C语言的艺术之——头文件

  • C语言的艺术之头文件

    • 1头文件中适合放置接口的声明不适合放置实现
    • 2头文件应当职责单一
    • 3头文件应向稳定的方向包含
    • 4每一个c文件应有一个同名h文件用于声明需要对外公开的接口
    • 5禁止头文件循环依赖
    • 6ch文件禁止包含用不到的头文件
    • 7头文件应当自包含
    • 8总是编写内部include保护符define 保护
    • 9禁止在头文件中定义变量
    • 10只能通过包含头文件的方式使用其他c提供的接口禁止在c中通过extern的方式使用外部函数接口变量
    • 11禁止在extern C中包含头文件

1、头文件中适合放置接口的声明,不适合放置实现

头文件中应放置对外部的声明,如对外提供的函数声明、宏定义、类型定义等。

内部使用的函数(相当于类的私有方法)声明不应放在头文件中。
内部使用的宏、枚举、结构定义不应放入头文件中。

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

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

2、头文件应当职责单一

头文件过于复杂,依赖过于复杂是导致编译时间过长的主要原因。

3、头文件应向稳定的方向包含

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

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

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

  一旦把私有定义、声明放到独立的头文件中,就无法从技术上避免别人include之,难以保证这些定义最后真的只是私有的。

示例:对于如下场景,如在一个.c中存在函数调用关系:

void foo()
{bar();
}void bar()
{Do something;
}

必须在foo之前声明bar,否则会导致编译错误。
这一类的函数声明,应当在.c的头部声明,并声明为static的,如下:

static void bar();void foo()
{bar();
}void bar()
{Do something;
}

5、禁止头文件循环依赖

头文件循环依赖,指a.h包含b.h,b.h包含c.h,c.h包含a.h之类导致任何一个头文件修改,都导致所有包含了a.h/b.h/c.h的代码全部重新编译一遍。而如果是单向依赖,如a.h包含b.h,b.h包含c.h,而c.h不包含任何头文件,则修改a.h不会导致包含了b.h/c.h的源代码重新编译。

6、.c/.h文件禁止包含用不到的头文件

很多系统中头文件包含关系复杂,开发人员为了省事起见,可能不会去一一钻研,直接包含一切想到的头文件,甚至有些产品干脆发布了一个god.h,其中包含了所有头文件,然后发布给各个项目组使用,这种只图一时省事的做法,导致整个系统的编译时间进一步恶化,并对后来人的维护造成了巨大的麻烦。

7、头文件应当自包含

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

示例:
  如果a.h不是自包含的,需要包含b.h才能编译,会带来的危害:
  每个使用a.h头文件的.c文件,为了让引入的a.h的内容编译通过,都要包含额外的头文件b.h。
  额外的头文件b.h必须在a.h之前进行包含,这在包含顺序上产生了依赖。
注意:
  该规则需要与“.c/.h文件禁止包含用不到的头文件”规则一起使用,不能为了让a.h自包含,而在a.h中包含不必要的头文件。a.h要刚刚可以自包含,不能在a.h中多包含任何满足自包含之外的其他头文件。

8、总是编写内部#include保护符(#define 保护)

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

  通常的手段是为每个文件配置一个宏,当头文件第一次被包含时就定义这个宏,并在头文件被再次包含时使用它以排除文件内容。
  所有头文件都应当使用#define 防止头文件被多重包含,命名格式为

FILENAME_H

为了保证唯一性,更好的命名是

PROJECTNAME_PATH_FILENAME_H

注:没有在宏最前面加上单下划线”“,是因为一般以单下划线”“和双下划线”_”开头的标识符为ANSI C等使用,在有些静态检查工具中,若全局可见的标识符以”“开头会给出告警。
  定义包含保护符时,应该遵守如下规则:
1)保护符使用唯一名称;
2)不要在受保护部分的前后放置代码或者注释。

示例:假定VOS工程的timer模块的timer.h,其目录为VOS/include/timer/timer.h,应按如下方式保护:

#ifndef VOS_INCLUDE_TIMER_TIMER_H
#define VOS_INCLUDE_TIMER_TIMER_H
...
#endif

也可以使用如下简单方式保护:

#ifndef TIMER_H
#define TIMER_H..
#endif

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

9、禁止在头文件中定义变量

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

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

若a.c使用了b.c定义的foo()函数,则应当在b.h中声明extern int foo(int input);并在a.c中通过#include

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

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

  在extern “C”中包含头文件,可能会导致被包含头文件的原有意图遭到破坏。例如,存在a.h和b.h两个头文件:

#ifndef A_H__
#define A_H__ #ifdef __cplusplus
void foo(int);
#define a(value) foo(value)
#else
void a(int)
#endif #endif /* A_H__ *
#ifndef B_H__
#define B_H__ #ifdef __cplusplus
extern "C" {
#endif #include "a.h" void b(); #ifdef __cplusplus
}
#endif #endif /* B_H__ *

使用C++预处理器展开b.h,将会得到

extern "C" { void foo(int); void b();
}

  按照a.h作者的本意,函数foo是一个C++自由函数,其链接规范为”C++”。但在b.h中,由于#include “a.h”被放到了extern “C” { }的内部,函数foo的链接规范被不正确地更改了。

示例:错误的使用方式:

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

正确的使用方式:

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

C语言的艺术之——头文件相关推荐

  1. C语言两种导入头文件的区别

    C语言两种导入头文件的区别 #include<stdio.h> 和 #include"stdio.h" 这两种导入头文件的区别: 区别在于编译器查找头文件的顺序, &q ...

  2. linux c之c语言符合标准的头文件和linux常用头文件

    1.C语言符合标准的头文件 #include <assert.h> //设定插入点 #include <ctype.h> //字符处理 #include <errno.h ...

  3. 09C语言高级篇之头文件的编写

    C语言高级篇之头文件的编写 1.extern理解 ​ 1.extern,声明,当然也可以同时定义(一般没必要),函数可以省略,使用该关键字定义变量称做"外部变量声明" ​ 2.ex ...

  4. 【酷熊科技】工作积累 ----------- Unity3D / c 语言 使用 Stringbuilder 引用头文件 using System.Text...

    使用 StringBuilder Unity3D / c 语言 使用 Stringbuilder 引用头文件 using System.Text 简述StringBuilder和String的区别? ...

  5. c语言exit在哪个头文件_C语言函数执行成功时,返回1和返回0,究竟哪个好?

    基本上,没有人会将大段的C语言代码全部塞入 main() 函数,更好的做法是按照复用率高,耦合性低的原则,尽可能的将代码拆分不同的功能模块,并封装成函数.C语言代码的组合千变万化,因此函数的功能可能会 ...

  6. C语言再学习 -- 常用头文件和函数(转)

    参看:C/C++常用头文件及函数汇总 linux常用头文件如下: POSIX标准定义的头文件 <dirent.h>        目录项 <fcntl.h>         文 ...

  7. mp4文件时长 c++源码_【C语言】如何使用头文件 .h 编译 C 源码!so easy!

    在 C 语言中,头文件或包含文件通常是一个源代码文件,程序员使用编译器指令将头文件包含进其他源文件的开始(或头部),由编译器在处理另一个源文件时自动包含进来. 一个头文件一般包含类.子程序.变量和其他 ...

  8. go语言调用c 的头文件 so,Golang生成共享库(shared library)以及Golang生成C可调用的动态库.so和静态库.a...

    Golang类似于C的静态语言,效率也接近于C,如果Golang也可以导出可供C调用的库,那可以和很多高级语言say goodbye了,goodbye似乎又有点武断,但至少说,Golang可以做很多事 ...

  9. 单片机c语言怎样添加自定义头文件,单片机C语言编程与或|头文件常见问题

    一.常见问题 1.头文件reg51.h和reg52.h其实是一样的,大家两个都可以用. 2.main()前面的void可加可不加,反正都是无返回值函数. 3.不是每一个程序都要用到死循环while(1 ...

最新文章

  1. python使用fpdf生成pdf文件:配置多种语言字体写入多种文字
  2. Niblack二值化方法的实现
  3. visual studio安装python插件_Visual Studio 2012 Ultimate 上安装 Python 开发插件 PTVS
  4. matplotlib画图时间长_Python学习第86课-数据可视化之matplotlib介绍
  5. 解决一条高难度的,关于时间段 数据汇总问题
  6. 再读《精通css》00
  7. 暴力破解sshd服务的密码的小技巧
  8. 在 Go 语言中使用 Log 包--转自GCTT
  9. 计算机应用与医学信息基础知识,第一篇医学信息基础知识.PDF
  10. 1602 c语言驱动程序,51单片机驱动LCD1602程序设计(C语言)很详细的教程
  11. 什么是弱网测试?为什么要进行弱网测试?怎么进行弱网测试?
  12. lua如何将用户ip转为地理位置信息
  13. 吃着火锅唱着歌,却被操作系统砸了饭碗,开发者如何反击?
  14. shader篇-纹理-渐变纹理
  15. @kubernetes(k8s)使用adm安装实现keepalived高可用
  16. ctfshow_密码3
  17. pandas计算excel两列的日期差
  18. On the Generality of Facial Forgery Detection论文原文翻译以及阅读笔记
  19. 单链表的头插法与尾插法详解
  20. 径向基函数和粗糙集在进化多目标优化中的应用

热门文章

  1. 基于android的数码交流社区
  2. 按键精灵html代码,按键精灵键盘代码~
  3. 学计算机的kaocpa,注册会计师考试CPA要避开四种假象
  4. 【转】Win32子窗口控件(按钮、编辑框、静态框、滚动条等)!!
  5. 山东大学计算机学院预推免,【更新】2021届计算机相关预推免信息(个人使用)...
  6. 怎样有效的恢复电脑回收站文件丨有效数据有哪些
  7. 如何得到一个向量的垂直向量
  8. java日历制作_java简单日历的制作代码
  9. 木马病毒隐身穿墙术解密之文件注入和反弹连接
  10. Python_多线程错误及解决