Understanding of redefinition compiler error and multiple definition error.

My question is about to learn some preventive knowledges about redefinition/multiple definition compiler error.
Actually, when I get them - really get lost.
Also what about object files, executable and a bit more information about what does linker.
The good answer is those which is able to visually demonstrate the job of linker.
I know that, linker gathers objects together, and creates an executable. But that level of knowledge does not satisfy me.
It's too high level of abstraction. Need to see whole picture, but see in details.

Assuming that we are spearking in context of C programming language in Linux environment or Windows environment.
Nusrat NuriyevAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

sarabandeCommented:
some preventive knowledges about redefinition/multiple definition compiler error.

Open in new window


A.
compiler errors because of a duplicate definition were mostly due to a header file which wasn't protected against multiple inclusion:

//a.h
// no protection
....
int f()
{
    ....
}

// b.h
#ifndef B_H
#define B_H
#include "a.h"
....
#endif

// b.cpp
#include "a.h"
#include "b.h"
...

Open in new window


if you compile b.cpp the compiler would complain that function f has multiple definition because implementation (definition) of f was included twice by including a.h both in b.cpp and in b.h.

if b.cpp only would include b.h the compiler is happy. but if you include a.h or b.h in a.cpp, the linker would complain because it finds the same 'object' code for function f in b.obj (b.o in linux) and in a.obj.

note, compiler errors because of mutual inclusion of header files (a.h includes b.h and b.h includes a.h) may cause the compiler to show strange errors which even becomes more cryptic if the header protection is valid in both headers. that is because the include statements were processed by the precompiler which would not run into a infinite cycle because of the header macros. but the compiler would miss definitions of either a.h or b.h depending on which was included first. however, such compiler errors are not because of duplicates.

B.
another case where the compiler would complain is if you were creating a new function by copying code from an already existing function and 'forget' to change the name of the new function. if using namespaces with a using clause you also might create ambiguous names. same applies if you copy a whole class  with implementation and forget to exchange the classname:: somewhere. again, the compiler (or linker) would find duplicates where unique symbol names are required.


C.
compiler errors because of ambiguous function call easier can be understood. they occur if the compiler cannot resolve c++ polymorphism where the same (function) name was used but has different implementations depending on the arguments or the class object instantiated. the linker will not have any problems with that since ambiguous call will be detected by the compiler.

Also what about object files, executable and a bit more information about what does linker.
the compiler will create an object file for each .c or .cpp source you added to the project or makefile. object files is machine code which contains all the compiled functions and implementation which was in the c or cpp file plus the implementation of source code included by the header files. though normally header files contain only declarations, there are exceptions to this. the most important exception is template code where all the implementation must be provided by include files such that the compiler can create machine code for the concrete template type(s) you added in your source code. anyway, finally the object file contains all machine code from implementation of your source code.

the linker would take the object files and make an executable out of this. especially the linker would add code such that all stack data needed by the object code was allocated and initialized. it also would add startup code such that the main function was invoked after initialization.

additionally the linker needs to add all object code that was not provided (implemented) by your source.  such machine code normally was not added by .obj (.o) files but by .lib (.a) libraries. a library simply is a collection of object files and there are two main advantages of using a library rather than a list of object files. one is, that the linker normally would not add all object code which is in the library but only those modules which are needed by your code and the code used from libraries. that means the linker checks for each function added to the executable which additional dependencies are needed for that piece. then there are two possibilities: either the module is already included or it needs to be extracted from libraries again. if any of the needed 'symbols' (functions or global variables) was missing the linker would fail with 'unresolved symbol'. if any of the symbols exist twice or multiple, the linker also would complain and fail. however, libraries were searched in the order they were defined. because of that there might be duplicate code in library B what doesn't matter because the linker already has resolved the symbol from library A or the object files. the second main advantage of libraries is that they can contain many object files. for example the runtime libraries provided by the compiler contain object files of thousand source files or more and the linker only has one library configured for all these code.

a specialty are dynamic link libraries (.dll in windows or .so in unix). a dll contains contrary to an object library executable code which was loaded at runtime to the calling program. there are two ways a dll could be bound to an executable. one is that the calling program loads the dll file at runtime and reads the needed function code from the file (the operation system will cache the dll and the functions but that is not the main point) and calls the function by means of a function pointer. the dll is only referenced by name in the calling program and you easily can replace the dll file without a new build of your program. the disadvantage of this method is that the program might fail at runtime if the dll cannot be found or if the functions do not exist or have a different interface than expected in the calling program. because of that, there is a second method how to link against a dll: the provider of the dll provides an additional object library (also called import library or export library depending on the direction) which contains code to load the dll and wrapper functions which call into the dll. when you build your executable you link it against the object libraries same as if the object code of the functions of the dll was fully embedded in the dll, but actually you need the import library for build (for the linker) to resolve the function calls in your program and the dll at runtime to get the executable code. the main advantage to the first method is that a missing dll will prevent your program from executing while otherwise you may get a runtime failure when your program firstly calls into the dll.

Sara
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Nusrat NuriyevAuthor Commented:
We can also make preprocessing w/o compilation by putting -E switch

g++ -E b.cpp

# 1 "b.cpp"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "b.cpp"
# 1 "a.h" 1


int f()
{

}
# 2 "b.cpp" 2
# 1 "b.h" 1



# 1 "a.h" 1


int f()
{

}
# 5 "b.h" 2
# 3 "b.cpp" 2

Open in new window


Could you explain a little bit about the content of this output. What does all lines with # sign mean?
0
Nusrat NuriyevAuthor Commented:
1.
if you compile b.cpp the compiler would complain that function f has multiple definition because implementation (definition) of f was included twice by including a.h both in b.cpp and in b.h.

if b.cpp only would include b.h the compiler is happy. but if you include a.h or b.h in a.cpp, the linker would complain because it finds the same 'object' code for function f in b.obj (b.o in linux) and in a.obj.
Could we say, that there are two "levels" of redefinitions: at source code level and at object code level?

2. Well, I have tried mutual inclusion, g++ compiler falls into an infinite loop. :)

3. Could you provide an example of "same 'object' code for function" compiler error?

4.
implementation must be provided by include files such that the compiler can create machine code for the concrete template type(s) you added in your source code. anyway, finally the object file contains all machine code from implementation of your source code.
So, when I add
 #include "stdio.h"

Open in new window

which object file is embedded to my object file?

5. Can we say that object file == executable file with one exception that object file must not contain main function?
0
sarabandeCommented:
Could we say, that there are two "levels" of redefinitions: at source code level and at object code level?
actually it is more a duplicate than a redefinition. the errors occur because either the compiler or the linker detect an ambiguous definition which they can't resolve. obviously the compiler can detect ambiguous definitions only in the source file it was compiling and the code of header files which have been included by the precompiler.

Well, I have tried mutual inclusion, g++ compiler falls into an infinite loop. :)
bad. the precompiler should have a maximum include depth.

Could you provide an example of "same 'object' code for function" compiler error?

class X
{
      void f();
};

void X::f()
{
    ...
}

void X::f()
{
    ...
}

Open in new window


the compiler error is 'X::f already has a body.'

Sara
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
System Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.