【C语言进阶(14)】程序的编译与链接

文章目录

  • 前言
  • Ⅰ 程序的翻译环境
    • 1. 编译的过程
    • 2. 链接的过程
  • Ⅱ 程序的执行环境
  • Ⅲ 预定义符号
  • Ⅳ 预处理指令 #define
    • 1. #define 定义标识符
    • 2. #define 定义宏
    • 3. #define 替换规则
  • Ⅴ 预处理操作符 # 和
    • 1. # 操作符
    • 2. ## 操作符
  • Ⅵ 宏和函数的对比
  • Ⅶ 预处理指令 #undef
  • Ⅷ 条件编译
    • 1. 单分支条件编译
    • 2. 多分支条件编译
    • 3. 判断符号是否被定义
  • Ⅸ 文件包含
    • 1. 本地文件包含
    • 2. 库文件包含

前言

在标准 C 语言的任何一种实现中,存在两个不同的环境:

  1. 翻译环境:将源代码翻译为可执行的机器指令。
  2. 执行环境:用于实际执行代码。

在这里插入图片描述

Ⅰ 程序的翻译环境

  • 当一个 .c 文件,最终要翻译成 .exe 文件时,需经过 翻译 + 运行两个环境。
  • 而翻译环境也需要经过两个过程:编译 + 链接

在这里插入图片描述

可执行程序的生成过程

在这里插入图片描述

  • 组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
  • 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
  • 链接器同时也会引入标准 C 函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。

不同环境下生成的目标文件

  • windows:生成的目标文件是 xxx.obj
  • Linux:生成的目标文件是 xxx.o

1. 编译的过程

编译分为 3 个过程:预编译 (预处理) → 编译 → 汇编

1. 预编译 (预处理)

在预处理完成后,会生成一个 xxx.i 文件。该文件会完成以下操作

  1. 注释的替换,将注释替换成空格。
  2. 头文件的包含,#include <>
  3. #define 符号的替换。

注:# 开头的都被称为预处理指令。所有的预处理指令都在预处理阶段就被处理掉。

在这里插入图片描述

2. 编译

C 语言代码翻译成汇编代码。执行过程如下

  1. 词法分析
  2. 语法分析
  3. 语义分析
  4. 符号汇总

3. 汇编

计算机无法看懂在编译阶段后翻译出的汇编代码,此时就需要使用汇编器将汇编代码翻译成二进制指令。生成了 .o 文件(目标文件)。

2. 链接的过程

  • 链接目标文件和链接库生成可执行程序 (二进制的程序)。
  1. 合并段表:将目标文件相同数据的段落进行合并。
  2. 符号表的合并和重定位

Ⅱ 程序的执行环境

程序运行的过程

  1. 程序载入内存。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
  2. 程序的执行开始。调用 main 函数。
  3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
  4. 终止程序。正常终止main函数;也有可能是意外终止。

Ⅲ 预定义符号

  • C 语言本身就预定义好的符号,这些符号可以被直接使用
符号名符号功能
__FILE__显示进行编译的源文件
__LINE__显示文件当前的行号
__DATE__显示文件被编译的日期
__TIME__显示文件被编译的时间
__STDC__如果编译器遵循 C 标准,其值为1,否则未定义
__FUNCTION__显示正在编译的是哪个函数

使用实例

  • 显示文件在编译过程中的文件、行号、日期、时间。

在这里插入图片描述

Ⅳ 预处理指令 #define

#define 的功能

  1. 定义标识符
  2. 定义宏

1. #define 定义标识符

语法形式

#define 名字 内容

举个例子

#define MAX 100 //MAX 就是标识符的名字,100 就标识符的内容

#define 的实现过程

  • #define 定义的标识符在预处理过程中,执行的是替换操作。不进行运算操作。

在这里插入图片描述

因为 #define 实现的是替换操作,所以在使用 #define 定义标识符时才不能出现分号。

#define MAX 100;	//如果出现分号printf("%d\n",MAX);		//将 MAX 替换之后的结果就成了 printf("%d\n",100;);

2. #define 定义宏

#define 允许把参数替换到文本中,这种实现通常称为宏或定义宏。

宏的声明方式

#define 名字(参数列表) 内容
  • 其中的参数列表是一个由逗号隔开的符号表,它们可能出现在内容中。

举个例子

  • 利用宏来实现乘法

在这里插入图片描述

#define MUL(x,y) x * y
//1.将 2 + 3 和 4 + 5 传给 x y 成了 #define MUL(2 + 3,4 + 5)
//2.然后将替换后的 x y 传进内容中 #define MUL(2 + 3,4 + 5) 2 + 3 * 4 + 5 int main()
{printf("%d\n", MUL(2 + 3, 4 + 5));//3.将 MUL(2 + 3,4 + 5) 替换成 2 + 3 * 4 + 5//结果就是 printf("%d\n",2 + 3 * 4 + 5);return 0;
}

定义宏时要舍得加括号

  • 因为宏实现的是替换值,所以宏在实现的过程中因为优先级的问题导致会很容易出现错误,因此在定义宏的时候,不要舍不得加括号。

在这里插入图片描述

3. #define 替换规则

在程序中扩展 #define 定义符号和宏时,需要涉及 3 个步骤:

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注意事项

  1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
  2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

Ⅴ 预处理操作符 # 和

1. # 操作符

功能

  • 将一个宏的参数以字符串的形式插入到字符串中。

举个例子

在这里插入图片描述

2. ## 操作符

功能

  • 将位于它两边的符号合成一个符号,它允许宏定义从分离的文本段创建标识符。

举个例子

在这里插入图片描述

Ⅵ 宏和函数的对比

宏的优点

  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
  2. 函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用 > 来比较的类型。宏是类型无关的

宏的缺点

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  2. 宏无法调试。
  3. 宏由于类型无关,也就不够严谨。
  4. 宏可能会带来运算符优先级的问题,导致程序容易出现错。

宏和函数的对比

属性#define 定义宏函数优势方
代码长度每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码函数
执行速度更快存在函数䣌调用和返回的额外开销,相对慢一些
操作符优先级宏参数的求值实在所有周围表达时的上下文环境里,除非加上括号,非则邻近操作符的优先级可能会产生不可预料的后果,建议宏在书写时加多括号函数参数只在函数调用的时候求值一次,将它的结果值传给函数,表达式的求值结果更容易预测函数
带有副作用的参数参数可能被替换到宏体中的多个位置,所以带有副作用的参数值可能会产生不可预料的结果函数参数只在传参的时候求值一次,结果更容易控制函数
参数类型宏的参数有与类型无关,只要对参数的操作时合法的,它就可以用于任何参数类型函数的参数类型与类型有关,如果参数的类型不同,就需要不同的函数,即使它们执行的任务时不同的
调试宏不方便调试函数可以逐语句调试函数
递归宏不能递归函数可以递归函数

如果选择函数 / 宏

  • 如果逻辑比较简单,可以使用宏来实现。
  • 如果计算逻辑比较复杂,就要使用函数。

Ⅶ 预处理指令 #undef

  • 移除一个宏定义

举个例子

在这里插入图片描述

Ⅷ 条件编译

  • 在编译一个程序的时候将一条语句 (一组语句) 编译或放弃。

条件编译

  • 满足编译条件才允许执行编译。

以下为常见的条件编译指令

1. 单分支条件编译

语法格式

#if 常量表达式//代码
#endif
  • 表达式结果如果为真,中间的代码参与编译,反之不参与编译

举个例子

在这里插入图片描述

2. 多分支条件编译

语法格式

#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif

举个例子

在这里插入图片描述

3. 判断符号是否被定义

符号已定义则参与编译

#if defined(symbol)
#ifdef symbol

符号未定义则参与编译

#if !defined(symbol)
#ifndef symbol

举个例子

int main()
{
//以下为定义了符号才会去编译的条件编译指令
#if defined(M)	printf("world hello!\n");
#endif
#ifdef Mprintf("world hello!\n");
#endif//以下为未定义符号才回去编译的条件编译指令
#if !defined(M)	//未定义 M 才编译代码printf("hello world!\n");
#endif
#ifndef M printf("hello world!\n");
#endif return 0;
}

在这里插入图片描述

Ⅸ 文件包含

1. 本地文件包含

包含的是自己的 .h 文件

语法格式

#include "xxx.h"

查找策略

  • 现在源文件所在目录下查找,如果该头文件未被找到,编译器则去标准位置查找头文件。
  • 如果找不到则提示编译错误。

举个例子

#include "test.h"
//寻找 test.h 文件
//1.先在当前的 .c 文件所在的文件夹下寻找
//2.如果没有找到,则取标注库文件夹下寻找
//3.如果在这两个地方都找不到则报错

在这里插入图片描述

2. 库文件包含

包含的是标准库的头文件

语法格式

#include <xxx.h>
  • 查找头文件直接去标准路径下去查找,如果找不到就提示编译错误.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/164601.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【力扣每日一题】2023.10.19 同积元组

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目比较简洁,给我们一个元素各不相同的数组&#xff0c;要我们找出该数组里能够组成 a*bc*d 的组合数目。 比较直观的做法是我们直接暴…

【STM32】--PZ6860L,STM32F4,ARM3.0开发板

一、ARM3.0开发板详细介绍 1.开发板整体介绍 &#xff08;1&#xff09;各种外设和主板原理图 &#xff08;2&#xff09;主板供电部分5V和3.3V兼容设计 注意跳线帽 2.STM32核心板介绍 3.核心板原理图 STM32和51的IO对应关系 下载电路 二、ARM3.0开发板ISP下载原理分析 1.I…

分布式系统部署Redis

文章目录 一、单点问题二、主从模式概念配置主从结构查看主从节点断开从属关系拓扑结构主从复制原理replication复制offset偏移量 全量复制和部分复制全量复制部分复制 实时复制redis主节点无法重启 三、主从哨兵模式哨兵概念监控程序人工恢复自动恢复为什么是哨兵集合使用dock…

一文拿下HTTP

HTTP HTTP协议 是应用层使用最广泛的协议之一&#xff0c;从浏览器获取到网页&#xff0c;就是基于http 浏览器和服务器之间的交互桥梁 基于传输层的TCP协议来实现的&#xff0c;是一种无状态的应用层协议 为啥是无状态的呢 简化服务器端的处理逻辑&#xff1a;HTTP是无状态…

如何用记事本制作一个简陋的小网页(3)——注册信息表

目录 前提须知&#xff1a; 一、表格建立之前&#xff1a; 二、表格的建立&#xff1a; 三、信息表的内容填充&#xff1a; 1.昵称 和 电话 &#xff1a; 2.密码&#xff1a; 3.性别&#xff1a; 4. 爱好&#xff1a; 5.民族&#xff1a; 6. 出生日期&#xff1a; 7.…

Android apkanalyzer简介

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、用法3.1 使用 Android Studio3.1.1…

Python词语转拼音

使用python写的图形汉语词语转拼音小工具 1)安装库 pip install flet 2)代码 # 声母列表 initial_consonant_list [b, p, m, f, d, t, n, l, g, k, h, j, q, x, zh, ch, sh, r,z, c, s, y, w] # 韵母列表 list_of_vowels [a, o, e, i, u, , ai, ei, ui, ao, ou, iu, ie, e…

SpringCloudSleuth异步线程支持和传递

场景 在使用Sleuth做链路跟踪时&#xff0c;默认情况下异步线程会断链&#xff0c;需要进行代码调整支持。 调整内容 方式一 使用Async实现异步线程 开启异步线程池 EnableAsync SpringBootApplication public class LizzApplication {public static void main(String[] a…

织造业的数字安全守护者:深入了解迅软DSE数据加密

客户简要介绍 某织造企业成立于2004年&#xff0c;工厂位于苏州平望&#xff0c;公司目前拥有先进纺织设备330台套和日本瑞士等前道配套设备&#xff0c;公司占地33亩、具有现代化标准厂房办公楼等3万平米。 某织造企业面料、功能性面料、新材料面料的生产商&#xff0c;公司坚…

教程更新 | 持续开源 RK3568驱动指南-驱动基础进阶篇

《iTOP-RK3568开发板驱动开发指南》手册文档更新&#xff0c;手册内容对应视频教程&#xff0c;后续资料会不断更新&#xff0c;不断完善&#xff0c;帮助用户快速入门&#xff0c;大大提升研发速度。 ✦ 第一篇 驱动基础 第1章 前言 第2章 你好&#xff01;内核源码 第3章 …

Mysql第二篇---InnoDB数据存储结构

Mysql第二篇—InnoDB数据存储结构 数据库的存储结构: 页 索引结构给我们提供了高效的索引方式, 不过索引信息以及数据记录都是保存在文件上的(innodb的ibd文件, MyISAM的MyI和MyD文件), 确切的说是存储在页结构中. 另一方面, 索引是在存储引擎中实现的, MySQL服务器上的存储引…

工业自动化编程与数字图像处理技术

工业自动化编程与数字图像处理技术 编程是计算机领域的基础技能&#xff0c;对于从事软件开发和工程的人来说至关重要。在工业自动化领域&#xff0c;C/C仍然是主流的编程语言&#xff0c;特别是用于工业界面(GUI)编程。工业界面是供车间操作员使用的&#xff0c;使用诸如Hal…

二叉树,堆排序及TopK问题

要讲二叉树的概念&#xff0c;就要先讲树的概念。 树是什么呢&#xff1f; 树其实是一种储存数据的结构&#xff0c;因为他的结构倒过来和生活中的树很相似所以才被称之为树。 这是一颗多叉树&#xff0c;从最顶端的节点可以找到下边的几个节点&#xff0c;下边的节点又可以找…

【力扣刷题】回文链表、环形链表、合并两个有序链表

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 刷题篇 一、回文链表1.1 题目描述1.2 思路分…

1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计

文章目录 一、HttpContext模块二、HttpServer模块三、HttpContext模块实现思想&#xff08;一&#xff09;功能&#xff08;二&#xff09;意义&#xff08;三&#xff09;接口 四、HttpServer模块实现思想&#xff08;一&#xff09;功能&#xff08;二&#xff09;意义&#…

经典网络模型

Alexnet VGG VGG的启示 VGGNet采用了多次堆叠3x3的卷积核&#xff0c;这样做的目的是减少参数的数量。 例如&#xff0c;2个3x3的卷积核效果相当于1个5x5的卷积核效果&#xff0c;因为它们的感受野&#xff08;输入图像上映射区域的大小&#xff09;相同。但2个3x3卷积核的参数…

1024程序员狂欢节 | IT前沿技术、人工智能、数据挖掘、网络空间安全技术

一年一度的1024程序员狂欢节又到啦&#xff01;成为更卓越的自己&#xff0c;坚持阅读和学习&#xff0c;别给自己留遗憾&#xff0c;行动起来吧&#xff01; 那么&#xff0c;都有哪些好书值得入手呢&#xff1f;小编为大家整理了前沿技术、人工智能、集成电路科学与芯片技术、…

YOLOv8改进实战 | 更换主干网络Backbone之轻量化模型Efficientvit

前言 轻量化网络设计是一种针对移动设备等资源受限环境的深度学习模型设计方法。下面是一些常见的轻量化网络设计方法: 网络剪枝:移除神经网络中冗余的连接和参数,以达到模型压缩和加速的目的。分组卷积:将卷积操作分解为若干个较小的卷积操作,并将它们分别作用于输入的不…

PHP的学习入门建议

学习入门PHP的步骤如下&#xff1a; 确定学习PHP的目的和需求&#xff0c;例如是为了开发网站还是为了与数据库交互等。学习PHP的基础语法和程序结构&#xff0c;包括变量、数据类型、循环、条件等。学习PHP的面向对象编程&#xff08;OOP&#xff09;概念和技术。学习与MySQL…

BIO、NIO、IO多路复用模型详细介绍Java NIO 网络编程

文章目录 前言基本概念BIO过程NIO过程IO多路复用过程Java NIO编程Java NIO 核心概念Java NIO 示例 总结 前言 上文介绍了网络编程的基础知识&#xff0c;并基于 Java 编写了 BIO 的网络编程。我们知道 BIO 模型是存在巨大问题的&#xff0c;比如 C10K 问题&#xff0c;其本质就…