使用make&C确定多个文件编译

Jas*_*son 1 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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Bet*_*eta 5

先说一下你的makefile.

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
Run Code Online (Sandbox Code Playgroud)

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

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o $@ $^
Run Code Online (Sandbox Code Playgroud)

现在,您只需要维护一个对象列表的副本.

另一个版本是错的:

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
Run Code Online (Sandbox Code Playgroud)

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

此外,你应该尝试使用C++,而不是C,但这是另一天.

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

/* main.c */

void foo()
{
  // do foo things
}

int main()
{
  // do main things
  foo();
  return(0);
}
Run Code Online (Sandbox Code Playgroud)

如你所知,foo必须先行,否则编译器在main尝试调用未声明的函数时会犹豫不决.但我们可foo事先声明:

/* main.c */

void foo();

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

void foo()
{
  // do foo things
}
Run Code Online (Sandbox Code Playgroud)

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

/* main.c */

void foo();

int main()
{
  // do main things
  foo();
  return(0);
}
Run Code Online (Sandbox Code Playgroud)

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

/* foo.c */

void foo()
{
  // do foo things
}
Run Code Online (Sandbox Code Playgroud)

我们可以手工制作:

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
Run Code Online (Sandbox Code Playgroud)

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

cc= gcc
CFLAGS= -g -Wall -lm

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

%.o: %.c
  $(CC) $(CFLAGS) -c -o $@ $<
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.如果这一点很清楚,那么我们可以转到头文件......