标准c库链接
作者:互联网
我试图理解标准库何时链接到我自己的二进制文件.我写了以下内容:
#include <stdio.h>
double atof(const char*);
int main(){
const char * v="22";
printf("Cast result is %f", atof(v));
}
它正在用g -c main.cpp编译成功,但是当我链接刚创建的目标文件时,我发现了一个错误.错误描述是:
/tmp/ccWOPOS0.o: In function `main':
main.cpp:(.text+0x19): undefined reference to `atof(char const*)'
collect2: error: ld returned 1 exit status
但是我不明白为什么会出现这个错误?我认为标准c库通过ld链接器自动链接到我的二进制文件.包含头文件和声明我需要显式使用的函数之间有什么区别.
解决方法:
作为C中的一般规则,手动声明库函数(如atof())是一个坏主意.
它曾经在旧的C程序中很常见,但是C没有函数重载,因此它对“几乎”正确的声明更加宽容. (好吧,有些旧的编译器,我真的不能说最新的编译器).这就是为什么我们将C描述为“弱类型”语言,而C是一种更“强类型”的语言.
另一个复杂因素是编译器执行“名称修改”:它们传递给链接器的名称是源名称的修改版本. C编译器可能会从C编译器执行完全不同的名称修改. atof()的标准lib版本是C函数.要在C源文件中声明它,您需要将其声明为
extern "C"
{
double atof(const char *);
}
或者可能
extern "C" double atof(const char *);
还有许多额外的复杂性,但这足以继续下去.
最安全的想法是只包含适当的标题.
#include <iostream>
#include <cstdlib>
int main()
{
const char v[]= "22";
std::cout << "Cast result is " << atof(v) << std::endl;
return 0;
}
响应@DmitryFucintv评论的额外背景
>调用约定
调用函数时,calling convention是关于如何在调用函数和被调用函数之间传递参数和返回值的协议.在x86架构中,最常见的两个是__cdecl和__stdcall,但是存在许多其他架构.
考虑以下:
/* -- 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;
}
在C程序中,这可能会编译并链接OK.函数f()期望__stdcall格式的args,但我们以__cdecl格式传递它们.结果是不确定的,但很容易导致堆栈损坏.
因为C链接器有点麻烦,它可能会产生类似你看到的错误.大多数人都认为这是一个更好的结果.
2姓名Mangling
Name Mangling(或名称修饰)是一种方案,其中编译器向对象名称添加一些额外的字符以向链接器提供一些提示.对象可以是函数或变量.允许函数重载的语言(如C和Java)必须执行类似的操作,以便链接器可以区分具有相同名称的不同函数.
例如
int f(int a);
int f(double a);
int f(const char *a, ...);
标签:c,linker,standard-library 来源: https://codeday.me/bug/20190830/1771244.html