用make& amp找出多文件编译C

时间:2011-10-05 02:13:30

标签: c compiler-errors makefile

对于一个项目,我正在与合作伙伴一起编写决策树实施。由于我们都是C的相对新手并且必须快速工作,我们基本上将所有功能转储到单个文件中,最终超过1600行。这是一个快速而又肮脏的项目,但是现在下一个任务让我们负责扩展和重新实现代码。在目前的情况下,这不会发生。

现在,我正在根据职能责任分解原始来源。事实是,许多功能交织在一起,我的make文件出现重大错误。更具体地说,其他源文件报告在单独文件中声明的函数的隐式声明。

我真的没有使用多个文件makefile的经验。当前语法是在去年的系统编程类中从简单的shell实现中借用的,尽管这个当前项目的复杂程度要高出一个数量级。

cc= gcc
CFLAGS= -g -Wall -lm

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o proj2 main.o split.o tree.o id3.o output.o

我还尝试了以前的版本,其中每个目标文件都是单独编译的,如

main.o: main.c split.c tree.c id3.c output.c
  $(CC) $(CFLAGS) -o main.c split.c tree.c id3.c output.c

并重复为每个源创建一个.o文件,然后将其编译为可执行文件。

然而,这没有用,我得到了大约500行编译器投诉和警告,主要是关于隐式函数声明。

所以,基本上我有两个相关的问题:

  • 是否可以在不同的源文件之间交织函数调用?
  • 如果是的话,我怎么能在这里成功呢?

2 个答案:

答案 0 :(得分:5)

首先谈谈你的文件。

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o proj2 main.o split.o tree.o id3.o output.o

这应该有效(如果代码是正确的)但如果你正在使用GNUMake(你应该),你可以整理它:

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o $@ $^

现在您只有一个要维护的对象列表副本。

另一个版本是错误的:

main.o: main.c split.c tree.c id3.c output.c
  $(CC) $(CFLAGS) -o main.c split.c tree.c id3.c output.c

首先,您尝试将所有源文件编译到一个目标文件中,这样会破坏目标文件的目的。其次,当您的名称确实属于由main.o制作的目标文件时,您将命名一个目标文件main.cc。第三,该命令告诉编译器将所有其他源文件(split.ctree.c,...)编译到名为“main.c”的目标文件中 - - 不是非法的,但你一定会绊倒自己。

此外,您应该尝试使用C ++,而不是C,但那是另一天。

现在分手Big Ball of Mud。我假设您知道如何将大函数分解为较小的函数,因此问题是将函数分离到不同的源文件中(然后正确地编译和链接它们)。假设main()调用函数foo()

/* main.c */

void foo()
{
  // do foo things
}

int main()
{
  // do main things
  foo();
  return(0);
}

如您所知,foo必须先出现,否则编译器会在main尝试调用未声明的函数时发出警告。但我们可以提前声明foo

/* main.c */

void foo();

int main()
{
  // do main things
  foo();
  return(0);
}

void foo()
{
  // do foo things
}

当编译器到达foo()的调用时,它已经知道存在这样的函数,并且信任我们稍后定义它。现在的诀窍是:如果我们指示编译器进行编译,而不是链接(即生成像main.o这样的对象文件,而不是proj2之类的可执行文件),它将更信任我们:< / p>

/* main.c */

void foo();

int main()
{
  // do main things
  foo();
  return(0);
}

这将很好地编译成main.o。当我们将事物链接到可执行文件时,编译器信任我们在某些其他目标文件中提供void foo()的定义。定义将在另一个文件中,如下所示:

/* foo.c */

void foo()
{
  // do foo things
}

我们可以手工制作:

gcc -g -Wall -lm -c foo.c -o foo.o
gcc -g -Wall -lm -c main.c -o main.o
gcc -g -Wall -lm foo.o main.o -o proj2

但这很快就会变得乏味,所以我们会编写一个makefile:

cc= gcc
CFLAGS= -g -Wall -lm

proj2: main.o foo.o 
  $(CC) $(CFLAGS) -o $@ $^

%.o: %.c
  $(CC) $(CFLAGS) -c -o $@ $<

到目前为止一切顺利。如果这一点很清楚,那么我们可以转到头文件......

答案 1 :(得分:1)

您需要为每个源代码创建头文件以在其中包含声明。然后#include包含源代码顶部的相应头文件。