使用 make & 计算出多个文件编译C

发布于 2024-12-07 20:05:21 字数 854 浏览 0 评论 0原文

在一个项目中,我正在与一位合作伙伴一起编写决策树实现。由于我们俩都是 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 行编译器投诉和警告,主要是关于隐式函数声明的。

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

  • 是否可以在不同源文件之间交织函数调用?
  • 如果是这样,我怎样才能在这里实现它?

For a project, I was working with a partner writing a decision tree implementation. Since both of us are relative newcomers to C and had to work quickly, we basically dumped all the functionality in a single file, which ended up being over 1600 lines. It was a quick and dirty project to get working, but now the next assignment has us responsible for extending and re-implementing the code. In its current condition, that isn't going to happen.

Right now, I'm breaking up the original source based on function responsibility. Thing is, many of the functions are intertwined, and I'm getting major errors with my make file. More specifically, the other source files are reporting implicit declaration of functions that are declared in a separate file.

I really have no experience with multiple file makefiles. The current syntax is borrowed from a simple shell implmentation in last years Systems Programming class, although this current project is an order of magnitude greater in complexity.

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

I also tried a previous version where each object file was compiled separately like

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

and this repeated to create a .o file for each source, which then was compiled into an executable.

However, that didn't work and I got about 500 lines of compiler complaints and warnings, mainly about implicit function declarations.

So, essentially I have two related questions:

  • is it possible to intertwined function calls between different source files?
  • if so, how can I make it possible here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

茶色山野 2024-12-14 20:05:21

首先介绍一下你的 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

这应该可以工作(如果代码正确),但如果您使用 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,但那是另一天的事了。

现在来分解大泥球。我假设您知道如何将大函数分解为较小的函数,因此问题是将函数分离到不同的源文件中(然后正确编译和链接它们)。假设 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 这样的可执行文件),它会更加信任我们:

/* 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 $@ 
lt;

到目前为止一切顺利。如果这些都清楚了,那么我们可以继续讨论头文件......

First a word about your makefiles.

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

This should work (if the code is correct) but if you're using GNUMake (which you should) you can tidy it up:

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

Now you have only one copy of the object list to maintain.

The other version is just wrong:

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

First, you're trying to compile all of the source files into one object file, which kind of defeats the purpose of object files. Second, you're naming your one object file main.o when that name should really belong to an object file made from main.cc. Third, the command tells the compiler to compile all of the other source files (split.c, tree.c, ...) into an object file called "main.c"-- not illegal, but you're bound to trip yourself up.

Also, you should try to use C++, not C, but that's for another day.

Now for breaking up the Big Ball of Mud. I assume you know how to break big functions into smaller ones, so the problem is segregating functions into different source files (and then compiling and linking them correctly). Suppose main() calls a function foo():

/* main.c */

void foo()
{
  // do foo things
}

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

As you know, the foo must come first, otherwise the compiler would balk when main tried to call an undeclared function. But we can declare foo beforehand:

/* main.c */

void foo();

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

void foo()
{
  // do foo things
}

When the compiler reaches the call to foo(), it already knows that such a function exists, and trusts us to define it later. Now here's the trick: if we instruct the compiler to compile, but not link (that is, produce an object file like main.o, not an executable like proj2), it will trust us even farther:

/* main.c */

void foo();

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

That will compile into main.o quite nicely. The compiler trusts us to provide the definition of void foo() in some other object file when we link things together into an executable. The definition will be in another file like so:

/* foo.c */

void foo()
{
  // do foo things
}

We could build this by hand:

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

But that gets tedious fast, so we'll write a makefile:

cc= gcc
CFLAGS= -g -Wall -lm

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

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

So far so good. If this much is clear then we can move on to header files...

糖粟与秋泊 2024-12-14 20:05:21

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

You need to create header files for each of the source code to have the declarations in them. You then #include the appropriate header files at the top of the source code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文