Contact
Evil Genius SocietyHomeLearnForumLeaderboardSocialsMessages
← Back to module

Lesson 6 of 6

You fixed every compiler error. The build still fails, and the message looks completely different — no line number, no excerpt, no helpful note:. You've reached the linker.

/usr/bin/ld: /tmp/ccXY1234.o: in function `main':
main.cpp:(.text+0x1a): undefined reference to `greet()'
collect2: error: ld returned 1 exit status

That's ld, the linker, and it's a different kind of program with a different kind of complaint.

Declaration versus definition

The whole idea rests on this distinction, so it's worth stating plainly.

A declaration says this exists, and here's its shape:

int greet();

A definition says and here is what it does:

int greet() {
    return 42;
}

The compiler only needs declarations. When it sees greet() called, it checks that a greet has been declared, that you passed the right arguments, and that you used the result correctly — then it emits a note saying "call whatever greet turns out to be."

The linker has to resolve that note into a real address. If no definition of greet exists anywhere in the object files or libraries it was given, it fails. It doesn't know or care what greet means — only that a name was promised and never delivered.

That's why the messages read so differently. The compiler talks about types and syntax at a specific line. The linker talks about names, and often can't tell you a useful line at all.

The four causes

Almost every undefined reference is one of these.

1. Declared but never defined

You wrote the declaration and forgot the body, or wrote the body and never saved the file.

// WILL NOT COMPILE
#include <iostream>

int greet();   // declared...

int main() {
    std::cout << greet() << "\n";
    return 0;
}
// ...never defined. Linker error.

2. Signature mismatch

The subtlest one. You did define it — but not quite the same function.

// WILL NOT COMPILE
#include <iostream>

int greet(int times);          // declared taking an int

int greet(double times) {      // defined taking a double — a different function
    return static_cast<int>(times);
}

int main() {
    std::cout << greet(3) << "\n";
    return 0;
}

C++ allows several functions with the same name and different parameters (lesson 5.6), so greet(int) and greet(double) are genuinely two different things. You defined one and called the other. Watch for mismatched parameter types, a missing const, or a typo in the name.

3. Forgot to compile one of your files

Your project has main.cpp and helpers.cpp, and you built only the first:

g++ main.cpp -o main          # helpers.cpp never compiled → undefined reference
g++ main.cpp helpers.cpp -o main   # correct

This is the most common cause once a project outgrows one file, and it's precisely the problem build systems exist to solve (Module 17).

You used something from an external library and didn't tell the linker where it lives. The fix is a -l flag, e.g. -lm for the older C math library. Modern C++ and the standard library are linked automatically by g++, so you'll mostly meet this with third-party libraries.

The special case: no main

/usr/lib/.../Scrt1.o: in function `_start':
(.text+0x1b): undefined reference to `main'

The startup code that runs before your program needs a function called main and couldn't find one. Usually: you misspelled it (Main, mian), or you compiled the wrong file, or you passed -c and never actually linked.

Telling the two apart at a glance

| | Compiler error | Linker error | | --- | --- | --- | | Names a line of your code | Yes, precisely | Rarely, and vaguely | | Mentions | types, syntax, scope | undefined reference, ld, collect2 | | Typical cause | you wrote something wrong | something is missing | | Fix | change the code at that line | supply the definition, file, or library |

If the message says undefined reference or mentions ld or collect2, stop looking for a typo on that line. Ask instead: what didn't I build, define, or link?

Try it

Produce a linker error on purpose, then fix it — twice, two different ways. First by declaring a function you never define. Then by defining it with a different parameter type than you declared.

Module 2 complete

You now know what happens between typing g++ and getting a program: four stages, each failing in its own way. You can read a compiler error down to the line and column, you know to fix the first one first, and you can tell at a glance when a failure is the linker's complaint rather than the compiler's.

That's the debugging foundation for everything that follows. Next up: Module 3 — Values and Types, where you start actually storing things.

Questions about this lesson

Log in to ask a question.