2009-04-23 83 views

回答

53

函数是在头文件中定义的吗?因此,实际的代码是在函数直接给出,就像这样:

static int addTwo(int x) 
{ 
    return x + 2; 
} 

那么这只是一个到许多不同的C文件提供了一个有用的功能的方式。每个包含头文件的C文件都会得到它自己可以调用的定义。这当然会浪费内存,并且(在我看来)是一件相当丑陋的事情,因为在头文件中使用可执行代码通常不是一个好主意。

请记住,#include:如果编译器看到,头部基本上只是将头部的内容(和它包含的任何其他头部)的内容粘贴到C文件中。编译器从不知道一个特定的函数定义来自头文件。

UPDATE:在许多情况下,它实际上是一个好主意,做一些像上面,然后我意识到我的回答听起来很黑与白的这个这是一种过于简单化的东西一点。例如,代码模型(或只是使用)intrinsic functions可以表示类似上面,并用显式inline关键字连:

static inline int addTwo(int *x) 
{ 
    __add_two_superquickly(x); 
} 

这里,__add_two_superquickly()功能是一个虚构的本质,因为我们希望整个函数基本上编译成单个指令,我们真的希望它被内联。尽管如此,上面比使用宏更清晰。

仅仅直接使用内在的优点当然是将它包装在另一个抽象层中,这使得可以在缺乏该特定内在因素的编译器上构建代码,方法是提供一个替代实现并根据哪一个选择合适的代码编译器正在被使用。

+12

那么,编译器可能会内联短的函数。所以如果功能足够短,它实际上可以使用更少的内存。但是我会预先设置一个“内联”,所以你不会收到关于未使用的静态函数的编译警告。 – quinmars 2009-04-23 09:26:32

+0

@quinmars好点,我编辑过。我希望晚点好点。 :) 谢谢。 – unwind 2014-02-02 17:58:15

+0

我不知道链接器是否会优化。它看到b.obj上的addTwo没有被引用,那么它将删除obj中的定义?如果是这样,开销只是(函数的大小)*(引用它的不同obj文件的数量)。仍然大于(函数的大小),但不是不好? – doorfly 2014-06-30 22:10:44

10

它将有效地创建一个单独的静态函数,它在每个包含在其中的cpp文件中具有相同的名称。这同样适用于全局变量。

7

正如其他人所说的,它的含义与.c文件本身的static函数完全相同。这是因为.c.h文件之间没有语义差异;只有编译单元由实际传递给编译器的文件(通常名为.c)组成,其中包含#include行(通常名为.h)中指定的任何和所有文件的内容,因为它们被预处理器看到。

约定C源文件名为.c,公共声明位于名为.h的文件中只是一个约定。但它通常是一个很好的。根据该公约,.h文件中应该出现的唯一内容是声明,因此通常避免在单个程序中多次使用相同的符号定义

在这种特殊情况下,static关键字使符号对模块是私有的,所以不存在多重定义冲突等待造成麻烦。所以从某种意义上说,这是安全的。但是如果没有保证函数会被内联的话,那么你就会冒这个函数在每个发生在#include这个头文件中的模块中被实例化的风险,这最多是代码段中的内存浪费。

我不确定在通常可用的公共头文件中用什么用例可以证明这一点。

如果.h文件中生成的代码和仅包含在一个单一的.c文件,然后我会亲自命名比.h其他文件的东西以强调它实际上不是一个公共头都没有。例如,将二进制文件转换为初始化变量定义的实用程序可能会写入一个文件,该文件旨在通过#include使用,并且很可能包含该变量的一个static声明,甚至可能包含访问器或其他相关实用程序的static定义功能。

1

定义在源文件或头文件中没有语义上的差异,基本上这两个意思在使用static关键字时在纯C中是一样的,你限制了范围。

但是,在头文件中编写这个问题时存在一个问题,这是因为每次将头文件包含在源文件中时,都会有一个具有相同实现的函数的副本,这与具有正常函数在头文件中定义。通过在头文件中添加定义,您不能达到静态函数的目的。

因此,我建议你应该只在你的源文件中实现你的实现,而不是在头文件中。

1

它在一些带有小内联函数的“仅标题”库中是有用的。在这种情况下,你总是希望制作一个函数的副本,所以这不是一个坏模式。然而,这给你一个简单的方法在单头文件中插入不同的接口和实现部分:

// header.h 

// interface part (for user?!) 
static inline float av(float a, float b); 

// implementation part (for developer) 
static inline float av(float a, float b) 
{ 
    return (a+b)/2.f; 
} 

苹果在GLK框架矢量数学库使用构建适应和支持等(例如GLKMatrix4.h)。

2

如果您在头文件中定义函数(而不是简单地声明它),函数的副本将在每个翻译单元(基本上在每个包含该头文件的cpp文件中)中生成。

这可能会增加可执行文件的大小,但是如果函数很小,这可能会忽略不计。优点是大多数编译器可以内联函数,这可能会提高代码的性能。

但是可能有一个这样做的区别在任何答案中都没有提及。如果你的函数使用静态局部变量,例如:

static int counter() 
{ 
    static int ctr = 0; 
    return ctr++; 
} 

不是:

//header 
int counter(); 

//source 
int counter() 
{ 
    static int ctr = 0; 
    return ctr++; 
} 

然后包括这个标题中的每个源文件都有其自己的计数器。如果函数在头文件中声明并在源文件中定义,则计数器将在整个程序中共享。

所以说唯一的区别是性能和代码大小是错误的。

相关问题