(C++) ubuntu g++ bug: multiple definition of a function. What went wrong?

Click For Summary

Discussion Overview

The discussion revolves around a C++ programming issue related to a "multiple definition" error encountered when compiling code using g++. Participants explore the causes of this error, particularly focusing on the implications of header file usage, the One Definition Rule, and the behavior of the preprocessor and linker in C++.

Discussion Character

  • Technical explanation
  • Debate/contested
  • Exploratory

Main Points Raised

  • One participant describes their confusion regarding a multiple definition error related to the function hello(int, int) being defined in multiple source files.
  • Another participant explains that the One Definition Rule is violated because the function hello is defined in both main.cpp and subtract.cpp, suggesting that functions in headers should be marked as inline to avoid this issue.
  • Some participants discuss the limitations of include guards, noting that they only prevent multiple definitions within a single compilation unit, and that separate compilation of files leads to multiple definitions being seen by the linker.
  • A participant attempts to clarify how the preprocessor handles includes, expressing curiosity about the output of the preprocessor and how it relates to the multiple definition error.
  • Another participant elaborates on the linking process, explaining that the linker sees object files and does not retain context from individual source files, leading to ambiguity when multiple definitions exist.
  • Some participants speculate about the implications of function scope and potential naming conflicts with standard library functions, raising concerns about managing function names to avoid collisions.

Areas of Agreement / Disagreement

Participants generally agree on the existence of the multiple definition issue and the role of the One Definition Rule, but there is ongoing exploration and clarification regarding the behavior of the preprocessor and linker, indicating that some aspects of the discussion remain unresolved.

Contextual Notes

Participants express uncertainty about the preprocessor's behavior and how it affects the compilation process, particularly regarding the inclusion of header files and the resulting definitions in object files. There is also a lack of consensus on the best practices for managing function definitions in headers to avoid conflicts.

Nishiura_high
Messages
7
Reaction score
0

Homework Statement



I can't understand what's causing this bug.
I'm not trying to actually use this program, but it's just something I made up to learn how it works, and understand why things don't work, while I'm learning C++. So my question is, what's causing this program to get this error?

Homework Equations



add.cpp
Code:
int add(int x, int y)
{
    return x + y;
}

main.cpp
Code:
#include <iostream>
#include "math.h"
using namespace std;

int main()
{
    cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
    subtract(1, 2);
    hello(4, 5);
    return 0;
}

subtract.cpp
Code:
#include "math.h"

int subtract(int x, int y)
{
    return add(4, 5)
}

math.h
Code:
#ifndef MATH_H
#define MATH_H
int add(int x, int y);
int subtract(int x, int y);

int hello(int x, int y)
{
    return 4;
}
#endif
These 4 files (add.cpp, main.cpp, subtract.cpp, math.h) are all in the same directory. I'm running g++ on ubuntu, so in that directory I type in:

Code:
g++ -g *.cpp

and the output is:

Code:
/home/myname/Desktop/CS/math.h:8: multiple definition of 'hello(int,int)'
/tmp/ccWAqLWY.o:/home/myname/Desktop/CS/math.h:8: first defined here
collect2: ld returned 1 exit status

The Attempt at a Solution


I think it has to do with the header file includes. I thought that it isn't being defined twice, because the second time math.h is included, it would see the #ifndef and not redefine it again. (?)
Can anyone explain what went wrong? Much thanks.
 
Physics news on Phys.org
Your program violates the one definition rule (see http://en.wikipedia.org/wiki/One_Definition_Rule). You have the function hello(int, int) defined multiple times, once in subtract.cpp, and again in main.cpp.

You can define functions in headers, but they need to be qualified with inline. For example,
Code:
inline int hello(int x, int y)
{
    return 4;
}
 
You're probably thinking that the '#ifndef MATH_H' trick is supposed to prevent this sort of thing, but it works only within the compilation of a single file.

Your command 'g++ -g *.cpp' doesn't act as if it were concatenating add.cpp, main.cpp and subtract.cpp together and then compiling them all in one go, as if they were in a single file; instead, it compiles the three files separately from each other. If you were to compile to unlinked .o files, both main.o and subtract.o would contain definitions of the function subtract(). This is what the linker is complaining about (note the error message comes from the linker, 'ld', not from the compiler itself).

[added] Suppose the compiler compiles main.cpp first. When it finishes main.cpp, it loses all its "internal memory" of what happened in that file, including the fact that the symbol MATH_H has been defined. When it then compiles subtract.cpp, the symbol MATH_H is now undefined, and so the compiler compiles the definition of subtract() again.

Try concatenating the two files main.cpp and subtract.cpp into a single file that has two copies of the line '#include "math.h"' and see the difference.
 
Last edited:
D H said:
Your program violates the one definition rule (see http://en.wikipedia.org/wiki/One_Definition_Rule). You have the function hello(int, int) defined multiple times, once in subtract.cpp, and again in main.cpp.

You can define functions in headers, but they need to be qualified with inline. For example,
Code:
inline int hello(int x, int y)
{
    return 4;
}
Thanks so much for telling me about the One Definition Rule. I hadn't heard of it before, and I tried reading that article but it was hard to understand. From what I can understand from that article, it looks like I was doing things right by putting
"ifndef NAME
def NAME
Code:
endif"

How is it defined multiple times, when I have the include guard?

I tried to see the result of the preprocessor to see if it actually did insert the header file twice by typing in this command to the terminal:

[CODE]
g++ -E *.cpp -o output

And when I opened the resulting output file in a text editor, it looked like this:

Code:
# 1 "subtract.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "subtract.cpp"
# 1 "math.h" 1



int add(int x, int y);
int subtract(int x, int y);

int hello(int x, int y)
{
 return 4;
}
# 2 "subtract.cpp" 2

int subtract(int x, int y)
{
 return add(4,5);
}

But, I had expected it to simply copy and paste the header files where the "#include"s were. Could you tell me what this means? I'm curious because it seems to contradict the idea that wherever I include a header file, that header file gets copied and pasted directly into that spot. Thanks!
 
Nishiura_high said:
How is it defined multiple times, when I have the include guard?

See my post above.
 
jtbell said:
If you were to compile to unlinked .o files, both main.o and subtract.o would contain definitions of the function subtract(). This is what the linker is complaining about.

Thanks for your post. Could you elaborate what you mean here, maybe with an example? I don't think I quite understand. Thanks!

edit:
So, here's what I'm thinking so far. Maybe the preprocessor makes main.cpp look like this:

Code:
#include <iostream>  //Except this would be expanded a whole lot

int add(int x, int y); // function prototype for add.h
int subtract(int x, int y);

int hello(int x, int y)
{
	return 4;
}using namespace std;int main()
{
    cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
    subtract(1,2);
    hello(4,5);
    return 0;
	
}
And subtract.cpp might look like this after being preprocessed:
Code:
int add(int x, int y); // function prototype for add.h
int subtract(int x, int y);

int hello(int x, int y)
{
	return 4;
}int subtract(int x, int y)
{
	return add(4,5);
}

So when it tries to link it together, the linker doesn't know whether to use the address of the function "hello" that is in main.cpp, or the address of the function "hello" that is in subtract.cpp, because they have the same name? Why doesn't it use the particular "hello" function that is in the current file, and if it isn't go look for it in another file?
...
*lightbulb* Maybe it's because if there was, say, a multiply.cpp that also included "math.h", then there would be two "hello" functions to choose from? And then, since the preprocessor doesn't keep track of which functions are both from the same header file (and therefore identical), the linker has to have an error, in order to prevent programmers from having undefined results by randomly choosing one function when there could be two different functions with the same name?

Am I on the right track? It's making my mind spin...

This seems like an issue of scope of the functions. Maybe since my functions aren't defined in a class, they have a global scope. Can they possibly conflict with the standard library functions as well? Do I have to keep a large list of all the function names that are "already taken" by the standard libraries, in order to avoid naming conflicts such as my "multiple definition of hello" error?
 
Last edited:
Nishiura_high said:
So, here's what I'm thinking so far. Maybe the preprocessor makes main.cpp look like this:

Code:
#include <iostream>  //Except this would be expanded a whole lot

int add(int x, int y); // function prototype for add.h
int subtract(int x, int y);

int hello(int x, int y)
{
	return 4;
}using namespace std;int main()
{
    cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
    subtract(1,2);
    hello(4,5);
    return 0;
	
}
And subtract.cpp might look like this after being preprocessed:
Code:
int add(int x, int y); // function prototype for add.h
int subtract(int x, int y);

int hello(int x, int y)
{
	return 4;
}int subtract(int x, int y)
{
	return add(4,5);
}

Basically yes.

So when it tries to link it together, the linker doesn't know whether to use the address of the function "hello" that is in main.cpp, or the address of the function "hello" that is in subtract.cpp, because they have the same name?

The linker doesn't know anything about .cpp files. What the linker sees is a collection of chunks of compiled object code that have names. Within those chunks of object code are references (function calls) to other named chunks of object code. It's the job of the linker to convert ("resolve") the names in those function calls to actual addresses to jump to during program execution. If there are two chunks named "hello", the linker has a problem: which one does it use when it encounters a call to "hello"?

Maybe since my functions aren't defined in a class, they have a global scope. Can they possibly conflict with the standard library functions as well? Do I have to keep a large list of all the function names that are "already taken" by the standard libraries, in order to avoid naming conflicts such as my "multiple definition of hello" error?

In C++, the concept of "namespaces" addresses this problem. In effect, all standard library function names are prefixed with "std::". The "using namespace std;" statement that you've probably been slapping into every program that you've written so far, without knowing why, basically tells the compiler, "whenever you see a call to a function that doesn't look like 'something::functionname()', first assume I mean 'std::functionname()' and try to find a match for it; if there's no match, then look for plain old 'functionname()' among the functions that I've defined."

You can define your own namespace, so that you can have both 'std::sin()' and, say, 'nishiura::sin()' without running into multiple-definition problems.

(I've probably oversimplified here, so use this only as a starting point for further study of namespaces!)
 
jtbell said:
In C++, the concept of "namespaces" addresses this problem. In effect, all standard library function names are prefixed with "std::". The "using namespace std;" statement that you've probably been slapping into every program that you've written so far, without knowing why, basically tells the compiler, "whenever you see a call to a function that doesn't look like 'something::functionname()', first assume I mean 'std::functionname()' and try to find a match for it; if there's no match, then look for plain old 'functionname()' among the functions that I've defined."

You can define your own namespace, so that you can have both 'std::sin()' and, say, 'nishiura::sin()' without running into multiple-definition problems.

This is not correct. While unnamed namespaces are the preferred way to provide internal linkage with C++, namespaces in general will not solve multiple definition problems*.

The two acceptable solutions to the OP's problem are:

  • Put the hello function in an unnamed namespace.
    [*]Define the hello function in a separate translation unit (cpp file), and just have a prototype (declaration) in the header file.
* for example this would not successfully build:

h1.h
Code:
#ifndef H1_H
#define H1_H

namespace my_namespace
{
	void function1(){}
}

#endif

h2.h
Code:
#ifndef H2_H
#define H2_H

void function2();

#endif

translation_unit2.cpp
Code:
#include "h1.h"
#include "h2.h"

void function2() {

	my_namespace::function1();
}

main.cpp
Code:
#include "h1.h"
#include "h2.h"int main()
{
	my_namespace::function1();
}

To make it build, one could use an anonymous namespace in h1.h (and forgo the "my_namespace::" prefixes in both cpp files).

h1.h
Code:
#ifndef H1_H
#define H1_H

namespace
{
	void function1(){}
}

#endif
 
Last edited:
MisterX said:
This is not correct. While unnamed namespaces are the preferred way to provide internal linkage with C++, namespaces in general will not solve multiple definition problems*.
I disagree completely. Namespaces were invented precisely to solve the multiple definition problem. Package X from vendor A has a class named Foo and a function bar, so does package Y from vendor B. There are no name collisions between the two packages if the vendors enclosed there packages in different namespaces.

The two acceptable solutions to the OP's problem are:

  • Put the hello function in an unnamed namespace.
    [*]Define the hello function in a separate translation unit (cpp file), and just have a prototype (declaration) in the header file.

You missed a third acceptable solution, which is to prefix the function spec in the header with the inline keyword.

Big alarm bells go off in my head when I'm called upon to review some chunk of code and see the use of unnamed namespace.
 
  • #10
D H said:
I disagree completely. Namespaces were invented precisely to solve the multiple definition problem. Package X from vendor A has a class named Foo and a function bar, so does package Y from vendor B. There are no name collisions between the two packages if the vendors enclosed there packages in different namespaces.

No, that's a different problem. The problem we're discussing in this thread is the same code being compiled into two different translation units, and then there is an error upon linking.
D H said:
You missed a third acceptable solution, which is to prefix the function spec in the header with the inline keyword.
Yes, I thought of that, but I wasn't sure that this necessarily implied internal linkage. But you are correct, inline should work as well, although AFAIK it only works for functions. Unnamed namespaces works for global variables and classes as well.
D H said:
Big alarm bells go off in my head when I'm called upon to review some chunk of code and see the use of unnamed namespace.

Well, it's the method one is supposed to use when internal linkage is required.
 

Similar threads

Replies
5
Views
3K
  • · Replies 23 ·
Replies
23
Views
3K
  • · Replies 3 ·
Replies
3
Views
6K
  • · Replies 7 ·
Replies
7
Views
8K
  • · Replies 3 ·
Replies
3
Views
7K
  • · Replies 2 ·
Replies
2
Views
12K
  • · Replies 1 ·
Replies
1
Views
3K
  • · Replies 1 ·
Replies
1
Views
4K
  • · Replies 2 ·
Replies
2
Views
2K
  • · Replies 1 ·
Replies
1
Views
2K