I've been experimenting with faking C a bit in my free time, and I came across some behavior I find interesting. I was hoping someone could help answer some questions I came up with. For the sake of simplicity, let's limit the discussion to compiling with gcc. First, the code (the .h files all have include guards):
a.h
void print_a();
b.h
void print_b();
c.h
void print_c();
a.c
#include "a.h"
#include "c.h"
#include <stdio.h>
void print_c(){
printf("Printing c from a");
}
void print_a(){
print_c();
}
b.c
#include "b.h"
#include "c.h"
#include <stdio.h>
void print_c(){
printf("Printing c from b");
}
void print_b(){
print_c();
}
main.c
#include "a.h"
#include "b.h"
int main(int argc, char **argv){
print_a();
print_b();
return 0;
}
So first of all, I understand that both a.c and b.c have implementations of c.h. Because of this I would expect that compiling like this would fail, since the compiler wouldn't know which implementation of print_c to bind to the interface:
gcc main.c a.c b.c
But you'll notice main.c has no dependencies on c.h. Therefore, when compiling each component separately, I was a bit surprised to see this fail at the linker stage:
gcc -c a.c
gcc -c b.c
gcc -c main.c
gcc main.o a.o b.o
b.o: In function `print_c':
b.c:(.text+0x0): multiple definition of `print_c'
a.o:a.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
It quickly became obvious that since the linker hadn't linked the definitions of print_c yet, so we should still expect these errors. That said, I can think of a several realistic use cases where this would be problematic. For example, what if I used some custom implementation of a log.h interface in my program, but I wanted to include a library that internally implements the same log.h interface? This leads to my questions:
- Is there a way to run the linker on individual files to produce some sort of intermediate linking files, and then combine them in another linking step?
- As an alternative to the above, would a well-defined C application just avoid using any shared header files like this?
- If so, what if multiple dependencies have methods with the same definitions but different implementations? If compiling dependencies from source it's obviously possible to rename functions, but how would this work in cases where that's not possible?
- Is this even a problem in C? I come from an OO background, where problems like this don't really exist, but it seems like this a clear hurdle to abstraction. It's very possible that I'm just missing something common (like method naming conventions, etc) that would prevent this problem.