C++ 在编译系统中,链接器(ld)如何知道将myprogram.o链接到谁?

C++ 在编译系统中,链接器(ld)如何知道将myprogram.o链接到谁?,c++,c,compiler-construction,C++,C,Compiler Construction,我最近读了CSAPP,对其中的编译系统部分有些怀疑 现在我们有一个使用HelloWorld.c的示例(只需打印HelloWorld)。书中说,在预处理阶段,他们用这个头文件的内容替换“#include”行。但是当我打开stdio.h时,我发现只有printf()的声明,没有具体的实现。那么在编译系统中,什么时候会引入printf()的具体实现呢 书中还说,在链接阶段,链接器(ld)链接了helloworld.o和printf.o。为什么链接器知道将我的对象文件链接到printf.o?在编译系统中

我最近读了CSAPP,对其中的编译系统部分有些怀疑

现在我们有一个使用HelloWorld.c的示例(只需打印HelloWorld)。书中说,在预处理阶段,他们用这个头文件的内容替换“#include”行。但是当我打开stdio.h时,我发现只有printf()的声明,没有具体的实现。那么在编译系统中,什么时候会引入printf()的具体实现呢

书中还说,在链接阶段,链接器(ld)链接了helloworld.o和printf.o。为什么链接器知道将我的对象文件链接到printf.o?在编译系统中,为什么它在第一步(预处理器阶段)声明此函数,并在最后一步(链接阶段)链接具体实现?

为什么链接器知道将我的对象文件链接到printf.o

LD知道如何搜索和找到它们。你可以看到和男人在一起的感觉:

如果共享对象依赖项不包含斜杠,则为 按以下顺序搜索:

  • 使用二进制文件的DT_RPATH dynamic section属性中指定的目录(如果存在)和DT_RUNPATH属性不存在 存在。不推荐使用DT_RPATH
  • 使用环境变量LD_LIBRARY_PATH,除非可执行文件正在安全执行模式下运行(请参见下文),其中 案例忽略此变量
  • 使用二进制文件(如果存在)的DT_运行路径动态部分属性中指定的目录。这样的目录只会被搜索 要查找DT_所需的对象,需要(直接依赖项) 条目和不适用于这些对象的子对象,这些子对象必须 它们自己有自己的DT_运行路径条目。这与DT_RPATH不同, 用于搜索依赖关系树中的所有子级
  • 从缓存文件/etc/ld.so.cache中,该文件包含以前在增强的 图书馆路径。但是,如果二进制文件与-z nodeflib链接 链接器选项时,将跳过默认路径中的共享对象。共享 安装在硬件功能目录(见下文)中的对象是 优先于其他共享对象
  • 在默认路径/lib中,然后是/usr/lib。(在某些64位体系结构上,64位共享对象的默认路径是/lib64, 然后是/usr/lib64。)如果二进制文件与-z nodeflib链接 链接器选项,跳过此步骤
在编译系统中,为什么它在第一步(预处理器阶段)声明此函数,并在最后一步(链接阶段)链接具体实现

在编译阶段,您需要知道要链接到什么并进行相应的编译,因此需要读取具有定义的
.h
文件。在链接阶段,只需要
.o
文件

为什么链接器知道将我的对象文件链接到printf.o

因为编译器在其生成的内容(通常称为对象文件(.o))中记录了这一点

为什么它要在第一步声明这个函数

我想知道这件事

。。。并将具体实施与最后一步联系起来


因为不需要提前做这个。

< P>所有的C++和C++标准告诉你,你需要<代码>包含一个给定的头文件,以便引入一些功能(在一些平台上,即使是一个很好的想法,你也可以编写可移植代码)。 这为编译器提供了很大的灵活性

链接(如果有)将自动完成。请注意,有些函数甚至可能硬编码到编译器本身。

默认情况下,每次在C程序中都会链接库(包含printf的实现)


通过包含头,您只需在编译时指定(暂时)已声明函数的实现(在头内部)在其他地方。在链接阶段的后期,这些函数实现会“添加”到代码中。

实际上,过于简化了:

  • 您可以将函数编译到库中(例如unix上的
    .a
    .so
    文件)
  • 该库有一个函数体(汇编指令)和一个函数名。例如,库
    libc.so
    具有
    printf
    函数,该函数从库文件
    libc.so
    中的字符编号
    0xaabbccdd
    开始
  • 你想编译你的程序
  • 您需要知道
    printf
    采用的参数。需要
    int
    吗?是否需要
    char*
    ?它至少需要24分钟吗?它在头文件中-
    intprintf(constchar*,…)。标头告诉编译器如何调用函数(函数采用什么参数以及返回什么类型)。请注意,每个
    .c
    文件都是单独编译的
  • 函数声明(函数接受的参数和返回的参数)不存储在库文件中。它存储在标题中(仅限)。该库具有函数名(仅
    printf
    )和编译后的函数体。标头具有
    int printf(常量字符*,…)没有函数体
  • 你编译你的程序。编译器生成代码,以便将具有适当大小的参数推送到堆栈上。代码从堆栈中获取函数返回的变量。现在,您的程序被编译成程序集,该程序集看起来像
    将指针推送到堆栈上的“%d\n”;在堆栈上推一些int;调用printf;从堆栈中弹出返回的“int”;其余的说明
  • 链接器在编译的程序中搜索,它会看到
    调用printf
    。然后它说:“Och,你的代码中没有
    printf
    body”。所以