当前位置: 首页 > 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...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

Spring Boot面试题精选汇总

🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性&#xf…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...

Kafka入门-生产者

生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...