【Skynet】C头文件不要定义函数?

参考自:函数实现不放在头文件的原因,及何时可以放头文件的情况(绿色冰点)


对于C/C++我们很早就被告知不要在头文件里定义函数,这样不好。今天学习Skynet时,发现里面有很多函数都定义在头文件里?很是疑问,于是有了这篇文章。


把函数定义在头文件有哪些主要缺点?

1、不利于代码理解和维护:
通常,头文件被用来唯一指定接口,且多少提供一些文档来说明如何使用在该文件中声明的组件。若把所有变量和函数的定义都写在头文件中,往往头文件异常的大,不利于代码理解;推荐把不同功能的函数分布在不同的源文件中,共享一个头文件;

2、函数细节在头文件中被暴露;


3、导致链接冲突:
头文件被多个源文件包含,头文件里的函数,变量在链接时发生冲突;

4、增加编译时间: 
头文件被包含到多个源文件中,该头文件就会被编译到多个源文件里,增加了编译时间,也增大了执行程序的体积。相比之下,如果函数的实现在源文件里,而源文件则单独编译一份。(在C和C++中有个例外,即内联函数。内联函数通常放在头文件中,因为大多数实现如果不知道其定义,在编译时便无法适当的展开内联函数。)



如果看重缺点2,则应该在第一时间放弃在头文件中定义函数。


缺点3是可以克服的。如下一段代码:

#ifndef _A_H_
#define _A_H_

int gint = 0;

int myAdd(const int a, const int b)
{
	return a + b;
}

#endif

在同一工程中,两个以上的源文件包含了此头文件,则在链接时期就会发生冲突,因为在两个源文件编译得到的目标文件中都有一份 myAdd 的函数实现,链接器不知道对于调用了此函数的调用者,应该使用哪一个副本。这一问题对于全局变量也是如此。


解决的途径:一个是 inline(注意:在C下,内联一般使用 static inline) ,另一个是 static 。使用这两个关键字的任意一个来修饰全局函数,都会消除上述的冲突问题,然而本质却大不相同。

如果使用 inline ,则意味着编译器会在调用此函数的地方把函数的目标代码直接插入,而不是放置一个真正的函数调用,实际作用就是这个函数事实上已经不再存在,而是像宏一样被就地展开了。使用 inline 的副作用,首先在于毋庸置疑地,代码的体积变大了;其次则是,这个关键字严格算起来并不是 C 语言的关键字,使用它多少会带来一些移植性方面的风险,尽管主流的 C 语言编译器都可以支持 inline 。对于 GCC , inline 功能关键字就是 inline 本身,而对于微软的编译器,应该是 __inline (注意有两个前导下划线)。而且,根据惯例, inline 通常都是对编译器的某种暗示而非强制要求,编译器有权力在你不知情的情况下把它实现为非 inline 的状态(可能的原因有,函数太大或者复杂度过高)。这样的后果不是我们愿意的。

如果使用 static ,那么至少结果是可预料的。所有包含此头文件的源文件中都会存在此函数的一份副本。虽然代码也有一定程度的膨胀,但好就好在互相不冲突,因为 static 关键字保证了该函数的可见度为单个源文件之内。注意:该方法对于全局变量来说是毁灭性的,因为变量也只是在单个源文件里可见!

a.h

#ifndef _A_H_
#define _A_H_

static int gint = 0;

static int myAdd(const int a, const int b)
{
	return a + b;
}

#endif



a.c

#include "a.h"
#include <stdio.h>

int funcA()
{
	printf("%d from A\n", gint);
	gint++;
	printf("%d from A\n", gint);
	return myAdd(1, 2);
}


b.c

#include "a.h"
#include <stdio.h>

int funcB()
{
	printf("%d from B\n", gint);
	return myAdd(1, 2);
}


main.c

#include <stdio.h>
extern int funcA();
extern int funcB();

int main()
{
	printf("%d	%d\n", funcB(), funcA());
	return 0;
}


输出如下:

0 from A
1 from A
0 from B
3	3

发现使用 static 确实可以解决函数的链接问题,对于全局变量则并不适合,两份变量都只在本地源文件可见。


但是:参见知乎上的大牛说法static函数在头文件中定义有什么好处么?,函数定义在头文件还是慎用。


以上的讨论虽然看起来主要聚焦在 C 语言上,但由于 C++ 是 C 语言的超集,并且在这些方面并没有做太多的修改,因此讨论结果同样也适用于 C++ 。对于C++可以进一步讨论,参见本文的文献。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值