当前位置: 首页 > news >正文

【c语言】预处理

在这里插入图片描述

🚀write in front🚀
📜所属专栏:> c语言学习
🛰️博客主页:睿睿的博客主页
🛰️代码仓库:🎉VS2022_C语言仓库
🎡您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!
关注我,关注我,关注我你们将会看到更多的优质内容!!

在这里插入图片描述

文章目录

  • 前言:
  • 一、详解编译与链接:
    • 1.程序的翻译环境与执行环境:
    • 2.翻译环境:
    • 3.翻译阶段:
      • ①.编译:
        • 预编译:
        • 编译:
        • 汇编
          • 符号表:
      • ②.链接:
    • 4.运行环境:
  • 二、预处理详解:
    • 1.预定义符号:
    • 2.#define:
        • ①. #define 定义标识符:
        • ②.#define 定义宏:
        • ③. #define 替换规则:
  • 总结:

前言:

一、详解编译与链接:

1.程序的翻译环境与执行环境:

  在研究程序的编译与链接细节之前,我们首先要了解我们程序的翻译以及执行环境,我们要知道,在 ANSI C 的任何一种实现中,都存在着两种环境:

第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
第2种是执行环境,它用于实际执行代码。

2.翻译环境:

  在翻译环境中执行的操作,简单来说可以分为三个步骤

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

在这里插入图片描述
每次编译运行完成以后,我们都可以在所在文件里发现.exe文件和.obj文件

在这里插入图片描述

3.翻译阶段:

  翻译阶段又可以分为两个阶段,即编译链接
在这里插入图片描述

①.编译:

预编译:

首先我们先一段普通的代码:
在这里插入图片描述
然后用gcc做以下操作:

  • 预处理 选项 gcc -E test.c -o test.i
  • 预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中

就会发现下面的情况:
在这里插入图片描述
预处理的作用:

头文件的包含、#define 定义符号的替换、注释的删除,文本操作。

编译:

用gcc选项 gcc -S test.c
编译完成之后就停下来,结果保存在test.s中,我们便会发现汇编代码

编译的作用

编译阶段就是将 C 语言的代码翻译成汇编代码,其中包含了语法分析、词法分析、语义分析、符号汇总(后面会重点讲到)等。

汇编

用gcc选项gcc -c test.c
汇编完成之后就停下来,结果保存在test.o中,出现乱码(2进制码以文本形式无法看懂)

汇编的作用:

将汇编代码翻译成二进制的指令(存放到目标文件)符号汇总后**:形成符号表

符号表:

一个类似于这样的表格:
在这里插入图片描述
在这里,我们的符号表里是该程序里所有的函数名和每个函数所在的的地址,(对于使用的其他文件的函数,地址都先计为0x00,在后面链接阶段在修改该地址)

②.链接:

编译的作用是:

合并段表
合并符号表
重定位符号表

这里就是对所有文件的一个汇总,对于一个程序里,一个源文件使用了另一个源文件的函数,通过链接,就会将函数的地址汇总,并且改掉地址为0的函数。

4.运行环境:

在这个环境下,我们的程序就真正进入了运行阶段。我们程序的执行过程可以简述为下面四个步骤

  • ①. 程序载入内存中:在有操作系统的环境中该步骤通常由操作系统完成;而在独立环境中则必须由我们自己手动完成;独立环境中的程序也有可能通过执行可执行代码置入只读内存来完成。

  • ②. 调用 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定义的符号的时候,字符串常量的内容并不被搜索

总结:

  这就是程序环境和预处理的相关知识。当然,这里对于宏的讲解是往往不够的!宏的相关知识将在下一篇文章中进行详细讲解!更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

专栏订阅:
每日一题
c语言学习
算法
智力题
更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

在这里插入图片描述

相关文章:

【c语言】预处理

🚀write in front🚀 📜所属专栏:> c语言学习 🛰️博客主页:睿睿的博客主页 🛰️代码仓库:🎉VS2022_C语言仓库 🎡您的点赞、关注、收藏、评论,是…...

嵌入式常用知识

12、并发和并行的区别? 最本质的区别就是:并发是轮流处理多个任务,并行是同时处理多个任务。 你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。 你吃饭吃到一半&…...

和平精英五曜赐福返场,老款玛莎返场来了

和平精英五曜赐福返场,老款玛莎返场来了!新款如何选择! 关于返场的新消息,都说云南百收SEO解说消息不准,之前看过文章的应该会知道,全网只有云南百收SEO解说发了。玛莎返场,快喊你的阿姨来看&a…...

React从入门到精通二

React从入门到精通之购物车案例1. 购物车需求说明使用到的data list2. 项目code1. 购物车需求说明 list data展示到列表中每个item的通过按钮来控制购买的数据量删除按钮可以删除当前的itemTotal Price计算当前购物车的总的价格 使用到的data list const books [{id: 1,name…...

【likeshop多商户】电子面单商家直播上线啦~

likeshop多商户商城v2.2.0版本更新啦! 新增功能: 商家直播 单子面单 优化: 个人中心优惠券数量统计优化 修复: 秒杀商品待审核时,下单价格计算错误 个人中心修改头像后地址保存错误 「商家直播」 提升品牌知名度…...

游戏化销售管理是什么?使用CRM系统进行有什么用?

对于企业销售来说,高薪酬也伴随着更高的压力与挑战。高强度的单一工作会让销售人员逐渐失去对工作的兴趣,导致销售状态缺少动力和激情,工作开展愈加困难。您可以通过CRM系统进行游戏化销售管理,让销售人员重新干劲满满。 游戏并不…...

Mysql 索引(三)—— 不同索引的创建方式(主键索引、普通索引、唯一键索引)

了解了主键索引的底层原理,主键索引其实就是根据主键字段建立相关的数据结构(B树),此后在使用主键字段作为条件查询时,会直接根据主键查找B树的叶子结点。除了主键索引外,普通索引和唯一键索引也是如此&…...

秒懂算法 | 基于朴素贝叶斯算法的垃圾信息的识别

本文将带领大家亲手实现一个垃圾信息过滤的算法。 在正式讲解算法之前,最重要的是对整个任务有一个全面的认识,包括算法的输入和输出、可能会用到的技术,以及技术大致的流程。 本任务的目标是去识别一条短信是否为垃圾信息,即输入为一条文本信息,输出为二分类的分类结果。…...

SpringCloud - Feign远程调用

目录 Feign的远程调用 RestTemplate方式调用存在的问题 介绍与初步使用 Feign的自定义配置 Feign运行自定义配置来覆盖默认配置,可以修改的配置如下: 配置Feign日志有两种方式: Feign性能优化 Feign底层的客户端实现: 连…...

Eotalk Vol.03:结合 API DaaS,让使用数据更方便

Eotalk 是由 Eolink CEO 刘昊臻发起的泛技术聊天活动,每期都会邀请一些技术圈内的大牛聊聊天,聊些关于技术、创业工作、投融资等热点话题。 Eotalk 的第 3 期,很高兴邀请到 Tapdata CEO TJ 唐建法,TJ 可以说是一位超级大咖&#x…...

从零开始学习Java编程:一份详细指南

Java入门Java简介和历史Java开发环境的安装和配置Java开发工具的介绍和使用(例如Eclipse、IntelliJ IDEA等)Java语言的基本概念(例如变量、数据类型、运算符、流程控制语句等)面向对象编程基础面向对象编程概念和基本原则类和对象…...

电子技术——系统性分析反馈电压放大器

电子技术——系统性分析反馈电压放大器 在本节我们提供一个系统性的分析反馈电压放大器的方法。首先我们考虑反馈网络没有负载效应理想情况,其次我们考虑反馈网络有限阻抗下的非理想情况。总之,这种方法的思路在于,将非理想情况转换为理想情况…...

【C语言进阶】结构体、位段、枚举、以及联合(共用体)的相关原理与使用

​ ​📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:C语言进阶 🎯长路漫漫浩浩,万事皆有期待 文章目录1.结构体1.1 概述&a…...

《蓝桥杯每日一题》哈希·AcWing 2058. 笨拙的手指

1.题目描述每当贝茜将数字转换为一个新的进制并写下结果时,她总是将其中的某一位数字写错。例如,如果她将数字 14 转换为二进制数,那么正确的结果应为 1110,但她可能会写下 0110 或 1111。贝茜不会额外添加或删除数字,…...

Linux 定时任务调度(crontab)

一、Crontab Crontab命令用于设置周期性被执行的指令。该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行。 可以使用Crontab定时处理离线任务,比如每天凌晨2点更新数据等,经常用于系统任务调度。…...

C进阶:6.C语言文件操作

目录 1.为什么使用文件 2.什么是文件 2.1程序文件 2.2数据文件 2.3文件名 3.文件的打开和关闭 3.1文件指针 4.文件的顺序读写 fputc()写入文件 fgetc()从文件中读取 fgets()读取一段字符串 fprintf格式化写入文件、fscanf格式化读出文件 4.1对比一组函数 5.文件…...

Linux环境变量

Linux环境变量孤儿进程进程优先级其他概念环境变量感性的理解环境变量常见的环境变量添加环境变量环境变量的组织形式通过代码如何获取环境变量再次理解环境变量命令行参数孤儿进程 概念:父进程先于子进程结束,这样的子进程就叫做“孤儿进程”; “孤儿”…...

Kotlin-委托、代理和单例对象

委托和代理 实现委托和代理,使用的是by关键字。 这里设计一个场景:假设某个演员被要求唱歌,但是不会唱歌,就委托一个会唱歌的歌手在后台唱歌。 如何实现这个需求,下面就开始直接写代码 首先定义一个唱歌能力接口 int…...

华为OD机试真题Python实现【报数】真题+解题思路+代码(20222023)

报数 题目 一百个人围成一圈,每个人有一个编码编号从一开始到一百。 他们从一开始依次报数,报道M的人自动退出圈圈, 然后下一个人接着从1开始报数一直到剩余人数小于M。 请问最后剩余人在原先的编码为多少? 🔥🔥🔥🔥🔥👉👉👉👉👉👉 华为OD机试(Py…...

MacOS:Error message “error:0308010C:digital envelope routines::unsupported“

命令行:export NODE_OPTIONS--openssl-legacy-provider 原帖:https://stackoverflow.com/questions/69692842/error-message-error0308010cdigital-envelope-routinesunsupported...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

【JavaEE】-- HTTP

1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...

LangFlow技术架构分析

🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...