我试图了解标准库何时链接到我自己的二进制文件。我写了以下内容:
#include <stdio.h>
double atof(const char*);
int main(){
const char * v="22";
printf("Cast result is %f", atof(v));
}
Run Code Online (Sandbox Code Playgroud)
它编译成功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
Run Code Online (Sandbox Code Playgroud)
但我不明白为什么会出现这个错误?我认为标准 C++ 库通过链接ld
器自动链接到我的二进制文件。包含头文件和仅声明我需要显式使用的函数之间有什么区别。
作为 C++ 中的一般规则,手动声明库函数(例如atof()
.
它曾经在旧的 C 程序中很常见,但 C 没有函数重载,因此它对“几乎”正确的声明更加宽容。(好吧,一些旧的编译器是,我不能真正代表最新的编译器)。这就是为什么我们将 C 描述为“弱类型”语言,而 C++ 是一种更“强类型”的语言。
另一个复杂之处是编译器执行“名称修改”:它们传递给链接器的名称是源名称的修改版本。C 编译器执行的名称修改可能与 C++ 编译器完全不同。标准库版本atof()
是 C 函数。要在 C++ 源文件中声明它,您需要将其声明为
extern "C"
{
double atof(const char *);
}
Run Code Online (Sandbox Code Playgroud)
或者可能
extern "C" double atof(const char *);
Run Code Online (Sandbox Code Playgroud)
还有许多额外的复杂性,但这足以继续下去。
最安全的想法是只包含适当的标头。
#include <iostream>
#include <cstdlib>
int main()
{
const char v[]= "22";
std::cout << "Cast result is " << atof(v) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
回应 @DmitryFucintv 评论的额外背景
调用函数时,调用约定是关于调用函数和被调用函数之间如何传递参数和返回值的协议。在 x86 架构上,最常见的两个是__cdecl和__stdcall,但也存在许多其他的。
考虑以下:
/* -- f.c --*/
int __stdcall f(int a, double b, char *c)
{
// do stuff
return something;
}
Run Code Online (Sandbox Code Playgroud)
/* 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;
}
Run Code Online (Sandbox Code Playgroud)
在 C 程序中,这可能会编译和链接正常。该函数f()
期望其参数采用 __stdcall 格式,但我们以 __cdecl 格式传递它们。结果是不确定的,但很容易导致堆栈损坏。
因为 C++ 链接器有点复杂,所以它可能会生成像您看到的那样的错误。大多数人都会同意这是一个更好的结果。
2 名称修改
名称重整(或名称修饰)是一种方案,编译器在对象名称中添加一些额外的字符,以向链接器提供一些提示。对象可以是函数或变量。允许函数重载的语言(如 C++ 和 Java)必须执行类似的操作,以便链接器能够区分同名的不同函数之间的区别。例如
int f(int a);
int f(double a);
int f(const char *a, ...);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1281 次 |
最近记录: |