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

In summary: I think that's what you call it. I'm still learning, sorry!)#In summary, the program violates the one definition rule by having the function hello(int, int) defined multiple times in different files. This is because the preprocessor does not keep track of which functions are both from the same header file and therefore identical. As a result, the linker cannot determine which function to use and produces an error to prevent undefined symbols. The solution is to use the inline qualifier when defining functions in headers.
  • #1
Nishiura_high
7
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
  • #2
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;
}
 
  • #3
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:
  • #4
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!
 
  • #5
Nishiura_high said:
How is it defined multiple times, when I have the include guard?

See my post above.
 
  • #6
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:
  • #7
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!)
 
  • #8
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:
  • #9
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.
 

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

1. What is a "multiple definition" bug in C++?

A multiple definition bug in C++ occurs when the same function or variable is defined more than once in a program. This can happen when multiple source files are compiled together, resulting in multiple definitions of the same function or variable.

2. How does this bug manifest in Ubuntu with g++?

When using g++ on Ubuntu, the compiler will throw an error indicating that the same function or variable has been defined more than once. This is because g++ is stricter in its handling of multiple definitions compared to other compilers.

3. What could have caused this bug to occur?

This bug is typically caused by a mistake in the code, where a function or variable is accidentally defined more than once. This can happen if the same header file is included in multiple source files, or if a function is accidentally declared and defined in both a header and source file.

4. How can this bug be fixed?

The fix for this bug depends on the specific cause. In general, you will need to remove any duplicate definitions of the function or variable in question. This may involve removing unnecessary include statements or consolidating multiple definitions into one.

5. How can this bug be prevented in the future?

To prevent this bug from occurring in the future, it is important to carefully manage header files and make sure they are only included where necessary. Avoid declaring and defining functions in both header and source files, and use include guards to prevent multiple definitions. Additionally, using a linter or compiler flags can help catch these errors during development.

Similar threads

  • Engineering and Comp Sci Homework Help
Replies
5
Views
2K
  • Engineering and Comp Sci Homework Help
Replies
3
Views
4K
  • Programming and Computer Science
Replies
6
Views
8K
  • Programming and Computer Science
Replies
4
Views
3K
  • Engineering and Comp Sci Homework Help
Replies
1
Views
2K
  • Engineering and Comp Sci Homework Help
Replies
1
Views
771
  • Engineering and Comp Sci Homework Help
Replies
2
Views
2K
  • Engineering and Comp Sci Homework Help
Replies
3
Views
6K
  • Engineering and Comp Sci Homework Help
Replies
5
Views
3K
  • Programming and Computer Science
Replies
3
Views
7K
Back
Top