1. 编译和链接----你真的了解HelloWord吗

2024-04-15 03:04

本文主要是介绍1. 编译和链接----你真的了解HelloWord吗,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • 平时的应用程序开发,很少需要关注编译和链接的过程,因为通常的开发环境都是流行的集成开发环境(IDE),比如VS等,这些IDE通常将编译和链接的过程一步完成,我们通常将这种合并到一起的过程称为构建。其实即使用命令行来编译一个源代码文件,简单的一句gcc hello.c 命令就包含了非常复杂的过程。
  • 虽然这些默认配置、编译和链接参数对于大部分的应用程序开发已经足够了,但是在开发过程中,我们会被这种便捷所迷惑,软件的运行机制和机理被掩盖了,一些程序莫名其妙的错误我们无法解决,性能瓶颈也束手无策。只能看到现象,看不到本质,所以要深入了解这些机制。

1.编译器到底隐藏了哪些东西

// hello.c
#include <stdio.h>int main()
{printf("Hello World\n");return 0;
}

在Linux下,当我们使用GCC来编译该程序时,只需要最简单的命令

gcc hello.c
./a.out

在这里插入图片描述

事实上,上述过程可以分解为4个步骤:预处理、编译、汇编、链接

在这里插入图片描述

1.1 预编译

  • 首先是源代码文件hello.c 和相关的头文件,如stdio.h 等被预编译器cpp 预编译成一个.i 文件。对于c++ 来说,它的源文件扩展名可能是.cpp 或者.cxx,头文件的扩展名可能是.hpp ,而预编译后的文件扩展名是.ii

  • 第一步的预编译过程相当于如下命令:其中-E 表示只进行预编译

    gcc -E hello.c -o hello.i

在这里插入图片描述

或者

cpp hello.c > hello.i

  • 预编译过程主要处理源文件中以# 开始的预编译指令,比如#include, #define 等。规则主要如下:
    • 将所有的#define 删除,并且展开所有的宏定义
    • 处理所有的条件编译指令,比如#if, #ifdef, #elif, #else, #endif 条件编译
    • 处理#include 预编译指令,将被包含的文件插入到该预编译指令的位置,注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。
    • 删除所有的注释//. /* */
    • 添加行号和文件名标识,比如 #2 "hello.c" 2 ,以便编译时编译器产生调试用的行号的信息,及用于编译时产生编译错误或警告时能够显示行号。
    • 保留所有的#progma编译器指令,因为编译器需要使用他们

在这里插入图片描述

在这里插入图片描述

可以看到简单的几行代码,预编译之后变成了733行!!!

行号也互相匹配

在这里插入图片描述

经过预编译后的.i文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件也已经被插入到.i文件中,所以当我们无法判断宏定义是否正确或头文件包含是否正确时,可以查看预编译后的文件来确定问题

1.2 编译

编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。这个过程是整个程序构建的核心部分,也是最复杂的部分。

  • gcc -S hello.i -o hello.s 或者 gcc -S hello.c -o hello.s

在这里插入图片描述

  • 现在版本的GCC把预编译和编译两个步骤合并成一个步骤,使用一个叫做ccl 的程序来完成这两个步骤。/usr/lib/gcc/x86_64-linux-gnu/9/cc1 ,因此也可以直接调用ccl 来完成

    /usr/lib/gcc/x86_64-linux-gnu/9/cc1 hello.c

对于C语言来说,这个预编译和编译的程序是cc1 ,对于C++来说,有对应的程序叫做cc1plus ;Objective-C是cc1objfortranf771 ;Java是jc1

所以实际上gcc命令只是这些后台程序的包装,它会根据不同的参数要求去调用预编译程序cc1, 汇编器as,链接器ld

在这里插入图片描述

1.3 汇编

汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。所以汇编器的汇编过程相对于编译器来讲比较简单,没有复杂的语法,也没有语义,也不需要指令优化,按照对照表一一翻译就行。

汇编过程可以调用汇编器as 来完成

as hello.s -o hello.o

或者

gcc -c hello.s -o hello.o

或者直接使用gcc命令,从C源代码文件开始,经过预编译、编译和汇编直接输出目标文件

gcc -c hello.c -o hello.o

在这里插入图片描述

在这里插入图片描述

2.1.4 链接

  • 为什么汇编器不直接输出可执行文件而是输出一个目标文件呢?
  • 链接过程到底包含了什么内容?
  • 为什么要链接?

下面我们来看看怎样调用ld 才可以产生一个能够正常运行的HelloWorld 程序

ld -static /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginT.o-L/usr/lib/gcc/x86_64-linux-gnu/9  -L/usr/lib  -L/lib hello.o--start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/9crtend.o/usr/lib/crtn.o

如果把所有的路径都省略掉,那么就是

ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc -end-group crtend.o crtn.o

也就是说,我们需要将一大堆文件链接起来才可以看到a.out,也就是最终的可执行文件。

  • 那么crt1.o, crti.o .... 这些文件是什么?
  • 做什么用?
  • -lgcc -lgcc_eh -lc 这些是什么参数?为什么使用?
  • 为什么要将他们和hello.o 链接起来才能得到可执行文件?

这些需要后续的章节一点点看了,未完待续。。。

这篇关于1. 编译和链接----你真的了解HelloWord吗的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/904724

相关文章

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

速了解MySQL 数据库不同存储引擎

快速了解MySQL 数据库不同存储引擎 MySQL 提供了多种存储引擎,每种存储引擎都有其特定的特性和适用场景。了解这些存储引擎的特性,有助于在设计数据库时做出合理的选择。以下是 MySQL 中几种常用存储引擎的详细介绍。 1. InnoDB 特点: 事务支持:InnoDB 是一个支持 ACID(原子性、一致性、隔离性、持久性)事务的存储引擎。行级锁:使用行级锁来提高并发性,减少锁竞争

为什么现在很多人愿意选择做债务重组?债重组真的就这么好吗?

债务重组,起初作为面向优质企业客户的定制化大额融资策略,以其高效周期著称,一个月便显成效。然而,随着时代的车轮滚滚向前,它已悄然转变为负债累累、深陷网贷泥潭者的救赎之道。在此路径下,个人可先借助专业机构暂代月供,经一段时间养护征信之后,转向银行获取低成本贷款,用以替换高昂网贷,实现利息减负与成本优化的双重目标。 尽管债务重组的代价不菲,远超传统贷款成本,但其吸引力依旧强劲,背后逻辑深刻。其一

每日一练7:简写单词(含链接)

1.链接 简写单词_牛客题霸_牛客网 2.题目 3.代码1(错误经验) #include <iostream>#include <string>using namespace std;int main() {string s;string ret;int count = 0;while(cin >> s)for(auto a : s){if(count == 0){if( a <=

Windows环境利用VS2022编译 libvpx 源码教程

libvpx libvpx 是一个开源的视频编码库,由 WebM 项目开发和维护,专门用于 VP8 和 VP9 视频编码格式的编解码处理。它支持高质量的视频压缩,广泛应用于视频会议、在线教育、视频直播服务等多种场景中。libvpx 的特点包括跨平台兼容性、硬件加速支持以及灵活的接口设计,使其可以轻松集成到各种应用程序中。 libvpx 的安装和配置过程相对简单,用户可以从官方网站下载源代码

PHP: 深入了解一致性哈希

前言 随着memcache、redis以及其它一些内存K/V数据库的流行,一致性哈希也越来越被开发者所了解。因为这些内存K/V数据库大多不提供分布式支持(本文以redis为例),所以如果要提供多台redis server来提供服务的话,就需要解决如何将数据分散到redis server,并且在增减redis server时如何最大化的不令数据重新分布,这将是本文讨论的范畴。 取模算法 取模运

短链接算法原理

平时我们在上网的时候,印象最深刻的有一次是短链接的服务。例如:平时在微信上看一个网页的时候,如果我们选择在浏览器打开的时候,会看到很长的URL,我们分享的时候,会看到一个很短URL,这就是本次所说的短链接的应用之一。 长链接示例:https://mp.weixin.qq.com/s?__biz=MzAxNzMwOTQ0NA==&mid=2653355437&idx=1&sn=5901826ea63

Golang test编译使用

创建文件my_test.go package testsimport "testing"func TestMy(t *testing.T) {t.Log("TestMy")} 通常用法: $ go test -v -run TestMy my_test.go=== RUN TestMyTestMy: my_test.go:6: TestMy--- PASS: TestMy (0.