I'm trying to understand when does standard library linking to my own binary. I've written the following:
#include <stdio.h>
double atof(const char*);
int main(){
const char * v="22";
printf("Cast result is %f", atof(v));
}
It's compiling successful with g++ -c main.cpp
, but when I'm linking just created object file I've an error. Error descriptio is:
/tmp/ccWOPOS0.o: In function `main':
main.cpp:(.text+0x19): undefined reference to `atof(char const*)'
collect2: error: ld returned 1 exit status
But I don't understand why this error is caused? I think that the standard c++ library automatically linked to my binary by the ld
linker. What is the difference between the including header files and just declaring a functions which I need to use explicitly .
As a general rule in C++, it is a bad idea to manually declare library functions such as atof()
.
It used to be common in old C programs, but C doesn't have function overloading so it is more forgiving about "almost" correct declarations. (Well some of the old compilers were, I can't really speak for the newest ones). That is why we describe C as a "weakly typed" language, while C++ is a more "strongly typed" language.
An additional complication is that the compilers perform "name mangling": the name they pass to the linker is a modified version of the source name. The C compiler may perform quite different name mangling from the C++ compiler. The standard lib version of atof()
is a C function. To declare it in a C++ source file you need to declare it as
extern "C"
{
double atof(const char *);
}
or possibly
extern "C" double atof(const char *);
There are many additional complexities, but that is enough to go on with.
Safest idea is to just include the appropriate headers.
#include <iostream>
#include <cstdlib>
int main()
{
const char v[]= "22";
std::cout << "Cast result is " << atof(v) << std::endl;
return 0;
}
Extra background in response to comment by @DmitryFucintv
When calling a function, a calling convention is an agreement on how parameters and return values are passed between the calling function and the called function. On x86 architecture, the two most common are __cdecl and __stdcall, but a number of others exist.
Consider the following:
/* -- f.c --*/
int __stdcall f(int a, double b, char *c)
{
// do stuff
return something;
}
/* main.c */
#include <iostream>
extern int __cdecl f(int a, double b, char *c);
int main()
{
std::cout << f(1, 2.3, "45678") << std::endl;
return 0;
}
In a C program, this will probably compile and link OK. The function f()
is expecting its args in __stdcall format, but we pass them in __cdecl format. The result is indeterminate, but could easily lead to stack corruption.
Because the C++ linker is a bit fussier, it will probably generate an error like the one you saw. Most would agree that is a better outcome.
2 Name Mangling
Name Mangling (or name decoration) is a scheme where the compiler adds some extra characters to the object name to give some hints to the linker. An object might be a function or a variable. Languages that permit function overloading (like C++ and Java) must do something like this so that the linker can tell the difference between different functions with the same name. e.g.
int f(int a);
int f(double a);
int f(const char *a, ...);