标准C++库链接

3

我正在尝试了解标准库何时链接到我的二进制文件中。下面是我写的代码:

#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 链接器自动链接到我的二进制文件上了。包含头文件和显式声明需要使用的函数之间有什么区别?

4个回答

5
作为C++的一般规则,手动声明库函数,如atof(),是一个不好的想法。
在旧的C程序中这是常见的,但是C没有函数重载,因此对于“几乎”正确的声明更加宽容。(嗯,一些旧编译器是这样的,我不能真正代表最新的编译器)。这就是为什么我们将C描述为“弱类型”语言,而C++是一种更“强类型”的语言。
另一个复杂因素是编译器执行“名称修饰”:它们传递给链接器的名称是源名称的修改版本。 C编译器可能会执行与C ++编译器非常不同的名称修饰。标准库版本的atof()是一个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 的评论所需的额外背景

  1. 调用约定

在调用函数时,调用约定 是关于参数和返回值如何在调用函数和被调用函数之间传递的协议。在 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程序中,这可能会编译和链接成功。函数f()期望以__stdcall格式传递参数,但我们以__cdecl格式传递参数。结果是不确定的,但很容易导致堆栈损坏。
因为C++链接器有些挑剔,它可能会生成像你看到的那样的错误。大多数人认为这是更好的结果。
2 名称修饰 名称修饰(或名称装饰)是一种方案,其中编译器向对象名称添加一些额外的字符,以给链接器一些提示。一个对象可以是函数或变量。允许函数重载的语言(如C++和Java)必须做一些类似的事情,以便链接器可以区分具有相同名称的不同函数。 例如:
int f(int a);
int f(double a);
int f(const char *a, ...);

1
@PaulR - 当然非常正确。我已经修复了这个错误。 - Michael J
谢谢您的回答。但我还有一些疑问。对于纯C++标准函数,我们是否可以只声明而不需要其他修饰符?我非常想知道如何在这种情况下解决extern "C"。 - user2953119
@DmitryFucintv - 我认为extern "C"是标准的C++。但是带回家的信息是:使用头文件而不是自己声明标准函数。你为什么想要自己声明函数?这样做有什么好处吗? - Michael J
仅供教育需要。我正在尝试理解链接器的工作原理。 - user2953119
@DmitryFucintv - 我添加了一些额外的背景材料和一些有用的链接。看看这是否使它更清晰明了。 - Michael J

1
这是因为atof具有C链接,而您正在将其编译为C++ - 请更改:
double atof(const char*);

至:

extern "C" double atof(const char*);

它应该可以工作。

显然,你通常不应该这样做,而应该使用正确的标题:

 #include <cstdlib>

0

这与标准库无关。

你遇到的问题是atof未被定义,因此链接器找不到它。你需要定义该函数,否则就无法知道代码应该做什么。

而且看起来atof是头文件stdlib.h中的一个C函数。这段代码应该可以工作,尽管没有使用C++专有的函数。

#include <stdlib.h>

int main(){
    const char * v="22";
    printf("Cast result is %f", atof(v));
}

0

当你声明 atof 时,你声明了一个与标准函数略有不同的函数。你声明的函数在标准库中是未定义的。

不要重新声明标准函数,因为你很可能会出错,就像这里一样。你只需要包含头文件,头文件会正确地为你声明函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接