开发者

Figuring out multiple file compilation with make & C

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 pos开发者_C百科sible to intertwined function calls between different source files?
  • if so, how can I make it possible here?


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 $@ $<

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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜