Common C++ Error Messages #2 – Undefined reference
In this article I’ll be looking at the “undefined reference” error message (or “unresolved external symbol, for Visual C++ users). This is not actually a message from the compiler, but is emitted by the linker, so the first thing to do is to understand what the linker is, and what it does.
To understand the linker, you have to understand how C++ programs are built. For all but the very simplest programs, the program is composed of multiple C++ source files (also known as “translation units”). These are compiled separately, using the C++ compiler, to produce object code files (files with a .o or a .obj extension) which contain machine code. Each object code file knows nothing about the others, so if you call a function from one object file that exists in another, the compiler cannot provide the address of the called function.
This is where the the linker comes in. Once all the object files have been produced, the linker looks at them and works out what the final addresses of functions in the executable will be. It then patches up the addresses the compiler could not provide. It does the same for any libraries (.a and .lib files) you may be using. And finally it writes the executable file out to disk.
The linker is normally a separate program from the compiler (for example, the GCC linker is called ld) but will normally be called for you when you use your compiler suite’s driver program (so the GCC driver g++ will call ld for you).
Traditionally, linker technology has lagged behind compilers, mostly because it’s generally more fun to build a compiler than to build a linker. And linkers do not necessarily have access to the source code for the object files they are linking. Put together, you get a situation where linker errors, and the reasons for them, can be cryptic in the extreme.
Put simply, the “undefined reference” error means you have a reference (nothing to do with the C++ reference type) to a name (function, variable, constant etc.) in your program that the linker cannot find a definition for when it looks through all the object files and libraries that make up your project. There are any number of reasons why it can’t find the definition – we’ll look at the commonest ones now.
Probably the most common reason for unresolved reference errors is that you simply have not defined the thing you are referencing. This code illustrates the problem:
Here, we have a declaration of the function foo(), which we call in main(), but no definition. So we get the error (slightly edited for clarity):
The way to fix it is to provide the definition:
Another common error is to provide a definition that does not match up with declaration (or vice versa). For example, if the code above we had provided a definition of foo() that looked like this:
then we would still get an error from the linker because the signatures (name, plus parameter list types) of the declaration and definition don’t match, so the definition actually defines a completely different function from the one in the declaration. To avoid this problem, take some care when writing declarations and definitions, and remember that things like references, pointers and const all count towards making a function signature unique.
Didn’t Link Object File
This is another common problem. Suppose you have two C++ source files:
If you compile f1.cpp on its own you get this:
and if you compile f2.cpp on its own, you get this even more frightening one:
In this situation, you need to compile both the the source files on the same command line, for example, using GCC:
or if you have compiled them separately down to object files:
For further information on compiling and linking multiple files in C++, particularly with GCC, please see my series of three blog articles starting here.
Wrong Project Type
The linker error regarding WinMain above can occur in a number of situations, particularly when you are using a C++ IDE such as CodeBlocks or Visual Studio. These IDEs offer you a number of project types such as “Windows Application” and “Console Application”. If you want to write a program that has a int main() function in it, always make sure that you choose “Console Application”, otherwise the IDE may configure the linker to expect to find a WinMain() function instead.
To understand this issue, remember that a header file (.h) is not a library. The linker neither knows nor cares about header files – it cares about .a and .lib files. So if you get a linker error regarding a name that is in a library you are using, it is almost certainly because you have not linked with that library. To perform the linkage, if you are using an IDE you can normally simply add the library to your project, if using the command line, once again please see my series of blog articles on the GCC command line starting here, which describes some other linker issues you may have.
The unresolved reference error can have many causes, far from all of which have been described here. But it’s not magic – like all errors it means that you have done something wrong, in you code and/or your project’s configuration, and you need to take some time to sit down, think logically, and figure out what.
C++ Errors: Undefined Reference, Unresolved External Symbol etc.
This Tutorial Details the Critical Errors that Programmers often Encounter in C++like Undefined Reference, a Segmentation Fault (core dumped) and Unresolved External Symbol:
We will discuss the most important errors that we often encounter in C++ that are equally critical indeed. Apart from the system and semantic errors and exceptions that occur from time to time, we also get other critical errors that affect the running of programs.
These errors mostly occur towards the end of the program at runtime. Sometimes the program gives proper output and then the error occurs.
What You Will Learn:
Important C++ Errors
In this tutorial, we will discuss three types of errors that are critical from any C++ programmer’s point of view.
- Undefined reference
- Segmentation fault (core dumped)
- Unresolved external symbol
We will discuss the possible causes of each of these errors and along with the precautions that we can take as a programmer to prevent these errors.
An “Undefined Reference” error occurs when we have a reference to object name (class, function, variable, etc.) in our program and the linker cannot find its definition when it tries to search for it in all the linked object files and libraries.
Thus when the linker cannot find the definition of a linked object, it issues an “undefined reference” error. As clear from definition, this error occurs in the later stages of the linking process. There are various reasons that cause an “undefined reference” error.
We discuss some of these reasons below:
#1) No Definition Provided For Object
This is the simplest reason for causing an “undefined reference” error. The programmer has simply forgotten to define the object.
Consider the following C++ program. Here we have only specified the prototype of function and then used it in the main function.
So when we compile this program, the linker error that says “undefined reference to ‘func1()’” is issued.
In order to get rid of this error, we correct the program as follows by providing the definition of the function func1. Now the program gives the appropriate output.
#2) Wrong Definition (signatures don’t match) Of Objects Used
Yet another cause for “undefined reference” error is when we specify wrong definitions. We use any object in our program and its definition is something different.
Consider the following C++ program. Here we have made a call to func1 (). Its prototype is int func1 (). But its definition does not match with its prototype. As we see, the definition of the function contains a parameter to the function.
Thus when the program is compiled, the compilation is successful because of the prototype and function call match. But when the linker is trying to link the function call with its definition, it finds the problem and issues the error as “undefined reference”.
Thus to prevent such errors, we simply cross-check if the definitions and usage of all the objects are matching in our program.
#3) Object Files Not Linked Properly
This issue can also give rise to the “undefined reference” error. Here, we may have more than one source files and we might compile them independently. When this is done, the objects are not linked properly and it results in “undefined reference”.
Consider the following two C++ programs. In the first file, we make use of the “print ()” function which is defined in the second file. When we compile these files separately, the first file gives “undefined reference” for the print function, while the second file gives “undefined reference” for the main function.
The way to resolve this error is to compile both the files simultaneously ( For example, by using g++).
Apart from the causes already discussed, “undefined reference” may also occur because of the following reasons.
#4) Wrong Project Type
When we specify wrong project types in C++ IDEs like the visual studio and try to do things that the project does not expect, then, we get “undefined reference”.
#5) No Library
If a programmer has not specified the library path properly or completely forgotten to specify it, then we get an “undefined reference” for all the references the program uses from the library.
#6) Dependent Files Are Not Compiled
A programmer has to ensure that we compile all the dependencies of the project beforehand so that when we compile the project, the compiler finds all the dependencies and compiles successfully. If any of the dependencies are missing then the compiler gives “undefined reference”.
Apart from the causes discussed above, the “undefined reference” error can occur in many other situations. But the bottom line is that the programmer has got the things wrong and in order to prevent this error they should be corrected.
Segmentation Fault (core dumped)
The error “segmentation fault (core dumped)” is an error that indicates memory corruption. It usually occurs when we try to access a memory that does not belong to the program into consideration.
Here are some of the reasons that cause Segmentation fault error.
#1) Modifying The Constant String
Consider the following program wherein we have declared a constant string. Then we try to modify this constant string. When the program is executed, we get the error shown in the output.
#2) Dereferencing Pointer
A pointer must point to a valid memory location before we dereference it. In the below program, we see that the pointer is pointing to NULL which means the memory location it’s pointing to is 0 i.e. invalid.
Hence when we dereference it in the next line, we are actually trying to access its unknown memory location. This indeed results in a segmentation fault.
The next program shows a similar case. In this program also, the pointer is not pointing to valid data. An uninitialized pointer is as good as NULL and hence it also points to unknown memory location. Thus when we try to dereference it, it results in a segmentation fault.
In order to prevent such errors, we have to ensure that our pointer variables in the program point to valid memory locations always.
#3) Stack Overflow
When we have recursive calls in our program, they eat up all the memory in the stack and cause the stack to overflow. In such cases, we get the segmentation fault as running out of stack memory is also a kind of memory corruption.
Consider the below program where we calculate the factorial of a number recursively. Note that our base condition tests if the number is 0 and then returns 1. This program works perfectly for positive numbers.
But what happens when we actually pass a negative number to a factorial function? Well, as the base condition is not given for the negative numbers, the function does not know where to stop and thus results in a stack overflow.
This is shown in the output below that gives segmentation fault.
Segmentation fault (core dumped)
Now in order to fix this error, we slightly change the base condition and also specify the case for negative numbers as shown below.
Now we see that the segmentation fault is taken care of and the program works fine.
Unresolved External Symbol
The unresolved external symbol is a linker error that indicates it cannot find the symbol or its reference during the linking process. The error is similar to “undefined reference” and is issued interchangeably.
We have given two instances below where this error can occur.
#1) When we refer a structure variable in the program that contains a static member.
In the above program, structure C has a static member s that is not accessible to the outside programs. So when we try to assign it a value in the main function, the linker doesn’t find the symbol and may result in an “unresolved external symbol” or “undefined reference”.
The way to fix this error is to explicitly scope the variable using ‘::’ outside the main before using it.
#2) When we have external variables referenced in the source file, and we have not linked the files that define these external variables.
This case is demonstrated below:
In general, in case of an “unresolved external symbol”, the compiled code for any object like function fails to find a symbol to which it makes a reference to, maybe because that symbol is not defined in the object files or any of the libraries specified to the linker.
In this tutorial, we discussed some major errors in C++ that are critical and can affect the program flow and might even result in an application crash. We explored all about Segmentation fault, Unresolved external symbol, and Undefined reference in detail.
Although these errors can occur anytime, from the causes that we discussed we know that we can easily prevent them by carefully developing our program.