【C语言】文件的编译链接和预处理

news2024/11/24 13:28:46

文件的编译链接和预处理

  • 程序的翻译环境和执行环境
    • 翻译环境
      • 预处理(预编译)过程
      • 编译过程
      • 汇编过程
      • 链接过程
    • 运行环境
  • 预处理详解
    • 预处理符号
    • 预处理指令
    • #define
      • #define定义标识符
      • #define定义宏
      • #define替换规则
    • #与##
      • #的使用
      • ##的使用
    • 带有副作用的宏参数
    • 宏与函数的对比
      • 宏的优势
      • 函数的优势
      • 宏与函数的命名约定
    • undef
    • 命令行定义
    • 条件编译

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

在ANSIC的任何一种实现中,存在俩个不同的环境:
1.翻译环境,在这个环境中源代码被转换为可执行的机器指令(二进制指令)
2.执行环境,用于执行代码

  • 计算机只能执行二进制的指令

翻译环境

在这里插入图片描述

在这里插入图片描述

在程序编译过程:

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

预处理(预编译)过程

1.#include头文件的包含
2.#define定义符号的替换和删除
3.注释的删除

  • 预处理(预编译)过程属于文本操作过程

编译过程

将C语言代码翻译成汇编代码

  • 语法分析
  • 词法分析
  • 语义分析
  • 符号分析

汇编过程

1.将汇编代码翻译成二进制指令(存放目标文件)
2.形成符号表

链接过程

1.合并段表
2.符号表的合并和符号表的重定义

【注意】把gcc移植到windows环境,编译产生的程序想在windows上运行,就得按照windows的可执行程序的格式进行

运行环境

在程序执行的过程:

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

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

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

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

预处理详解

预处理符号

在这里插入图片描述

int main(void)
{
	printf("%s\n",__FILE__);
	printf("%s\n",__LINE__);
	printf("%s\n",__DATE__);
	printf("%s\n",__TIME__);
	printf("%s\n",__STDC__);
	printf("%s\n",__FUNCTION__);
	return 0;
}
  • 这些预定义符号都是语言内置

预处理指令

1.#define
2.#include
3.#pragma
4.#error
5.#line

#define

#define定义标识符

#define name staff

  • define 定义一个数字
#define MAX 100
#define min 5
  • define 创建一个新名字
#define reg register
  • define 使用另外一个符号替换实现
#define do_forever for(;;)

注意:一般使用define定义标识符时,不使用;

#define定义宏

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

  • 宏的声明方式:
    #define name(parament-list) stuff
    其中的parament-list是一个由逗号隔开的符号表,可能出现在stuff中。

【注意】参数列表的左括号必须与name紧邻,如果俩种之间由任何空白存在,参数列表就会被解释为stuff的一部分

  • 宏可以看作是一种替换
#define ADD(x,y) x+y

int main(void)
{
	int a = 10;
	int b = 20;
	printf("%d",ADD(a,b));
	return 0;
}

【注意】所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。

#define替换规则

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

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

注意:

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

#与##

#的使用

将宏的参数变成对应的字符串

#define PRINTF(format,value)  printf("the value of "#value"is"format"\n",value)

int main(void)
{
	int i = 10;
	PRINT("%d",i);
	return 0;
}

##的使用

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

#define F(x,y) x##E##y
#include<stdio.h>
int main(void)
{
	printf("%f",F(2,-5));
	return 0;
}

带有副作用的宏参数

#define ADD(x,y) x+y
int main(void)
{
	int b = ++a;
	printf("%d",ADD(b,b));

	return 0;
}

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。

宏与函数的对比

宏的优势

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

函数的优势

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

宏与函数的命名约定

  • 宏的名字全部大写
  • 函数名的每个英文单词首字母大写

undef

#undef用于移除一个宏定义

#define ADD(x,y) x+y
#undef ADD

命令行定义

在命令行中定义符号。用于启动编译过程。

条件编译

在编译一个程序的时候如果一条语句(一组语句)编译或者放弃是很方便的

  • 这种情况适合于:调试型代码,删除可惜,保留多余
#if 常量表达式
 		//... 
#endif 
//常量表达式由预处理器求值。

2.多个分支的条件编译

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

3.判断是否被定义

#if defined(symbol) 
#ifdef symbol 

#if !defined(symbol) 
#ifndef symbol 

4.嵌套指令

#if defined(OS_UNIX) 
		 #ifdef OPTION1 
			 unix_version_option1(); 
 	  	 #endif 
		 #ifdef OPTION2 
 			 unix_version_option2(); 
  		 #endif 
 #elif defined(OS_MSDOS) 
    	 #ifdef OPTION2 
 			 msdos_version_option2(); 
 		 #endif 
#endif 

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

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

相关文章

C++面向对象的第二大特性:继承

1.继承的介绍 首先容我先向大家举一个列子: 我这里定义了一个Person的类 class Person { protected:string name;int age;string address;}; 在这个基础上&#xff0c;我要定义一个关于Student , Worker 的类 由于Student Worker都具有Person类中的成员变量 &#xff0c…

C语言自定义类型:结构体

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 C语言自定义类型:结构体 收录于专栏【C语言学习】 本专栏旨在分享学习C语言学习的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. 结…

EyeMock下载与使用教程

视频眼神修复直视镜头的AI具有极高的灵活性和适应性。它可以根据不同的拍摄环境和主播需求进行个性化设置&#xff0c;确保最佳的视觉呈现效果。在直播互动中&#xff0c;主播可能因为分神或疲劳而失去与观众的直视&#xff0c;这款工具能够迅速识别并修复这一问题&#xff0c;…

苹果M4性能分析:进步神速?还有多少空间?

2024年初&#xff0c;苹果推出了M4处理器&#xff0c;令人意外的是&#xff0c;它的发布距离M3发布仅仅过去了半年时间。更让人惊讶的是&#xff0c;M4首次亮相于iPad Pro。这一新处理器不仅仅是M3的简单升级版本&#xff0c;而是一次全面的架构优化。本文将详细分析M4处理器的…

网络工程师备考1——基础学习

认识设备 1 交换机 一、什么是交换机&#xff1f; 实现不同电脑之间数据的转发 换机是一种用于电(光)信号转发的网络设备。 它可以为接入交换机的任意两个网络节点提供独享的电信号通路。最常见的交换机是以太网交换机。交换机工作于OSI参考模型的第二层&#xff0c;即数据…

sw套合样条曲线

套合样条曲线,可以变成一条曲线,然后可以进行分段

sql select获取mysql所有数据库,指定数据库下的所有表名

介绍一下 MySQL 8.0 中默认安装的几个系统数据库/模式。 当我们安装 MySQL 8.0 并初始化数据库之后&#xff0c;默认会创建以下系统数据库&#xff1a; mysql&#xff0c;存储了 MySQL 服务器正常运行所需的各种信息。 information_schema&#xff0c;提供了访问数据库元数据…

黑马点评1——短信篇(基于session)

&#x1f308;hello&#xff0c;你好鸭&#xff0c;我是Ethan&#xff0c;一名不断学习的码农&#xff0c;很高兴你能来阅读。 ✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。 &#x1f3c3;人生之义&#xff0c;在于追求&#xff0c;不在成败&#xff0c;勤通…

使用DataGrip连接跳板机后再连接远程服务器的mysql数据库

相比配置本地数据库就是多了一步SSH/SSL配置。 添加新的mysql连接&#xff0c;选择SSH/SSL&#xff0c;勾选Use SSH tunnel&#xff1a; 点击右边的…配置跳板机连接&#xff0c;输入账号密码&#xff0c;然后保存&#xff1a; 接着配置General&#xff0c;里面填上要连接的数…

【蓝桥杯选拔赛真题76】python找出元素 第十四届青少年组蓝桥杯python选拔赛真题 算法思维真题解析

目录 python找出元素 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python找出元素 第十四届蓝桥杯青少年组python比赛选拔赛真题 一、题目要…

谷歌浏览器安装devtools工具

在浏览器中输入极简插件&#xff0c;然后打开如下的网页&#xff0c;在搜素框中输入vue 出现下图 点击推荐下载 &#xff08;地址&#xff1a;https://chrome.zzzmh.cn/info/nhdogjmejiglipccpnnnanhbledajbpd&#xff09; 打开谷歌浏览器如图 选择“扩展程序” 点开之后&…

抖音跳转微信卡片制作教程 小白也能搞

实测可以正常跳转&#xff0c;很牛逼&#xff0c;给大家分享一下~ 这是我做出来抖音发出去的效果&#xff0c;大家会制作了可以去卖钱&#xff0c;市场上一个这个卡片都要卖50-200&#xff0c;很不错的&#xff01;&#xff01; https://pan.baidu.com/s/1xPmGAWPcbAp7eXg7Dc…

防范TOCTOU竞态条件攻击

防范TOCTOU竞态条件攻击 在软件开发过程中&#xff0c;我们常常会遇到需要在使用资源之前检查其状态的情况。然而&#xff0c;如果资源的状态在检查和使用之间发生了变化&#xff0c;那么检查的结果可能会失效&#xff0c;导致软件在资源处于非正常状态时执行无效操作。这种时…

java微服在使用nacos注册中心时,ribbon负载均衡时给部分feign client使用静态serverList

我看很多贴子都是针对eureka环境下&#xff0c;做静态serverList配置&#xff0c;目前国内大部分都用Nacos&#xff0c;所以便研究了一下。 micore-service-x:ribbon:listOfServers: ip1:port,ip2:port2NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBased…

Java基础-反射原理

总结放前面&#xff1a; 反射是可以通过一个类对象或类名称获取到该类的全部信息&#xff08;属性和方法&#xff09;&#xff0c;包括为权限为private。 要使用反射第一步&#xff0c;要获取的类的Class对象&#xff0c;该Class对象存放在堆区&#xff0c;于类加载时创建&…

每日练习之完全背包——兑换零钱,完全背包问题总结

兑换零钱 题目描述 运行代码 #include<bits/stdc.h> #include<iostream> using namespace std; const int mod1e97; int a[20]{1,2,5,10,20,50,100,200,500,1000,2000,5000,10000},dp[100010]; int main() {dp[0]1;for(int i0;i<13;i)for(int ja[i];j<1000…

汇聚荣:新手做拼多多应该注意哪些事项?

新手在拼多多开店&#xff0c;面临的是竞争激烈的市场和复杂的运营规则。要想在这个平台上脱颖而出&#xff0c;必须注意以下几个关键事项。 一、市场调研与定位 深入了解市场需求和竞争对手情况是新手开店的首要步骤。选择有潜力的细分市场&#xff0c;并针对目标消费者群体进…

LaTeX 2022软件安装教程(附软件下载地址)

软件简介&#xff1a; 软件【下载地址】获取方式见文末。注&#xff1a;推荐使用&#xff0c;更贴合此安装方法&#xff01; LaTeX 2022是基于ΤΕΧ的一种排版系统&#xff0c;特别适用于生成科技和数学文档的高质量打印。它可用于各种文档类型&#xff0c;从简单信函到完整…

【Flutter】线性布局弹性布局层叠布局

&#x1f525; 本文由 程序喵正在路上 原创&#xff0c;CSDN首发&#xff01; &#x1f496; 系列专栏&#xff1a;Flutter学习 &#x1f320; 首发时间&#xff1a;2024年5月25日 &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e; 目…

如何修改WordPress网站的域名

我的网站用的是Hostease的虚拟主机&#xff0c;但是域名是之前在其他平台买的&#xff0c;而且已经快到期了&#xff0c;因为主机和域名在不同的平台上&#xff0c;管理不太方便&#xff0c;所以我又在Hostease重新注册了一个域名&#xff0c;然后把网站换成了新的域名&#xf…