【问题标题】:Should ANSI C function definition be placed inside header (.h) or source (.c) file?ANSI C 函数定义应该放在头文件 (.h) 还是源文件 (.c) 中?
【发布时间】:2015-03-09 21:43:44
【问题描述】:

这两种方法的优缺点是什么?

源代码与标头实现

源文件中的函数定义

头文件 sourcefunction.h 仅包含声明。

#ifndef SOURCEFUNCTION_H
#define SOURCEFUNCTION_H

void sourcefunction(void);

#endif // SOURCEFUNCTION_H

源文件 sourcefunction.c 包含定义

#include "sourcefunction.h"
#include <stdio.h>
void sourcefunction(void) { printf(" My body is in a source file\n"); }

头文件内的函数定义

头文件headerfunction.h包含定义,同时也是声明。

#ifndef HEADERFUNCTION_H
#define HEADERFUNCTION_H

#include <stdio.h>
void headerfunction(void) { printf(" My body is in a header file\n"); }

#endif // HEADERFUNCTION_H

不需要源文件。

消费者

文件 main.c

#include "sourcefunction.h"
#include "headerfunction.h"

int main(void) {
    sourcefunction();
    headerfunction();
    return 0;
}

为什么要编译很多源文件?

我们必须编译所有源文件并在链接期间记住它们。

gcc -c sourcefunction.c
gcc -c main.c
gcc main.o sourcefunction.o

Make 可以处理文件管理,但为什么还要麻烦呢?

接口和实现的分离总是一个问题吗?

这是大型项目和团队合作的明显原因。设计者指定接口。程序员实现功能。

较小的项目和非正式的方法呢?

从头文件中删除定义是否总能防止链接器错误?

假设我的程序正在使用另一个模块,它定义了具有相同名称的函数sourcefunction()。

#include "sourcefunction.h"
#include "sourcefunction1.h"
#include "headerfunction.h"

int main(void) {  
  headerfunction();
  sourcefunction();
  return 0;
}

不同的功能接口

文件sourcefunction1.h

#ifndef SOURCEFUNCTION1_H
#define SOURCEFUNCTION1_H

int sourcefunction(void);

#endif // SOURCEFUNCTION1_H

文件sourcefunction1.c

#include "sourcefunction1.h"
#include <stdio.h>
int sourcefunction(void) { int a = 5;  return a; }

通过编译 main.c,我得到一个 nice 编译器错误 sourcefunction1.h:4:5: error: conflicting types for 'sourcefunction' 显示错误的位置。

相同的功能界面

文件sourcefunction1.h

#ifndef SOURCEFUNCTION1_H
#define SOURCEFUNCTION1_H

void sourcefunction(void);

#endif // SOURCEFUNCTION1_H

文件sourcefunction1.c

#include "sourcefunction1.h"
#include <stdio.h>
void sourcefunction(void) { int a = 5;  printf("%d",a); }

编译器不介意多重声明。我收到 ugly 链接器错误。

头文件实现可以作为库吗?

jschultz410 说 如果您正在编写一个库并且您的所有函数定义都在标头中,那么如果在多个翻译单元中需要它们,其他将他们的开发分割成多个翻译单元的人将获得您的函数的多个定义

让我们拥有 文件 consumer1.c

#include "headerfunction.h"
void consume1(void) { headerfunction(); }

文件 consumer2.c

#include "headerfunction.h"
void consume2(void) { headerfunction();  headerfunction();}

文件 twoConsumers.c

extern void consume1(void);
extern void consume2(void);
int main(void) {
    consume1();
    consume2();
    return 0;
}

让我们编译源代码。

gcc -c consumer1.c
gcc -c consumer2.c
gcc -c twoConsumers.c

到目前为止,一切都很好。现在,链接。

gcc consumer1.o consumer2.o twoConsumers.o

链接器错误:当然是multiple definition of 'headerfunction'。

但是我可以让我的库函数static。 文件 headerfunction.h,然后。

#ifndef HEADERFUNCTION_H
#define HEADERFUNCTION_H

#include <stdio.h>
static void headerfunction(void) { printf(" My body is in a header file\n"); }

#endif // HEADERFUNCTION_H

它对其他翻译单元隐藏了定义。

【问题讨论】:

  • 首先,链接器错误。然后,谷歌“模块化”。
  • @TheParamagneticCroissant 1. 当您在不同的头文件和源文件中有 2 个相同函数的声明时,也会发生链接器错误。 2. 模块化的概念对于大型项目和团队合作很重要。
  • 不,当两个定义出现在两个不同的文件中时,会发生链接器错误。就像你在两个地方包含相同的定义一样

标签: c definition


【解决方案1】:

我不应该回答这个问题,但我会的。

  1. 这可能会创建重复的定义,除非您的项目中确实只有一个 .c 文件(不明智)。如果多次包含不同的 .c 文件,即使标题保护也不会阻止文件多次包含标题。当 .obj 文件链接在一起时,会出现冲突。
  2. 如果头文件中只有函数声明而不是定义,那么只有对接口(函数名称、参数或返回类型)的更改需要重新编译依赖项。但是,如果整个定义都在头文件中,那么对函数的任何更改都需要重新编译所有依赖它的 .c 和 .h 文件,这在较大的项目中会产生大量不必要的重新编译。
  3. 这不是惯例。库不会使用此约定,因此您将无法处理它们的头文件结构。其他开发人员不会使用此约定,因此您可能会在那里制造混乱或烦恼。

【讨论】:

  • 我只能同意 2. 并且仅在开发过程中,Make 不知道标题更改并认为 .o 是最新的。当实现稳定时不再有问题。
  • @3 事实上,这个库让我考虑将实现放在标题中。我很快就能将Boost::lambda header-library 与MinGW 一起使用。但是我不能使用Boost::regex,因为我可以编译那个库。
  • "Make 不知道标头更改并认为 .o 是最新的。"您也可以了解 .h。查看makedepend。 @3 如果您正在编写一个库并且您的所有函数定义都在标题中,那么如果在多个翻译单元中需要它们,其他将他们的开发分割成多个翻译单元的人将获得您的函数的多个定义。
  • 程序只有一个翻译单元的主要优点是它可以使您的编译器更容易找出内联优化。 OTOH,它使您的所有文件范围对象有效地全局化,并且它们现在可以发生冲突。不管这个问题如何,通常的做法是使用 .c 和 .h 进行正常开发,然后在一个翻译单元中使用 #include 包含您想要的所有 .c 和 .h 的文件。
猜你喜欢
  • 1970-01-01
  • 2013-08-21
  • 2020-09-28
  • 2010-11-25
  • 1970-01-01
  • 2011-02-14
  • 2013-05-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多