【c语言进阶】预处理 以你之姓@ 2024-04-26 11:08 10阅读 0赞 ![在这里插入图片描述][6ac7346dc5944908b5dd227e55fab217.gif_pic_center] > 🚀write in front🚀 > 📜所属专栏:> [c语言学习][c] > 🛰️博客主页:[睿睿的博客主页][Link 1] > 🛰️代码仓库:🎉[VS2022\_C语言仓库][VS2022_C] > 🎡您的点赞、关注、收藏、评论,是对我最大的激励和支持!!! > **关注我,关注我,关注我**,你们将会看到更多的优质内容!! ![在这里插入图片描述][85d8f838a7524c8bb8bed80b7ac059c1.gif_pic_center] #### 文章目录 #### * 前言: * 一.翻译环境: * * 1.翻译环境总括: * ①.编译: * * 预编译(预处理): * 编译: * 汇编 * * * 符号表: * ②.链接: * 二.运行环境: * 二、预处理详解: * * 1.预定义符号: * 2.\#define: * * * ①. \#define 定义标识符: * ②.\#define 定义宏: * ③. \#define 替换规则: * 总结: ## 前言: ## 在研究程序的编译与链接细节之前,我们首先要了解我们程序的翻译以及执行环境。对于一个程序的运行,第一步是翻译,第二步是执行。 > 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。 > 第2种是执行环境,它用于实际执行代码。 ## 一.翻译环境: ## ### 1.翻译环境总括: ### 在翻译环境中执行的操作,简单来说可以分为**三个步骤**: * 组成一个程序的**每个源文件**通过编译**过程分别转换成**目标代码(.obj)。 * **每个目标文件由链接器捆绑在一起**,形成一个单一而完整的可执行程序。 * 链接器**同时**也会引入**链接库(标准C函数库)中任何被该程序所用到的函数**,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。 ![在这里插入图片描述][b18a85de90e347cfa22a864d4586f105.png] 每次编译运行完成以后,我们都可以在所在文件里发现`.exe`文件和`.obj`文件 ![在这里插入图片描述][0ea15d385e274d3bb0c69ef3c63595e4.png] 翻译阶段可分为两个阶段,即**编译**与**链接**: ![在这里插入图片描述][c8b075073888471e8ed83bfa746daf9a.png] ### ①.编译: ### #### 预编译(预处理): #### 首先我们先一段普通的代码: ![在这里插入图片描述][bb6831121bcc4ade8e5e27445ddb128b.png] 然后用gcc做以下操作: * 预处理 选项 gcc -E test.c -o test.i * 预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中 就会发现下面的情况: ![在这里插入图片描述][5226658ce2cd4363bc094b4e055243ec.png] **预处理的作用:** > 头文件的包含、**\#define 定义符号的替换**、注释的删除,文本操作。 #### 编译: #### 用gcc选项 gcc -S test.c 编译完成之后就停下来,结果保存在test.s中,我们便会发现汇编代码 **编译的作用**: > 编译阶段就是将 C 语言的代码**翻译成汇编代码**,其中包含了语法分析、词法分析、语义分析、**符号汇总**(后面会重点讲到)等。 #### 汇编 #### 用gcc选项gcc -c test.c 汇编完成之后就停下来,结果保存在test.o中,出现乱码(2进制码以文本形式无法看懂) **汇编的作用:** > 将汇编代码**翻译成二进制的指令**(存放到目标文件)符号汇总后:**形成符号表** ###### 符号表: ###### 一个类似于这样的表格: ![在这里插入图片描述][91e50df5823e4710baa28f79f53d37dd.png] 在这里,我们的符号表里是该程序里所有的函数名和每个函数所在的的地址,(**对于使用的其他文件的函数,只要该函数的申明存在,地址都先设为0x00**,在后面**链接阶段在修改该地址**) ### ②.链接: ### 链接的作用是: > 合并段表 > 合并符号表 > **重定位符号表** 这里就是对所有文件的一个汇总,对于一个程序里,一个源文件使用了另一个源文件的函数,通过链接,就会将函数的地址汇总,并且改掉地址为0的函数。 ## 二.运行环境: ## 在这个环境下,我们的程序就真正进入了运行阶段。我们程序的执行过程可以简述为下面**四个步骤**: * ①. **程序载入内存中**:在有操作系统的环境中该步骤通常由操作系统完成;而在独立环境中则必须由我们自己手动完成;独立环境中的程序也有可能通过执行可执行代码置入只读内存来完成。 * ②. **调用 main 函数**:程序正式开始执行。 * ③. **顺序执行程序代码**:在这个阶段中,我们的程序会使用一个运行时堆栈来存储函数的局部变量和返回地址。同时程序也可以使用静态内存来存储变量,并且这些存储于静态变量中的变量在整个程序的执行过程中将始终保留它们的值。 * ④. **终止程序**:一般情况下会正常终止 main 函数,但我们的程序也有可能会意外终止。 ## 二、预处理详解: ## ### 1.预定义符号: ### __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义 这些预定义符号都是语言内置的。 举个栗子: 通过: printf("time:%s %s\n", __DATE__, __TIME__); 你就可以知道编译这段代码的当前时间 ### 2.\#define: ### \#define 的用处非常多,就比如我们常用的定义标识符常量、定义宏等等,而在这个过程中,也有一些细节值得我们去注意。 ##### ①. \#define 定义标识符: ##### 我们常常会使用 \#define 去定义一些标识符来方便我们的代码书写: 语法: #define name stuff 定义了之后,只有我们看到 name 这个东西,就给他一比一替换成 stuff这个东西就行即可 举个栗子: #define MAX 1000 #define reg register //为 register这个关键字,创建一个简短的名字 #define do_forever for(;;) //用更形象的符号来替换一种实现 #define CASE break;case //在写case语句的时候自动把 break写上。 // 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。 #define DEBUG_PRINT printf("file:%s\tline:%d\t \ date:%s\ttime:%s\n",\ __FILE__,__LINE__,\ __DATE__,__TIME__) **注意:** 在 \#define 的最后,最好不要加上分号 “ ; ” 加上可能会出现两个;号,可能会导致语法错误: ##### ②.\#define 定义宏: ##### 在\#define 的机制中,包括了一个规定,这个规定允许把参数替换到文本中,这种实现通常称为定义宏(或简称为宏)。 宏的申明方式为: > \#define name( parament-list ) stuff > 其中的 parament-list 是一个由**逗号隔开的符号列表**,且可能出现在 stuff 中 **注意:** 注意: **参数列表的左括号必须与name紧邻**。 如果两者之间有任何空白存在,**参数列表就会被解释为stuff的一部分** **易错点:** 初学者在学习宏时很容易出现以下问题: 举个栗子: #define SQUARE( x ) x * x int main { int a = 5; printf("%d\n" ,SQUARE( a + 1) ); } 乍一看,你可能觉得这段代码将打印36这个值。 事实上,它将打印11. 为什么? 我们前面说过了,**标识符的替换是一比一替换**! 替换文本时,参数x被替换成a + 1,所以这条语句实际上变成了: printf (“%d\\n”,a + 1 \* a + 1 ),所以结果是11。 所以我们可以对这个宏进行修改: #define SQUARE(x) (x) * (x) 由此可见,我们在进行宏定义的时候,**一定要舍得加括号**!避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。 ##### ③. \#define 替换规则: ##### \#define 在进行符号替换时,**遵循以下规则**: 1. 调用宏时首先对参数进行检查,检查是否包含任何由 \#define 定义的符号。如果有,它们将**首先被替换**。 2. 替换文本随后被插入到程序中原来文本的位置。**对于宏,参数名被他们的值所替换。** 3. 最后,**再次对结果文件进行扫描**,看看它**是否包含任何由 \#define 定义**的符号。如果有,就重复上述处理过程。 **注意:** 1. 宏参数和\#define 定义中可以出现其他\#define定义的符号。但是**对于宏,不能出现递归**。 2. 当预处理器搜索\#define定义的符号的时候,**字符串常量的内容并不被搜索**。 ## 总结: ## 这就是程序环境和预处理的相关知识。当然,这里对于宏的讲解是往往不够的!宏的相关知识将在下一篇文章中进行详细讲解!更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正! > **专栏订阅:** > [每日一题][Link 2] > [c语言学习][c] > [算法][Link 3] > [智力题][Link 4] > 更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正! ![在这里插入图片描述][55270e2552774a17a711aa978da84193.gif_pic_center] [6ac7346dc5944908b5dd227e55fab217.gif_pic_center]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/57a923cbd82444c0975c122faa430381.gif [c]: https://blog.csdn.net/qq_74310471/category_12157207.html?spm=1001.2014.3001.5482 [Link 1]: https://blog.csdn.net/qq_74310471?spm=1000.2115.3001.5343 [VS2022_C]: https://gitee.com/zhangxuruihhhh/c-language-learning [85d8f838a7524c8bb8bed80b7ac059c1.gif_pic_center]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/e51879bfd020498498d034f6e286f293.gif [b18a85de90e347cfa22a864d4586f105.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/3dfdb127ddf547c69a1d43f4ee4cfc7c.png [0ea15d385e274d3bb0c69ef3c63595e4.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/27bc18f194fc473ab45ba0c52f21b316.png [c8b075073888471e8ed83bfa746daf9a.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/f5bc465d7f9a4c1683fb5b83652d8698.png [bb6831121bcc4ade8e5e27445ddb128b.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/436566e2797b4ccd96419c35bf07b7d8.png [5226658ce2cd4363bc094b4e055243ec.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/8a37618526eb4aa3b3ec0a1620887b7b.png [91e50df5823e4710baa28f79f53d37dd.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/ef168b58a26641939ab6e71ae138bda1.png [Link 2]: https://blog.csdn.net/qq_74310471/category_12166401.html?spm=1001.2014.3001.5482 [Link 3]: https://blog.csdn.net/qq_74310471/category_12144452.html?spm=1001.2014.3001.5482 [Link 4]: https://blog.csdn.net/qq_74310471/category_12166402.html [55270e2552774a17a711aa978da84193.gif_pic_center]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/e889205e15994e31a890bd2676496aa4.gif
相关 【C语言进阶】指针进阶(三) 指针进阶(三) 9.指针和数组笔试题解析 10.指针笔试题 9.指针和数组笔试题解析 数组名的理解 数组名是数组首元素的地址 但 朴灿烈づ我的快乐病毒、/ 2024年03月27日 18:31/ 0 赞/ 27 阅读
相关 【C语言】预处理 文章目录 一、程序环境 1、程序的翻译环境和执行环境 2、编译+链接 1.翻译环境 2.翻译阶段 Love The Way You Lie/ 2024年03月17日 21:32/ 0 赞/ 32 阅读
相关 C语言-指针进阶 目录 一、字符指针 二、指针数组 三、数组指针 3.1 数组指针的定义 3.2 数组名和&数组名 3.3 数组指针的使用 四、数组参数、指针参数 4.1 拼搏现实的明天。/ 2024年03月17日 21:28/ 0 赞/ 20 阅读
相关 C语言进阶——指针进阶 C语言指针进阶 C语言初阶——指针 一、字符指针 二、数组指针 2.1 指针数组 2.2 数组指针 2.3 &数 我不是女神ヾ/ 2023年01月06日 11:57/ 0 赞/ 138 阅读
相关 C语言预处理 C语言预处理 预处理 预定义符号 2.宏定义: 自定义一个宏(要符合标识符的命名规则),用于表示任意数据或者表达式. 3.文 秒速五厘米/ 2022年11月25日 13:00/ 0 赞/ 114 阅读
相关 C语言预处理 由源码到可执行的程序 源码-》(编译)-》elf可执行程序 源码-》(编译)-》目标文件.o-》(链接)-》elf可执行程序 源码-》(编译)-》汇编文件.s-》( 女爷i/ 2022年06月02日 07:00/ 0 赞/ 176 阅读
还没有评论,来说两句吧...