c包括,防止冗余代码

时间:2014-07-25 16:12:27

标签: c include refactoring duplicates header-files

如果我有一个c项目,我的主程序需要file1和file2,但file2也需要file1。有没有办法在main和file1中包括file2?如果我有一个包含保护,这会阻止file1.c被添加两次吗?

//file1.h
#ifndef FILE1_H
#define FILE1_H

void func1(void);
#endif

-

//file1.c
#include "file1.h"
void func1(void) {
  ..do something
}

-

//file2.h
#ifndef FILE2_H
#define FILE2_H

void func2(void);
#endif

-

//file2.c
#include "file2.h"
#include "file1.h"

void func2(void) {
  ..do something
  func1();
}

-

//main.c
#include "file1.h"
#include "file2.h"

int main(void) {
  func1();
  func2();

  return 0;
}

- 由于file2包含file1,我可以这样做吗?它会阻止重复file1代码吗?

//main.c (alternate)
#include "file2.h"

int main(void) {
  func1();
  func2();

  return 0;
}

如果file2决定将来不再包含file1,我不会太担心会出现的问题。我更关心浪费的空间。

我想知道的是:A:include guard是否会阻止代码重复,如果是这样,在main.c和file2.c中包含file1就没有额外的空间。 B:在使用额外空间的情况下,我的备用main.c会工作吗?

2 个答案:

答案 0 :(得分:0)

为什么你不能有一个标题,你可以放置函数func1()和func2()。 只需将标题包含在不同的文件中即可。

没有得到代码重复的含义。

//file1.h



extern void func1();
extern void func2();

//file1.c
#include<file1.h>
void func1()
{`
enter code here`
}


//file2.c
#include<file1.h>
void func2()
{
}

//main.c

#include <file1.h>

main()
{
func1();
func2();
}

答案 1 :(得分:0)

快速解释(注意所有这些都可以被知道他们在做什么的人覆盖):

首先,两个定义:声明是指你写下存在的东西。例如,&#34; int foo();&#34;或&#34; struct bar;&#34;。请注意,我们实际上还没有使用过这个东西,我们只是给它起了一个名字。只要您将它们声明为同一个东西,您就可以根据需要多次声明事物! (变量声明有自己的规则)。

您要使用的任何内容都需要在引用之前声明。

定义是您说出声明的内容。 int foo() {asdfadf;}struct bar{int x;}。事情可以(通常是)在宣布时定义,但并非总是如此。

在C中,您必须遵循一个定义规则。可以根据需要随时声明事物,但每个翻译单元(在一秒内定义)只能定义一次。 (此外,函数调用只能在每个可执行文件中声明一次)。

在使用它们之前需要定义的东西很少......除了变量之外,您只需要在需要其大小或访问其成员的上下文中使用它之前定义结构。 / p>

什么是翻译单元?它是用于编译单个源文件的所有文件。您的头文件不是编译目标。只有.c文件(称为&#34;源文件&#34;)是。对于每个c文件,我们都有一个&#34;翻译单元&#34;的概念,它是用于编译该c文件的所有文件。该代码的最终输出是.o文件。 .o文件包含在该c ++文件中运行代码 defined 所需的所有符号。因此,您的c文件和包含的任何文件都包含头文件。注意:并非需要在翻译单元中声明所有声明的内容以获取有效的.o文件。

那么头文件中有什么?嗯(总的来说)你有几件事情:

  • 功能声明
  • 全球定义&amp;声明
  • 结构定义&amp;声明

基本上,您需要在翻译单元之间共享简单的声明和定义。 #include允许您将其保存在一个共享文件中,而不是全部复制和粘贴此代码。

您的定义只能发生一次,因此包含警卫可以防止出现问题。但是,如果您只有声明,那么您在技术上需要并包含警卫。 (无论如何,你仍然应该使用它们,它们可以限制你所做的交叉包含,以及作为无限递归包含的保证)。但是,您确实需要包含与每个翻译单元相关的所有声明,因此您很可能会多次包含该声明。还行吧。至少声明在一个文件中。

编译.o文件时,编译器会检查您是否遵循了一个定义规则,以及所有语法是否正确。这就是为什么你会在&#34;创建.o&#34;中获得这些类型的错误。编译步骤。

因此在您的示例中,在编译之后,我们得到file1.o(包含func1的定义),file2.o(包含func2的定义)和main.o(包含main的定义)。下一步是使用链接器 链接所有这些文件。当我们这样做时,编译器会获取所有这些.o文件,并确保文件中每个函数符号只有一个定义。这就是让main.o知道file1.o和file2.o中的内容发生的魔力:它解决了&#34;未解决的符号&#34;并检测何时存在冲突的符号。

最后的想法:

保持代码短缺是一种错误的任务。您希望代码可维护和可读,并使代码尽可能短,与此相反。我可以用一个单字母的alpha-numberic变量名称在一行上编写一个完整的程序,但没有人会知道它做了什么......你想要避免的是像声明这样的代码重复。维护一长串#includes可能会变得棘手,因此将相关函数组合在一起通常是好的(一个好的经验法则是,如果我几乎总是一起使用A和B)那么它们应该在同一个头文件中。

我偶尔会遇到的另一件事(偶尔因为它有一些严重的缺点)是使用方便的头文件:

//convience.h
#ifndef CONVIENIENCE_H
#define CONVIENIENCE_H
#include "file1.h"
#include "file2.h"
#endif

便捷头文件中只有其他头文件,这可以确保它永远不会包含代码,这使得它更容易维护,但仍然有点混乱。另请注意,如果你在file1和file2中执行包含警戒,那么会信保护不是必须的,尽管它可以(理论上)加速编译。