【C语言】进阶——程序编译

 

目录

一:🔒程序环境

程序的翻译环境和执行环境

💡1.1翻译环境  

预编译阶段:

编译阶段:

汇编阶段:

链接阶段:

💡1.2运行环境 

 二:🔒预处理详解

💡2.1预处理符号

💡2.2#define 

#define定义标识符

#define定义宏

#define替换规则 

 🔒#和## 

💡#的作用

💡##的作用

带副作用的宏参数 

三:宏与函数的对比 

💡命名约定 

 四:🔒条件编译

五:🔒文件包含

💡头文件的包含方式 

💡避免头文件被重复引用 


 

 

一:🔒程序环境

程序的翻译环境和执行环境

在ANSIC任何一种实现中,存在两种不同的环境

        1.翻译环境,在这个环境下源代码被转换成可执行的机器指令

        2.执行环境,用于实际代码运行

 

💡1.1翻译环境  

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

 

预编译阶段:

1.头文件包含

#include 预处理指令

2.define定义的符号替换

#define 预处理指令

3.注释删除

以上这些都是文本操作

编译阶段:

把c语言代码翻译成了汇编代码

1、语法分析

2、词法分析

3、语义分析

4、符号汇总 

汇编阶段:

把汇编指令翻译成了二进制的指令

形成符号表,这样就能够找到源文件外部的符号(只能汇总全局符号)

链接阶段:

1、合并段表

2、符号表的合并和重定位

💡1.2运行环境 

程序执行的过程:

1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序 的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。

2. 程序的执行便开始。接着便调用main函数。

3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。

4. 终止程序。正常终止main函数;也有可能是意外终止。

 二:🔒预处理详解

💡2.1预处理符号

__FILE__      //进行编译的源文件
__LINE__     //文件当前的行号
__DATE__    //文件被编译的日期
__TIME__    //文件被编译的时间
__STDC__    //如果编译器遵循ANSI C,其值为1,否则未定义

这些预定义符号都是语言内置的。 

int main()
{printf("file:%s line:%d\n", __FILE__, __LINE__);return 0;
}

💡2.2#define 

#define定义标识符

#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 MAX 100;
int main()
{int max = 0;if (1)max = MAX;    //errorelsemax = 0;return 0;
}

这里宏定义会直接替换,将 100; 替换到 MAX位置

 if (1)max = 100;;elsemax = 0;

#define定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。 

#define name(parament-list) stuff
//name表示名字
//parament-list 是以逗号隔开的参数
//宏的具体内容例子
//#define max(a,b) a+b

注意:
参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被认为是要替换的部分,参数列表就会被解释为stuff的一部分

同样也要注意因为宏是直接进行文本替换,然后才在程序中发生计算,所以如果不按照标准规定写宏,可能会产生bug 

#define SQUARE(x) x*xint c=5SQUARE(5+1);//我们预期这里的内容是36,但是最终结果是11,是因为实际计算的是//5+1*5+1==11

所以在写的时候我们应该尽可能带上括号,防止因为优先级的问题出现bug

#define SQUARE(x) ((x)*(x))

#define替换规则 

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

1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换。

2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。

3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。.

注意:

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

 🔒#和## 

💡#的作用

 如何把参数插入到字符串中?

int main()
{printf("who say!!!\n");printf("who" " say!!!\n");return 0;
}

 

字符串是具备自动连接的特点。

 而define定义的符号,在"X" 里面不会被识别,我们可以用 #X 解决此问题

#define PRINT(n, format)   printf("the value of "#n" is "format"\n", n)
int main()
{int a = 10;PRINT(a, "%d");//printf("the value of ""a"" is ""%d""\n", a);//printf("the value of a is %d\n", a);int b = 20;PRINT(b, "%d");return 0;
}

💡##的作用

##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符。

#define CAT(v, n) v##n
int main()
{int value10 = 100;printf("%d\n", CAT(value, 10));printf("%d\n", value10);return 0;
}

注:
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。 

带副作用的宏参数 

简单来讲就是宏在执行的过程中,参数自身的值会发生变化,这个就叫做带副作用的宏的参数

#define MAX(a,b) ((a)>(b)?(a):(b))int main()
{int x=5,y=8;int c=MAX(x++,y++);printf("%d ",c);return 0;
}
//这里输出的是多少,9嘛?
//首先带入  ((5++)>(8++)?(a++):(b++)) 首先是进行ab大小的比较,在这里比较之后,
//a跟b跟别变成了6和9,然后执行后面的b++,最终的结果应该a=6 c=9 b=10

要避免写出这样的代码,宏是无法调试的

三:宏与函数的对比 

 宏通常被应用于执行简单的运算。
比如在两个数中找出较大的一个。

宏定义和函数的比较 

属性#define宏定义函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常 小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每 次使用这个函数时,都调用那个、地方的同一份代码
执行速度  相对更快(简单的程序)存在函数的调用和返回的额外开 销,所以相对慢一些

操作符

优先级

宏参数的求值是在所有周围表达式的上下文环境里, 除非加上括号,否则邻近操作符的优先级可能会产生 不可预料的后果,所以建议宏在书写的时候多些括 号。函数参数只在函数调用的时候求 值一次,它的结果值传递给函 数。表达式的求值结果更容易预 测。
带 有 副 作 用 的 参 数惨数可能被替换到宏体中的多个位置,所以带有副作 用的参数求值可能会产生不可预料的结果。函数参数只在传参的时候求值一 次,结果更容易控制。
参数类型 宏的参数与类型无关,只要对参数的操作是合法的, 它就可以使用于任何参数类型。函数的参数是与类型有关的,如 果参数的类型不同,就需要不同 的函数,即使他们执行的任务是 不同的。
调试无法调试函数可以逐语句调试的
递归无法递归可以递归

1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。
所以宏比函数在程序的规模和速度方面更胜一筹。(相对简单定义)
2.更为重要的是函数的参数必须声明为特定的类型。
所以函数只能在类型合适的表达式上使用。

反之这个宏怎可以适用于整形、长整型、浮点型等可以
用于来比较的类型。
宏是类型无关的。

💡命名约定 

 一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。
那我们平时的一个习惯是:

把宏名全部大写
函数名不要全部大写(一般单词首字母大写)

 四:🔒条件编译

条件编译顾名思义就是满足条件才进行读取

1.
#if 常量表达式//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__//..
#endif
2.多个分支的条件编译
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif

五:🔒文件包含

#include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方 一样。

这种替换的方式很简单:

预处理器先删除这条指令,并用包含文件的内容替换。

这样一个源文件被包含10次,那就实际被编译10次。

💡头文件的包含方式 

本地文件

#include"test.h"

先从源文件所在目录进行查找头文件,然后再到标准函数库头文件所在目录下查找

#include<stdio.h>

直接从标准函数库头文件所在目录下查找

总的来说就是""的引用方式查找范围更广

但是!!!

本地文件还是按照 #include"test.h''  的方式

库文件按照 #include<test.h>的方式  

否则:查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

💡避免头文件被重复引用 

防止多次头文件频繁引用

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif   //__TEST_H__

或者

#pragma once

以上便是我对【C语言】程序编译的介绍,文中不足之处,还望得到指点得以改善。感谢!!! 

 

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

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

相关文章

文件夹图片相似图片检测并删除相似图片

项目开源地址 pip install imagededupgit clone https://github.com/idealo/imagededup.git cd imagededup pip install "cython>0.29" python setup.py installQuick Start from imagededup.methods import PHash phasher PHash()# Generate encodings for all…

分类预测 | Matlab实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆神经网络的数据多输入分类预测

分类预测 | Matlab实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆神经网络的数据多输入分类预测 目录 分类预测 | Matlab实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆神经网络的数据多输入分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现WOA-BiLSTM鲸鱼算法…

oracle-AWR报告生成方法

AWR报告生成方法 1. 以oracle用户登陆服务器 2. 进入到要保存awr报告的目录 3. 以sysdba身份连接数据库 sqlplus / as sysdba4. 执行生成AWR报告命令 ?/rdbms/admin/awrrpt.sql5. 选择AWR报告的文件格式 6. 选择生成多少天的AWR报告 7. 选择报告的快照起始和结束ID 8. 输入生…

MongoDB深度学习

MongoDB的简介 什么是MongoDB? MongoDB是一个基于分布式文件存储的数据库&#xff0c;由C语言编写。MongoDB是一个介于关系数据库和非关系数据库之间的产品&#xff0c;是非关系数据库当中功能最丰富&#xff0c;最像关系数据库的&#xff0c;它支持的数据结构非常松散&…

面试算法31:最近最少使用缓存

题目 请设计实现一个最近最少使用&#xff08;Least Recently Used&#xff0c;LRU&#xff09;缓存&#xff0c;要求如下两个操作的时间复杂度都是O&#xff08;1&#xff09;。 get&#xff08;key&#xff09;&#xff1a;如果缓存中存在键key&#xff0c;则返回它对应的值…

【二维差分】ICPC南京 A

https://codeforces.com/gym/104128/problem/A 题意 思路 二维差分经典模型 考虑如果没有洞那么经历操作之后会剩下什么样子的袋鼠。发现上下左右移动可以看成是边界在移动&#xff0c;边界一直保持一个原初的矩形形状&#xff0c;而且上下移动和左右移动没有任何关系。一旦…

【LeetCode刷题(数据结构与算法)】:将二叉搜索树转化为排序的双向链表

将一个 二叉搜索树 就地转化为一个 已排序的双向循环链表 对于双向循环列表&#xff0c;你可以将左右孩子指针作为双向循环链表的前驱和后继指针&#xff0c;第一个节点的前驱是最后一个节点&#xff0c;最后一个节点的后继是第一个节点 特别地&#xff0c;我们希望可以 就地 完…

Elasticsearch 8.9 Master节点处理请求源码

大家看可以看ElasticSearch源码&#xff1a;Rest请求与Master节点处理流程&#xff08;1&#xff09; 这个图非常好&#xff0c;下午的讲解代码在各个类和方法之间流转&#xff0c;都体现这个图上 一、Master节点处理请求的逻辑1、节点(数据节点)要和主节点进行通讯&#xff0…

【数据结构】线性表(四)双向链表的各种操作(插入、删除、查找、修改、遍历打印)

目录 线性表的定义及其基本操作&#xff08;顺序表插入、删除、查找、修改&#xff09; 四、线性表的链接存储结构 1. 单链表 2. 循环链表 3. 双向链表 a. 双向链表节点结构 b. 创建一个新的节点 c. 在链表末尾插入节点 d. 在指定位置插入节点 e. 删除指定位置的节点…

Juniper防火墙SSG-140 session 过高问题

1.SSG-140性能参数 2.问题截图 3.解决方法 &#xff08;1&#xff09;通过telnet 或 consol的方法登录到防火墙&#xff1b; &#xff08;2&#xff09;使用get session 查看总的session会话数&#xff0c;如果大于300 一般属于不正常情况 &#xff08;3&#xff09;使用get…

H5随机短视频滑动版带打赏源码,可封装APP软件或嵌入式观看

H5随机短视频滑动版带打赏源码&#xff0c;可封装APP软件或嵌入式观看&#xff0c;网站引流必备源码&#xff01; 数据来源抖音和快手官方短视频链接&#xff0c;无任何违规内容&#xff01;可自行添加广告等等&#xff01; 手机端完美支持滑动屏幕观看&#xff08;向上或向右…

思维模型 上瘾模型(hook model)

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。你到底是怎么上瘾&#xff08;游戏/抖音&#xff09;的&#xff1f;我们该如何“积极的上瘾”&#xff1f;让我们来一切揭晓这背后的秘密。 1 上瘾模型的应用 1.1上瘾模型的积极应用 1 学…

RT-Thread学习笔记(三):线程管理

线程管理 线程管理相关概念什么是时间片轮转调度器锁线程运行机制线程的五种状态 动态和静态创建线程区别动态和静态创建线程优缺点RT-Thread动态线程管理函数动态创建线程动态删除线程 RT-Thread静态线程管理函数静态创建线程 线程其他操作线程启动线程延时获得当前执行的线程…

四、基本组件

1. Designer设计师 Designer程序是Qt官方推出的专为设计人员使用的UI设计工具&#xff0c;程序员可以使用此工具大幅降低UI设计的代码量。 Designer设计文件的格式是.ui&#xff0c;需要配合同名的头文件与源文件使用。.ui文件通常被称为界面文件&#xff0c;其内部是xml语法的…

微机原理:汇编语言语句类型与格式

文章目录 壹、语句类型1、语句分类2、常用伪代码和运算符2.1数据定义伪指令2.1.1字节定义伪指令DB&#xff08;8位&#xff09;2.1.2字定义伪指令DW&#xff08;16位&#xff09;2.1.3双字节伪指令DD2.1.4 多字节定义DF/DQ/DT&#xff08;了解&#xff09; 2.2 常用运算符2.2.1…

[java进阶]——异常详解,try catch捕获异常,抛出异常

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 一、异常的体系结构 二、处理异常的本质 三、异常处理的三种方式 3.1虚拟机jvm处理(默认) 3.2 try catch捕获异常 3.3抛出异常 3.4finally关键字 四、自定义异常 五、总结 一、异常的体系结构 分析&#…

【LeetCode】145. 二叉树的后序遍历 [ 左子树 右子树 根结点]

题目链接 文章目录 Python3方法一&#xff1a; 递归 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法二&#xff1a; 迭代 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法三&#xff1a; Morris ⟮ O ( n ) 、 O ( 1 ) ⟯ \lgroup O(n)、O(1) \rgroup ⟮O(n)、O(1)⟯写…

MySQL表操作—存储

建表&#xff1a; mysql> create table sch( -> id int primary key, -> name varchar(50) not null, -> glass varchar(50) not null -> ); Query OK, 0 rows affected (0.01 sec) 插入数据&#xff1a; mysql> insert into sch (id,name,…

c语言练习94:分割链表

分割链表 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 示例 1&#xff1a; 输入&#xff1a;head [1,4,3,2,5,2], x…

短视频矩阵系统源码(搭建)

短视频矩阵源码的开发路径分享如下&#xff1a; 1、首先&#xff0c;确定项目需求和功能&#xff0c;包括用户上传、编辑、播放等。 2、其次&#xff0c;搭建开发环境&#xff0c;选择合适的开发工具和框架。 3、然后&#xff0c;进行项目架构设计和数据库设计&#xff0c;确…