为什么在 C 中将编译和链接过程分开很重要?
Why it's important to separate compilation and linking processes in C?
我用 C 编程已经有一段时间了,我想知道为什么分离这个过程(编译和链接)很重要?
有人可以解释一下吗?
实际上,这根本不重要。特别是对于更简单的程序,两个步骤都用一个程序调用来执行,例如
gcc f1.c f2.c f3.c f4.c -o program
从这些源文件创建可执行文件 program
。
但事实仍然是这些是独立的过程,在某些情况下值得注意这一点。
因为,编译负责将每个源代码文件的源代码转换为相应的目标代码。而已。所以编译器不必关心你的外部符号(比如库和 extern
变量)。
链接负责查找这些引用,然后生成单个二进制文件,就好像您的项目是作为单个源代码文件编写的一样。 (我还建议您参考维基百科 linking 页面以了解静态链接和动态链接之间的区别)
如果您碰巧使用工具 Make
,您会发现它不会在您调用 make
时重新编译每个文件,它会查找自上次构建以来修改了哪些文件,并且然后只重新编译它们。然后调用链接过程。当您处理大型项目(例如 linux 内核)时,这可以节省大量时间。
这对于减少重建时间很有用。如果您只更改一个源文件 - 通常不需要重新编译整个项目,只需重新编译一个或几个文件。
现在它可能不像以前那么重要了。
但曾经有一段时间,编译一个项目可能需要几天的时间——我们过去常常在 1980 年代的一个周末完成一个 "complete build"。仅仅解析单个文件的源代码是一件相当大的事情,需要大量的时间和内存,因此语言的设计使得它们的模块(源文件)可以被隔离处理。
结果是 "object files" - .obj
(DOS/Windows/VMS) 和 .o
(unix) 文件 - 其中包含可重定位代码、静态数据和列表导出(我们定义的对象)和导入(我们需要的对象)。 linking 阶段将所有这些粘合在一起成为一个可执行文件,或成为一个存档(.lib
、.a
、.so
、.dll
文件等)以供进一步包含.
使昂贵的编译任务独立运行导致了像 make
这样复杂的增量构建工具的出现,这显着提高了程序员的生产力 - 对于大型 C 项目仍然至关重要,如 Linux 核心。
这也很有用,意味着任何可以编译成目标文件的语言都可以 link 一起编辑。因此,稍加努力,就可以 link C 到 Fortran 到 COBOL 到 C++ 等等。
自那时以来开发的许多语言都突破了目标文件中可以存储的内容的界限。 C++ 模板系统需要特殊处理,重载方法也不太适合,因为普通 .o
文件不支持多个同名函数(参见 C++ name mangling)。 Java 等人使用完全不同的方法,使用自定义代码文件格式和 "native code" 调用机制,该机制粘附到 DLL 和共享对象文件上。
我曾在需要两天时间编译的系统上工作过。你不想做一个小的改变然后必须等待 2 天来测试。
我用 C 编程已经有一段时间了,我想知道为什么分离这个过程(编译和链接)很重要?
有人可以解释一下吗?
实际上,这根本不重要。特别是对于更简单的程序,两个步骤都用一个程序调用来执行,例如
gcc f1.c f2.c f3.c f4.c -o program
从这些源文件创建可执行文件 program
。
但事实仍然是这些是独立的过程,在某些情况下值得注意这一点。
因为,编译负责将每个源代码文件的源代码转换为相应的目标代码。而已。所以编译器不必关心你的外部符号(比如库和 extern
变量)。
链接负责查找这些引用,然后生成单个二进制文件,就好像您的项目是作为单个源代码文件编写的一样。 (我还建议您参考维基百科 linking 页面以了解静态链接和动态链接之间的区别)
如果您碰巧使用工具 Make
,您会发现它不会在您调用 make
时重新编译每个文件,它会查找自上次构建以来修改了哪些文件,并且然后只重新编译它们。然后调用链接过程。当您处理大型项目(例如 linux 内核)时,这可以节省大量时间。
这对于减少重建时间很有用。如果您只更改一个源文件 - 通常不需要重新编译整个项目,只需重新编译一个或几个文件。
现在它可能不像以前那么重要了。
但曾经有一段时间,编译一个项目可能需要几天的时间——我们过去常常在 1980 年代的一个周末完成一个 "complete build"。仅仅解析单个文件的源代码是一件相当大的事情,需要大量的时间和内存,因此语言的设计使得它们的模块(源文件)可以被隔离处理。
结果是 "object files" - .obj
(DOS/Windows/VMS) 和 .o
(unix) 文件 - 其中包含可重定位代码、静态数据和列表导出(我们定义的对象)和导入(我们需要的对象)。 linking 阶段将所有这些粘合在一起成为一个可执行文件,或成为一个存档(.lib
、.a
、.so
、.dll
文件等)以供进一步包含.
使昂贵的编译任务独立运行导致了像 make
这样复杂的增量构建工具的出现,这显着提高了程序员的生产力 - 对于大型 C 项目仍然至关重要,如 Linux 核心。
这也很有用,意味着任何可以编译成目标文件的语言都可以 link 一起编辑。因此,稍加努力,就可以 link C 到 Fortran 到 COBOL 到 C++ 等等。
自那时以来开发的许多语言都突破了目标文件中可以存储的内容的界限。 C++ 模板系统需要特殊处理,重载方法也不太适合,因为普通 .o
文件不支持多个同名函数(参见 C++ name mangling)。 Java 等人使用完全不同的方法,使用自定义代码文件格式和 "native code" 调用机制,该机制粘附到 DLL 和共享对象文件上。
我曾在需要两天时间编译的系统上工作过。你不想做一个小的改变然后必须等待 2 天来测试。