学校里学不到的C语言教程之4:奇怪的第一行代码与重复的函数名
当我们学习 C 语言一定时间后,一定会很熟悉在编写各种功能代码逻辑之前一开始就会要 include 一堆的 h 头文件。例如我们大家都知道的 "#include
当那些有心的同学找到 winsock.h 的代码时一定会看到里面只是些函数的声明,最多再加点结构体的声明。直接把函数体写在 h 文件里的可能性非常之小(除非是性能需要的 inline 函数)。在我们失望之余一定还会惊奇的发现每个库的 h 文件开关都会有一行奇怪的代码,例如:
奇怪的宏定义
而这行代码就是 C/C++ 语言里特有的宏定义。不过我们这里并不打算讨论宏的写法。单单只说说这行代码的作用。要理解这行代码的作用,我们先来看看如果我们自己来写一个函数给别人使用(或者是方便自己重用)会发生什么。
这对于一名合格的在校生也是很容易的:那就是定义一个函数,把要做的事情写在里面就行了,以后要使用相同功能时不用再重写,直接调用这个函数就行了。没错,学会写好函数就是大家走上正式的开发人员的一个标志,在编程中还不习惯将功能写成函数或者类的话那么一看就是一位"新"新手。好了,当我们函数越来越多时我们要怎样做呢?好,按功能分别放到各个 C 文件中,然后 include 就行。没错大家应该都知道要这样做,不过当我们这样做的时候会有个很棘手的问题:那就是函数名会报重复。即使我们只有一函数的情况下,这种事情也会发生。例如下图:
两次include就会出错
你会说,谁让你 include 两次同一个文件的。确实如果一个只有一个函数的文件被 include 进来两次的话那么它也是会引发函数已经定义过的错误警告的。但是我要说的是,这是重复定义造成的,但却并不是 include 两次造成的 -- 即我们有办法 include 两次也正常 -- 之所以要学会这种办法是因为 C/C++ 的 include 会嵌套,在两个不同的文件中都 include 同一个文件就会引入这个文件两次,那么业界是怎么解决这个问题的呢?我们把 socket 的头文件 include 两次会怎样呢?见下图:
编译成功了
可以看到,两次 "#include
答案要回到 socket 的头文件的第一行代码上来,其实这不是一行代码,而是由三行代码组成的防代码重入"模块",这是目前 C 语言里普遍使用的方法,代码如下:
#ifndef _WINSOCK_H#define _WINSOCK_H//所有的代码都写在这个位置#endif
这实际上是利用的 C 语言的宏的特性来阻止一函数或者结构体或者定义的重复声明。否则就会报已存在的错误。另外还有一个知识点是要告诉初学者的:其实本质上 C 语言并不知道它 include 的是 h 还是 c 文件。所以以上规则无论对 h 还是对 c 都都是成立的。不过话说回头,这在现在的 C++ 环境中由于对 c 和 cpp 文件的解释不尽相同,以上的规则其实打了折扣。解决办法是加上 extern "C" 的说明。关于这个 extern "C" 我们以后再说吧。这篇文章大家只要记住这个通用的防止重入的做法就行了。
另外这种防止重入的做法在定义时,通常会在前面加上一个或者两个下划线,目的就是为了尽可能不能成为唯一标识。不过现在也有一些函数或者变量喜欢在前面加下划线,所以不得已,现在的做法是在后面又再补上一两个下划线。所以最终我们看到的这个防止重入的定义通常是这样 "__文件名_文件后缀名__" ,虽然初学者看来它们有些怪,不过这样做却是非常有道理而且是非常合理的。
我们今天介绍的这个功能虽然简单,不过却能很大程度上解决大型 C/C++ 代码开发中最大的问题:编译冲突,所以大家一定要记住。在以后的实际工作中你会发现这是非常重要的一种处理方式。