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
- Calling conventions
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, ...);
It's because atof
has C linkage, and you're compiling this as C++ - change:
double atof(const char*);
to:
extern "C" double atof(const char*);
and it should work.
Obviously you should not normally do this and you should just use the correct header:
#include <cstdlib>
This has nothing to do with the standard library.
The problem you have is that atof
is not being defined, so the linker doesn't find it. You need to define the function, otherwise is impossible to know what the code is supposed to do.
And it looks like atof
is a C
function in the header stdlib.h. This code should work, although is not using C++ exclusive functions.
#include <stdlib.h>
int main(){
const char * v="22";
printf("Cast result is %f", atof(v));
}
When you declare atof
, you're declaring a subtly different function to the standard one. The function you're declaring is not defined in the standard library.
Don't re-declare standard functions, because you're liable to getting it wrong, as here. You're including the header and the header correctly declares the functions for you.