C语言 C 头文件和编译/链接

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18548157/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:19:47  来源:igfitidea点击:

C header files and compilation/linking

clinker

提问by Aristides

I know that header files have forward declarations of various functions, structs, etc. that are used in the .cfile that 'calls' the #include, right? As far as I understand, the "separation of powers" occurs like this:

我知道头文件有各种函数、结构等的前向声明,这些函数、结构等在.c“调用” 的文件中使用#include,对吧?据我了解,“三权分立”是这样发生的:

Header file: func.h

头文件: func.h

  • contains forward declaration of function

    int func(int i);
    
  • 包含函数的前向声明

    int func(int i);
    

C source file: func.c

C源文件: func.c

  • contains actual function definition

    #include "func.h"
    
    int func(int i) {
        return ++i ;
    }
    
  • 包含实际的函数定义

    #include "func.h"
    
    int func(int i) {
        return ++i ;
    }
    

C source file source.c(the "actual" program):

C 源文件source.c(“实际”程序):

#include <stdio.h>
#include "func.h"

int main(void) {
    int res = func(3);
    printf("%i", res);
}

My question is: seeing that the #includeis simply a compiler directive that copies the contents of the .hin the file that #includeis in, how does the .cfile know how to actually execute the function? All it's getting is the int func(int i);, so how can it actually perform the function? How does it gain access to the actual definition of func? Does the header include some sort of 'pointer' that says "that's my definition, over there!"?

我的问题是:看到这#include只是一个编译器指令,它复制.h了文件中的内容,文件#include如何.c知道如何实际执行该函数?它得到的只是int func(int i);,那么它如何实际执行该功能呢?它如何访问 的实际定义func?标题是否包含某种“指针”,上面写着“这是我的定义,在那边!”?

How does it work?

它是如何工作的?

采纳答案by Emil Vatai

Uchia Itachi gave the answer. It's the linker.

宇智亚鼬给出了答案。这是链接器

Using GNU C compiler gccyou would compile a one-file program like

使用 GNU C 编译器,gcc你可以编译一个单文件程序,如

gcc hello.c -o hello # generating the executable hello

But compiling the two (or more) file program as described in your example, you would have to do the following:

但是按照示例中的描述编译两个(或更多)文件程序,您必须执行以下操作:

gcc -c func.c # generates the object file func.o
gcc -c main.c # generates the object file main.o
gcc func.o main.o -o main # generates the executable main

Each object file has external symbols (you may think of it as public members). Functions are by default external while (global) variables are by default internal. You could change this behavior by defining

每个目标文件都有外部符号(您可以将其视为公共成员)。函数默认是外部的,而(全局)变量默认是内部的。您可以通过定义来改变这种行为

static int func(int i) { # static linkage
    return ++i ;
}

or

或者

/* global variable accessible from other modules (object files) */
extern int global_variable = 10; 

When encountering a call to a function, not defined in the main module, the linker searches all the object files (and libraries) provided as input for the module where the called function is defined. By default you probably have some libraries linked to your program, that's how you can use printf, it's already compiled into a library.

当遇到对主模块中未定义的函数的调用时,链接器将搜索作为定义被调用函数的模块的输入而提供的所有目标文件(和库)。默认情况下,您可能有一些库链接到您的程序,这就是您可以使用的方式printf,它已经编译成一个库。

If you are really interested, try some assembly programming. These names are the equivalent of labels in assembly code.

如果您真的有兴趣,请尝试一些汇编编程。这些名称相当于汇编代码中的标签。

回答by Some programmer dude

It's the linker that handles all that. The compiler just emits a special sequence in the object file saying "I have this external symbol func, please resolve it" for the linker. Then linker sees that, and searches all other object files and libraries for the symbol.

它是处理所有这些的链接器。编译器只是在目标文件中发出一个特殊序列,func为链接器说“我有这个外部符号,请解析它”。然后链接器看到这一点,并在所有其他目标文件和库中搜索该符号。

回答by Henrik

A declaration of a symbol without a definition within the same compilation unit tells the compiler to compile with a placeholder for that symbol's address into an object file.

在同一编译单元中没有定义的符号声明告诉编译器使用该符号地址的占位符编译到目标文件中。

The linker will see that a definition for the symbol is required, and will look for external definitions of the symbol in libraries and other object files.

链接器将看到需要符号定义,并将在库和其他目标文件中查找符号的外部定义。

If the linker finds a definition, the placeholder in the original object file will be replaced with the found address in the final executable.

如果链接器找到定义,则原始目标文件中的占位符将替换为最终可执行文件中找到的地址。

回答by Potatoswatter

The header provides access not only to other .cfiles in the same program, but likewise to libraries that may be distributed in binary form. The relationship of one .cfile to another is exactly the same as a library that depends on another.

.c文件不仅提供对同一程序中其他文件的访问,还提供对可能以二进制形式分发的库的访问。一个.c文件与另一个文件的关系与依赖于另一个文件的库完全相同。

Since a programming interface needs to be in text form no matter the format of the implementation, header files make sense as a separation of concerns.

由于无论实现的格式如何,编程接口都需要采用文本形式,因此头文件作为关注点分离是有意义的。

As others have mentioned, the program that resolves function calls and accesses between libraries and sources (translation units) is called the linker.

正如其他人所提到的,解决库和源(翻译单元)之间的函数调用和访问的程序称为链接器。

The linker does not work with headers. It just makes a big table of all the names that are defined in all the translation units and libraries, then links those names to the lines of code that access them. Archaic usage of C even allows for calling a function without any implementation declaration; it was just assumed that every undefined type was an int.

链接器不适用于标头。它只是制作了一个包含在所有翻译单元和库中定义的所有名称的大表,然后将这些名称链接到访问它们的代码行。C 的古老用法甚至允许在没有任何实现声明的情况下调用函数;只是假设每个未定义的类型都是int.

回答by NlightNFotis

Generally when you compile a file like this:

通常,当您编译这样的文件时:

gcc -o program program.c

You really are calling a driver program, which does the following:

您实际上是在调用一个驱动程序,它执行以下操作:

  • preprocessing (if you asked for it to be a seperate step) using cpp.
  • compiling (may be integrated with preprocessing) using cc1
  • assembling, using as(gas, the GNU Assembler).
  • linking using collect2, which also uses ld(the GNU linker).
  • 预处理(如果你要求它是一个单独的步骤)使用cpp.
  • 编译(可以与预处理集成)使用 cc1
  • 组装,使用as(gas,GNU 汇编器)。
  • 链接 using collect2,它也使用ld(GNU 链接器)。

Typically, during the first 3 stages, you create a simple object file (.oextension), which gets created by compiling a compilation unit (that is a .c file, with the #include and other directives replaced by the preprocessor).

通常,在前 3 个阶段,您会创建一个简单的目标文件(.o扩展名),该文件通过编译编译单元(即 .c 文件,#include 和其他指令由预处理器替换)来创建。

The 4th stage is the one that creates the final executable. After compilation of a unit, the compiler marks several pieces of code as references that need to be resolved by the linker. The linker's job is to search among many compilation units and resolve references to external compilation units.

第四阶段是创建最终可执行文件的阶段。编译单元后,编译器将几段代码标记为需要由链接器解析的引用。链接器的工作是在许多编译单元中搜索并解析对外部编译单元的引用。