程序环境和预处理(含C语言程序的编译+链接)--1

🎉个人名片:

🐼作者简介:一名乐于分享在学习道路上收获的大二在校生
🐻‍❄个人主页🎉GOTXX
🐼个人WeChat:ILXOXVJE
🐼本文由GOTXX原创,首发CSDN🎉🎉🎉

🕊系列专栏:零基础学习C语言----- 数据结构的学习之路
🐓每日一句:如果没有特别幸运,那就请特别努力!🎉🎉🎉
————————————————

🎉文章简介:

本篇文章对    程序环境和预处理详解(含C语言程序的编译+链接)  的相关知识详细讲解!

如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加油,一起奔跑,让我们顶峰相见!!!🎉🎉🎉

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

在ANSI C(标准C)的任何一种实现中,存在两个不同的环境;
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令;
第2种是执行环境,它用于实际执行代码;

换种说法就是:

计算机时能够执行二进制指令的;

但是我们写出的代码是文本信息,计算机不能够直接理解;

翻译环境-->代码转换为--->二进制指令

执行环境-->执行二进制代码

2. 详解编译+链接

2.1 翻译环境

 每个源文件都会单独经过编译器的处理,生成一个对应的目标文件;

例如:test.c源文件    经过编译器处理生成    test.obj文件;

然后多个目标文件+链接库经过连接器的处理生成可执行程序,最终生成  test.exe  的文件;

其中:链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程
序员个人 的程序库,将其需要的函数也链接到程序中;

这个连接器的处理过程就叫链接;

实例:

当我们写好这两个文件进行编译链接后: 

如图:

 生成了两个obj的目标文件

一个exe的可执行程序

 

2.3 运行环境(简单介绍)

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

3. 预处理详解

3.1 预定义符号

__FILE__       // 进行编译的源文件
__LINE__     // 文件当前的行号
__DATE__     // 文件被编译的日期
__TIME__     // 文件被编译的时间
__STDC__     // 如果编译器遵循 ANSI C ,其值为 1 ,否则未定义
这些预定义符号都是语言内置的

举个栗子:

 

运行结果:

 

3.2 #define

3.2.1 #define 定义标识符

语法:
        #define    name    stuff
举个栗子:

提问:
在define定义标识符的时候,要不要在最后加上 ; ?
比如:
建议不要加上 ; ,这样容易导致问题
比如下面的场景:

这里会出现语法错误 

 

3.2.2 #define 定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义 宏(define macro)
下面是宏的申明方式:
#define name( parament - list ) stuff
其中的 parament - list 是一个由逗号隔开的符号表,它们可能出现在 stuff
注意:
参数列表的左括号必须与 name 紧邻;
如果两者之间有任何空白存在,参数列表就会被解释为 stuff 的一部分

如例子一:

这个宏接收一个参数
如果在上述声明之后,你把

置于程序中,预处理器就会用下面这个表达式替换上面的表达式:

 

警告:
这个宏存在一个问题:
观察下面的代码段:

 

乍一看,你可能觉得这段代码将打印36这个值
事实上,它将打印11.
为什么?
替换文本时,参数  x  被替换成  a + 1  ,所以这条语句实际上变成了:
printf  (" %d \ n", a + 1 * a + 1 );
这样就比较清晰了,由替换产生的表达式并没有按照预想的次序进行求值。
在宏定义上加上两个括号,这个问题便轻松的解决了:

这样预处理之后就产生了预期的效果:
例子二:
这里还有一个宏定义:

 

定义中我们使用了括号,想避免之前的问题,但是这个宏可能会出现新的错误

 

这将打印什么值呢?
warning
看上去,好像打印 100 ,但事实上打印的是 55.
我们发现替换之后:

 

乘法运算先于宏定义的加法,所以出现了 55 
这个问题,的解决办法是在宏定义表达式两边加上一对括号就可以了

 

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

 

2.2.3 #define 替换规则

在程序中扩展 #define 定义符号和宏时,需要涉及几个步骤
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由 #define 定义的符号。如果是,它们首先被替换;
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换;
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由 #define 定义的符号。如果是,就重复上述处理过程;
注意:
1. 宏参数和 #define 定义中可以出现其他 #define 定义的符号。但是对于宏,不能出现递归;
2. 当预处理器搜索 #define 定义的符号的时候,字符串常量的内容并不被搜索;

3.2.4 ###

知识铺垫:

C语言支持这样的写法:

两个字符串和在一起,相当于一个字符串;

我们发现字符串是有自动连接的特点的

 如:

当我们写一个这样的代码:

 我们发现几个printf函数打印的内容都相似,那我们能不能定义一个宏来实现打印呢?

答案是:可以的。

这里就要用到#的作用了,将参数插入到字符串中

 

 

#的作用:

使用 # 把一个宏参数变成对应的字符串,就是把一个宏的参数,以字符串的形式,插入到

一个字符串中去;

下面讲解  ##  的作用及用法:

##的作用:()只能在宏定义里使用

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

例如:

分析:

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

3.2.5 带副作用的宏参数

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

 

MAX宏可 以证明具有副作用的参数所引起的问题

例如:

运行结果是什么呢?

 

这里我们得知道预处理器处理之后的结果是什么:

输出的结果是:

 

 输出结果分析:

 

3.2.6 宏和函数对比

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

 

这里不用函数的原因有二:
1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多;
所以宏比函数在程序的规模和速度方面更胜一筹;
2. 更为重要的是函数的参数必须声明为特定的类型;
所以函数只能在类型合适的表达式上使用,反之这个宏怎可以适用于整形、长整型、浮点型等可以 用于>来比较的类型;
宏是类型无关的
宏的缺点:当然和函数相比宏也有劣势的地方:
1. 每次使用宏的时候,一份宏定义的代码将插入到程序中,除非宏比较短,否则可能大幅度增加程序 的长度;
2. 宏是没法调试的;
3. 宏由于类型无关,也就不够严谨;
4. 宏可能会带来运算符优先级的问题,导致程容易出现错;

 

宏有时候可以做函数做不到的事情
比如:宏的参数可以出现 类型 ,但是函数做不到

宏和函数的一个对比:

宏和函数的对比

 

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

本章完~

剩余未讲解完的知识在下章


 

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

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

相关文章

在CentOS 7上挂载硬盘到系统的步骤及操作

目录 1:查询未挂载硬盘2:创建挂载目录3:检查磁盘是否被分区4:格式化硬盘5:挂载目录6:检查挂载状态7:设置开机自动挂载总结: 本文介绍了在CentOS 7上挂载硬盘到系统的详细步骤。通过确…

完全背包问题

题目链接 题意&#xff1a;在01背包的基础上多了每个物品都可以无限取的条件 思路&#xff1a;首先考虑在01背包的基础上的暴力枚举&#xff0c;我们可以在枚举前i件物品最多拿j的容量时再遍历当前物品拿的数量 贴一个暴力tle代码&#xff1a; #include<bits/stdc.h> #d…

【云原生K8s】初识Kubernetes的理论基础

K8S由google的Borg系统(博格系统&#xff0c;google内部使用的大规模容器编排工具)作为原型&#xff0c;后经GO语言延用Borg的思路重写并捐献给CNCF基金会开源。 云原生基金会&#xff08;CNCF&#xff09;于2015年12月成立&#xff0c;隶属于Linux基金会。CNCF孵化的第一个项目…

短视频矩阵源码

一、短视频矩阵源码搭建解析&#xff1a; 目录 一、短视频矩阵源码搭建解析&#xff1a; 二、短视频矩阵源码的开发路径分享&#xff1a; 三、短视频矩阵系统开发应具备哪些能力&#xff1f; 短视频技术开发能力&#xff1a; 开发人员应具备短视频相关技术能力&#xff0c…

iOS 后台运行

iOS后台行&#xff0c;一般有两种方式&#xff1a; 1.UIBackgroundTaskIdentifier后台任务标记时, 2.设置后台运行模式&#xff0c;需要有voip&#xff0c;location功能的才行。不然app上线审核肯定是过不了的。 下面是我学习后台运行的尝试过程。 一.首先创建一个项目功程…

python#django数据库一对一/一对多/多对多

一对一OneToOneField 用户和用户信息 搭建 # 一对一 class TestUser(models.Model): usernamemodels.CharField(max_length32) password models.CharField(max_length32) class TestInfo(models.Model): mick_namemodels.CharField(max_length32) usermode…

zookeeper --- 高级篇

一、zookeeper 事件监听机制 1.1、watcher概念 zookeeper提供了数据的发布/订阅功能&#xff0c;多个订阅者可同时监听某一特定主题对象&#xff0c;当该主题对象的自身状态发生变化时(例如节点内容改变、节点下的子节点列表改变等)&#xff0c;会实时、主动通知所有订阅者 …

STM32CubeMX+VSCODE+EIDE+RT-THREAD 工程创建

Eide环境搭建暂且不表&#xff0c;后续补充。主要记录下Vscode环境下 创建Rt-thread工程的过程。分别介绍STM32CubeMX添加rtt支持包的方式和手动添加rtt kernel方式。STM32CubeMX生成工程的时候有"坑"&#xff0c;防止下次忘记&#xff0c;方便渡一下有缘人&#xff…

【Linux后端服务器开发】Reactor模式实现网络计算器

目录 一、Reactor模式概述 二、日志模块&#xff1a;Log.hpp 三、TCP连接模块&#xff1a;Sock.hpp 四、非阻塞通信模块&#xff1a;Util.hpp 五、多路复用I/O模块&#xff1a;Epoller.hpp 六、协议定制模块&#xff1a;Protocol.hpp 七、服务器模块&#xff1a;Server.…

基于SpringBoot+Vue的地方美食分享网站设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

Docker实战-操作Docker容器实战(一)

导语   在之前的分享中&#xff0c;我们介绍了关于如何去操作Docker镜像&#xff0c;下面我们来看看如何去操作容器。 简单来讲&#xff0c;容器是镜像运行的一个实例&#xff0c;与镜像不同的是镜像只能作为一个静态文件进行读取&#xff0c;而容器是可以在运行时进行写入操…

2023再谈前端状态管理

目录 什么是状态管理&#xff1f; 状态 常见模式 要解决的问题 心智模型 React Context Context 的问题 优点 缺点 React 外部状态管理库 概览 Class 时代 Redux 单向数据流 三大原则 如何处理异步 如何处理数据间联动 优点 缺点 Dva icestore Mobx 设…

危大工程智慧工地源码,微服务+Java+Spring Cloud +UniApp +MySql 物联网、人工智能、视频AI分析

一套智慧工地管理平台源码&#xff0c;PC端移动APP端可视货数据管理端源码 智慧工地可视化系统利用物联网、人工智能、云计算、大数据、移动互联网等新一代信息技术&#xff0c;通过工地中台、三维建模服务、视频AI分析服务等技术支撑&#xff0c;实现智慧工地高精度动态仿真&a…

windows上给oracle打补丁注意事项

打补丁的过程 1、升级opatch工具&#xff0c;检查剩余空间用于存放ORACLE_HOME的备份&#xff0c;设置oracle_home环境变量,通过readme中的先决条件来检查现有补丁是否和本次补丁冲突 2、opatch apply 升级数据库软件&#xff0c;这个必须数据库文件不要被进程调用 在windows上…

美团前端研发框架Rome实践和演进趋势

本文整理自美团技术沙龙第76期《大前端研发协同效能提升与实践》&#xff0c;为大家介绍了美团到店前端研发框架Rome实践和演进趋势。 具体来讲&#xff0c;本文首先介绍了Rome整体的工程生态、演变路径、规模化升级以及工程框架外的开发辅助工具&#xff1b;第二部分&#xff…

GoogLeNet卷积神经网络-笔记

GoogLeNet卷积神经网络-笔记 GoogLeNet是2014年ImageNet比赛的冠军&#xff0c; 它的主要特点是网络不仅有深度&#xff0c; 还在横向上具有“宽度”。 由于图像信息在空间尺寸上的巨大差异&#xff0c; 如何选择合适的卷积核来提取特征就显得比较困难了。 空间分布范围更广的…

matlab智能算法程序包89套最新高清录制!matlab专题系列!

关于我为什么要做代码分享这件事&#xff1f; 助力科研旅程&#xff01; 面对茫茫多的文献&#xff0c;想复现却不知从何做起&#xff0c;我们通过打包成品代码&#xff0c;将过程完善&#xff0c;让您可以拿到一手的复现过程以及资料&#xff0c;从而在此基础上&#xff0c;照…

OpenCV基础

目录 图像基本操作数据读取——图像数据读取——视频截取部分图像数据 ROI——Region of Interest颜色通道提取边界填充数值计算图像融合 图像处理灰度图HSV图像阈值图像平滑(滤波)形态学-腐蚀操作形态学-膨胀操作开运算与闭运算梯度运算礼帽与黑帽 图像梯度——Sobel算子看看L…

Vue 2.x 项目升级到 Vue 3详细指南【总结版】

文章目录 0.前言1.升级教程1.1. 升级 Vue CLI&#xff1a;1.2. 安装 Vue 3&#xff1a;1.3. 更新 Vue 组件&#xff1a;1.4. 迁移全局 API&#xff1a;1.5. 迁移路由和状态管理器&#xff1a;1.6. 迁移 TypeScript&#xff1a;1.7. 迁移测试代码&#xff1a; 2.迁移总结2.0. 这…

深入学习 Redis - 谈谈你对 Redis 的 RDB、AOF、混合持久化的了解吧?

目录 一、Redis 是怎么存储数据的&#xff1f; 二、Redis 具体是按照什么样的策略来实现持久化的&#xff1f; 2.1、RDB&#xff08;Redis Database&#xff09; 2.1.1、触发机制 2.1.2、bgsave 命令处理流程 2.1.3、RDB 文件的处理 2.1.4、演示效果 1&#xff09;手动执…