Multiple definition and header-only libraries

2019-01-24 04:54发布

问题:

I have a C program with several c and h files. I decided to make one part of the program 'header-only' so I moved the code from c to h. Now I'm getting multiples definition problems and I have no idea why. e.g.:

main.c includes utils.h
vector.c includes utils.h

I moved everything in utils.c to utils.h (and of course removed utils.c from the project). utils.h starts with

#ifndef UTILS_H_
#define UTILS_H_

// and end with:
#endif

To be sure my guard was unique I tried changing it (e.g.: UTILS718171_H_) but it doesn't work.

Still, the compiler complains:

/tmp/ccOE6i1l.o: In function `compare_int':
ivector.c:(.text+0x0): multiple definition of `compare_int'
/tmp/ccwjCVGi.o:main.c:(.text+0x660): first defined here
/tmp/ccOE6i1l.o: In function `compare_int2':
ivector.c:(.text+0x20): multiple definition of `compare_int2'
/tmp/ccwjCVGi.o:main.c:(.text+0x6e0): first defined here
/tmp/ccOE6i1l.o: In function `matrix_alloc':
ivector.c:(.text+0x40): multiple definition of `matrix_alloc'
/tmp/ccwjCVGi.o:main.c:(.text+0x0): first defined here
...

The problem might be something like: all c files are compiled and get their own version of the code and then at linkage it causes problem, but I have honestly no idea how to solve this problem.

回答1:

If you define your variables inside your header file and include the header in several c files, you are bound to get multiple definitions error because you break the One definition rule(ODR), which states that there should be only one definition in one Translation Unit(header files + source file).

Solution is:
You should define the entities you get Multiple definition errors for only once.
For Functions:
Declare the function prototypes in header file(which you include in other source files) and define the function in one and only one source file.
For Global variables:
You declare the variable extern in header file(which you include in other source files) and then define the variable in one and only one source file.



回答2:

You are missing the point of #ifndef _FOO_H / #define _FOO_H / #endif construct. That only protects against multiple inclusions in ONE file. For example they protect against this:

foo.h:

  #ifndef _FOO_H 
  #define _FOO_H

  /* some C stuff here */

  #endif /* _FOO_H */

foo.c:

   #include <foo.h>
   #include <bar.h>
   ...

bar.h:

   #include <foo.h>
   ...

note that foo.c and bar.h both include foo.h; here the #ifdef _FOO_H / #define _FOO_H / #endif protects against that double inclusion in foo.c ( the foo.h included in bar.h doesn't redefine stuff )

Now the next part.

Why would you move function implementation from utils.c to utils.h? Also why did you decide to make it "header-only" ?

You can, depending on whether your compiler supports static inline functions do this; but even then it's NOT recommended, as more than likely, it will make your program unnecessarily bloated because chances are your util functions are quite complex and cannot be inlined anyways.

So change it back to utils.c and utils.h construct that you had before which, i presume, WAS working and enjoy the software.



回答3:

You're violating the One-Definition-Rule. Each function must be defined precisely once, while you end up defining it in each translation unit.

You simply cannot do something like this:

// header.h
void foo() { }

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

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

The only real solution is to declare void foo(); in the header and define it once only (usually in a dedicated foo.c). The same goes for global variables, which should be declared as extern in the header and defined in the source file.

Include guards have nothing to do with this. They only serve to prevent recursive self-inclusion or redundant multiple inclusion within one translation unit.



回答4:

If you indend for the utils.h functions to be "copied" into each place where it is used, just use static functions in the header. (static inline in C99)