学C的第三十四天【程序环境和预处理】

=========================================================================

相关代码gitee自取

C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

学C的第三十三天【C语言文件操作】_高高的胖子的博客-CSDN博客

 =========================================================================

                     

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

                 

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

               

(1). 翻译环境:

            

在这个环境中源代码转换为可执行的机器指令

                  

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

但我们平常写的C语言代码文本信息计算机不能直接执行

翻译环境就可以把C语言代码(源代码)翻译为二进制指令(可执行的机器指令

                     


                    

(2). 执行环境:

                  

在这个环境下可以执行二进制的代码(实际执行代码)

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

2 . 翻译环境

                        

翻译环境包括 编译链接

编译又包括 预编译(预处理)编译汇编

                

(1). 翻译环境下的 编译 和 链接:

                     

  • 组成一个程序的每个源文件通过编译过程(各自进行编译)分别转换成目标代码(object code)

       

  • 每个目标文件链接器(linker)捆绑在一起形成一个单一而完整可执行程序

            

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

                   

图解:

                     


                    

(2). 编译的几个阶段:

                    

VS这种集成开发环境不方便观察编译各阶段细节

Linux系统下使用gcc编译器更好观察

                

                

(2.1). 预编译(预处理) 阶段

                 

生成文件后缀xxx.i

该阶段进行一些文本操作

                    

       包括

  • 注释删除
  • #include 头文件包含
  • #define 符号替换
  • 所有预处理指令都是在预处理阶段处理

             

图解:

                  

                  

(2.2). 编译阶段

                 

生成文件后缀xxx.s

该阶段把C语言代码翻译为汇编指令

                      

       包括

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

              

图解:

                

                  

(2.3). 汇编阶段

                 

生成文件后缀xxx.o  (object目标文件)

该阶段将编译阶段完成的汇编指令翻译为二进制指令

            

编译阶段进行符号汇总

该阶段则会形成对应的符号表

以便链接时使用

              

图解:

                   


                    

(3). 链接:

             

编译生成的目标文件进行操作

生成可执行程序(也是二进制文件)

                    

       包括

  • 合并段表
  • 符号表(由编译的汇编阶段形成)的合并符号表重定位

              

图解:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                     

3 . 预处理详解

(1). 预定义符号

                  

C语言预定义了一些符号,这些预定义符号都是语言内置的

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

               

示例:

                     


                    

(2). #define

                   

(2.1). #define 定义标识符

                

写法:

#define name stuff

                   

  • name -- 定义的标识符名称
  • stuff -- 定义的标识符内容

示例:

                      

                      

define定义标识符的时候,最好不要在最后加上“分号 ;”

因为有些编译器可能会把“分号;”也当作stuff(标识符的内容)

示例:

                       

               

(2.2). #define 定义宏

                

写法:

#define name( parament-list ) stuff

                   

  • name -- 定义的宏的名称
  • parament-list -- 参数列表参数会替换放到 stuff 中
  • stuff -- 定义的宏的内容

               

注意:

定义宏时

参数列表的左括号必须与name紧邻。 如果两者之间有任何空白存在

参数列表就会被解释为stuff的一部分

         

示例:

                    

注意:

用于对数值表达式进行求值的宏定义都应该用上图这种方式加上括号

对stuff中的各个参数分别加上括号对stuff整体加上括号

避免在使用宏时由于参数中的操作符邻近操作符之间不可预料的相互作用

操作符优先级问题)。

                       

               

(2.3). #define 替换规则

                

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

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

                 

注意:

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

                         

示例:

                     

               

(2.4). # 和 ##

               

### 这两个符号只能在宏里面使用

                      

#:

                  
该符号可以把宏的参数以字符串的形式插入到字符串中

                  

示例:

                

##:

               

##可以把位于它两边的符号合成一个符号

允许宏定义从分离的文本片段创建标识符

             

注意:

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

         

示例:

                       

               

(2.5). 带副作用的宏参数

                     

宏参数宏的定义中出现超过一次的时候,如果参数带有副作用

那么你在使用这个宏的时候就可能出现危险导致不可预测的后果

副作用就是表达式求值的时候出现的永久性效果

           

示例:

                       

               

(2.6). 宏和函数对比

                

属 性#define定义宏函数

代 码

长 度

每次使用时,宏代码都会被插入到程序中

除了非常小的宏之外程序的长度会大幅度增长

函数代码只出现于一个地方每次使用这个函数时,都调用那个地方的同一份代码

执 行

速 度

更快存在函数的调用和返回额外开 销,所以相对慢一些
操作符 优先级宏参数的求值在所有周围表达式的上下文环境里, 除非加上括号否则邻近操作符的优先级可能会产生 不可预料的后果,所以建议宏在书写的时候多些括 号函数参数只在函数调用的时候求值一次,它的结果值传递给函数表达式的求值结果更容易预测
带有副 作用的 参数参数可能被替换到宏体中的多个位置,所以带有副作 用的参数求值可能会产生不可预料的结果函数参数只在传参的时候求值一 次结果更容易控制

参 数

类 型

宏的参数与类型无关,只要对参数的操作是合法的, 它就可以使用于任何参数类型函数的参数与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是相同的。
调 试宏是不方便调试函数是可以逐语句调试
递 归宏是不能递归函数是可以递归

                       

               

(2.7). 命名约定

                     

一般来讲,函数和宏的使用语法很相似,所以语言本身没法帮我们区分二者。

所以我们从书写上进行区分

  • 宏名全部大写
  • 函数名不要全部大写

                     


                    

(3). #undef

                

这个指令用于移除一个宏定义移除之后不能再使用

                

写法:

#undef NAMENAME

                   

  • NAME  --  要移除的宏的名字

示例:

                     


                    

(4). 命令行定义

                

许多C的编译器提供了一种能力,允许在命令行中定义符号用于启动编译过程

VS不行gcc可以

             

例如:

当我们根据同一个源文件要编译出一个程序的不同版本的时候,这个特性会有点用处。

(假定某个程序中声明了一个某个长度的数组,如果机器内存有限

我们需要一个很小的数组,但是另外一个机器内存大些,我们需要一个数组能够大些

这时候就可以用命令行定义灵活调整

              

示例:

                     


                    

(5). 条件编译

                 

使用条件编译指令可以决定一条(一组)语句是否进行编译

实现选择性的编译

条件编译指令如果为真,则编译时内容保留

条件编译指令如果为假,则编译时内容删除

           

           

常见的条件编译指令:

                 

指令一:单个分支的条件编译

                                #if  常量表达式                         //一条(一组)语句...                               #endif                                       //常量表达式由预处理器求值。
示例:

                

                

指令二:多个分支的条件编译

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

                

                

指令三:判断是否被定义的条件编译

                                //如果定义过:#if defined(symbol)  //第一种写法#ifdef symbol        //第二种写法//如果未定义过:#if !defined(symbol) //第一种写法#ifndef symbol       //第二种写法//symbol -- 定义的符号
示例:

                

                

指令四:嵌套指令

               

将上面的三种条件编译组合使用

          

示例:

                     


                    

(6). 文件包含

                        

#include指令 可以使另外一个文件被编译

就像它实际出现于 #include指令 的地方一样。

这种替换的方式很简单:

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

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

                 

                   

头文件被包含的方式:

            

本地文件包含

写法:

#include "filename"

               

查找策略:

先在源文件所在目录下查找,如果该头文件未找到

编译器就像查找库函数头文件一样在标准位置查找头文件

如果找不到就提示编译错误

                 

                   

库文件包含

写法:

#include <filename.h>

               

查找策略:

查找头文件直接去标准路径下去查找

如果找不到就提示编译错误

                 

                   

嵌套文件包含:

                   

如果一个文件有很多头文件

另一个文件包含了该文件,同时该文件也有头文件

再有一个文件包含这两个头文件

那么同一个头文件就有可能重复出现在一个文件中

           

可以使用条件编译来防止头文件重复出现

                                //每个头文件的开头写:#ifndef __TEST_H__#define __TEST_H__//头文件的内容#endif   //__TEST_H__//或者:#pragma once//头文件的内容

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

4 . 运行环境

程序执行的过程:

            

1. 程序必须载入内存中

有操作系统的环境中程序载入内存一般这个由操作系统完成

独立的环境中,程序的载入必须由手工安排

可能是通过可执行代码置入只读内存来完成。

                    

            

2.

程序执行开始,接着便调用main函数

                

            

3. 开始执行程序代码

这个时候程序将使用一个运行时堆栈(stack)(函数栈帧)

存储函数的局部变量返回地址

程序同时也可以使用静态(static)内存

存储于静态内存中的变量在程序的整个执行过程一直保留他们的值

              

            

4. 终止程序

正常终止main函数;也有可能是意外终止

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

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

相关文章

容器虚拟化基础之cgroups/LXC

"你真的&#xff0c;自由了~" 容器虚拟化基础之Cgroups: (1) 什么是cgroups cgroups是 linux 内核提供的一种机制&#xff0c; 这种机制可以根据需求把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内&#xff0c;从而为系统资源管理提供一个统一…

3D与沉浸式技术,如何助力企业数字化转型?

说起3D&#xff0c;估计许多读者朋友会在第一时间想起《阿凡达》系列和《侏罗纪公园》系列电影大作。每一帧细节纤毫毕现的逼真画面&#xff0c;让观众几乎分不清虚拟与现实&#xff0c;完全沉浸在导演打造的视觉盛宴中。 事实上&#xff0c;除了大家所熟知的3D影视动画之外&am…

快速学习GO语言总结

备注&#xff1a;本博客将自己初步学习GO的总结进行分享&#xff0c;希望大家通过本博客可以在短时间内快速掌握GO的基本程序编码能力&#xff0c;如有错误请留言指正&#xff0c;谢谢&#xff01; 一、初步了解Go语言 &#xff08;一&#xff09;Go语言诞生的主要问题和目标…

iOS17 widget Content margin

iOS17小组件有4个新的地方可以放置分别是&#xff1a;Mac桌面、iPad锁屏界面、 iPhone Standby模式、watch的smart stack Transition to content margins iOS17中苹果为widget新增了Content margin, 使widget的内容能够距离边缘有一定的间隙&#xff0c;确保内容显示完整。这…

学习设计模式之适配器模式,但是宝可梦

前言 作者在准备秋招中&#xff0c;学习设计模式&#xff0c;做点小笔记&#xff0c;用宝可梦为场景举例&#xff0c;有错误欢迎指出。 适配器模式 意图&#xff1a;将一个类的接口转换成客户希望的另一个接口 主要解决&#xff1a;把现有对象放到新环境里&#xff0c;而新…

IDEA 如何制作代码补丁?IDEA 生成 patch 和使用 patch

什么是升级补丁&#xff1f; 比如你本地修复的 bug&#xff0c;需要把增量文件发给客户&#xff0c;很多场景下大家都需要手工整理修改的文件&#xff0c;并整理好目录&#xff0c;这个很麻烦。那有没有简单的技巧呢&#xff1f;看看 IDEA 生成 patch 和使用 patch 的使用。 介…

一、Kafka概述

目录 1.3 Kafka的基础架构 1.3 Kafka的基础架构 Producer&#xff1a;消息生产者&#xff0c;就是向 Kafka broker 发消息的客户端Consumer&#xff1a;消息消费者&#xff0c;向 Kafka broker 取消息的客户端。Consumer Group&#xff08;CG&#xff09;&#xff1a;消费者组&…

PyTorch学习笔记(十六)——利用GPU训练

一、方式一 网络模型、损失函数、数据&#xff08;包括输入、标注&#xff09; 找到以上三种变量&#xff0c;调用它们的.cuda()&#xff0c;再返回即可 if torch.cuda.is_available():mynn mynn.cuda() if torch.cuda.is_available():loss_function loss_function.cuda(…

SpringMVC之@RequestMapping注解

文章目录 前言一、RequestMapping介绍二、详解&#xff08;末尾附源码&#xff0c;自行测试&#xff09;1.RequestMapping注解的位置2.RequestMapping注解的value属性3.RequestMapping注解的method属性4.RequestMapping注解的params属性&#xff08;了解&#xff09;5.RequestM…

华为ENSP网络设备配置实战6(简单的链路聚合)

题目要求 1、创建聚合组&#xff0c;添加端口成员 2、PC1网段为vlan10&#xff0c;PC2网段为vlan20 3、LSW1为核心网关设备&#xff0c;正确配置PC网关 4、PC1与PC2互通 解题过程 1.1、 按照拓扑图&#xff0c;各个设备起名 sys &#xff08;进入系统视图&#xff09; sy…

TCP协议报文结构

TCP是什么 TCP&#xff08;传输控制协议&#xff09;是一种面向连接的、可靠的、全双工的传输协议。它使用头部&#xff08;Header&#xff09;和数据&#xff08;Data&#xff09;来组织数据包&#xff0c;确保数据的可靠传输和按序传递。 TCP协议报文结构 下面详细阐述TCP…

FRP内网穿透,配置本地电脑作为服务器

FRP内网穿透&#xff0c;配置本地电脑作为服务器 下载FRP服务端客户端 参考链接&#xff1a; https://www.it235.com/实用工具/内网穿透/pierce.html https://www.cnblogs.com/007sx/p/17469301.html 由于没有公网ip&#xff0c;所以尝试内网穿透将本地电脑作为服务器&#xff…

【Mariadb高可用MHA】

目录 一、概述 1.概念 2.组成 3.特点 4.工作原理 二、案例介绍 1.192.168.42.3 2.192.168.42.4 3.192.168.42.5 4.192.168.42.6 三、实际构建MHA 1.ssh免密登录 1.1 所有节点配置hosts 1.2 192.168.42.3 1.3 192.168.42.4 1.4 192.168.42.5 1.5 192.168.42.6 …

(二)结构型模式:7、享元模式(Flyweight Pattern)(C++实例)

目录 1、享元模式&#xff08;Flyweight Pattern&#xff09;含义 2、享元模式的UML图学习 3、享元模式的应用场景 4、享元模式的优缺点 5、C实现享元模式的简单实例 1、享元模式&#xff08;Flyweight Pattern&#xff09;含义 享元模式&#xff08;Flyweight&#xff09…

OpenCV笔记之solvePnP函数和calibrateCamera函数对比

OpenCV笔记之solvePnP函数和calibrateCamera函数对比 文章目录 OpenCV笔记之solvePnP函数和calibrateCamera函数对比1.cv::solvePnP2.cv::solvePnP函数的用途和工作原理3.cv::solvePnP背后的数学方程式4.cv::SOLVEPNP_ITERATIVE、cv::SOLVEPNP_EPNP、cv::SOLVEPNP_P3P5.一个固定…

为什么需要单元测试?

为什么需要单元测试&#xff1f; 从产品角度而言&#xff0c;常规的功能测试、系统测试都是站在产品局部或全局功能进行测试&#xff0c;能够很好地与用户的需要相结合&#xff0c;但是缺乏了对产品研发细节&#xff08;特别是代码细节的理解&#xff09;。 从测试人员角度而言…

Qt应用开发(基础篇)——纯文本编辑窗口 QPlainTextEdit

一、前言 QPlainTextEdit类继承于QAbstractScrollArea&#xff0c;QAbstractScrollArea继承于QFrame&#xff0c;是Qt用来显示和编辑纯文本的窗口。 滚屏区域基类https://blog.csdn.net/u014491932/article/details/132245486?spm1001.2014.3001.5501框架类QFramehttps://blo…

Elasticsearch复合查询之Boosting Query

前言 ES 里面有 5 种复合查询&#xff0c;分别是&#xff1a; Boolean QueryBoosting QueryConstant Score QueryDisjunction Max QueryFunction Score Query Boolean Query在之前已经介绍过了&#xff0c;今天来看一下 Boosting Query 用法&#xff0c;其实也非常简单&…

Chapter 15: Object-Oriented Programming | Python for Everybody 讲义笔记_En

文章目录 Python for Everybody课程简介Object-oriented programmingManaging larger programsGetting startedUsing objectsStarting with programsSubdividing a problemOur first Python objectClasses as typesObject lifecycleMultiple instancesInheritanceSummaryGlossa…

python基础5——正则、数据库操作

文章目录 一、数据库编程1.1 connect()函数1.2 命令参数1.3 常用语句 二、正则表达式2.1 匹配方式2.2 字符匹配2.3 数量匹配2.4 边界匹配2.5 分组匹配2.6 贪婪模式&非贪婪模式2.7 标志位 一、数据库编程 可以使用python脚本对数据库进行操作&#xff0c;比如获取数据库数据…