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

【C语言】预处理详解(上)

文章目录

  • 前言
  • 1. 预定义符号
  • 2. #define 定义常量
  • 3. #define定义宏
  • 4. 带有副作用的宏参数
  • 5. 宏替换的规则

前言

在讲解编译和链接的知识点中,我提到过翻译环境中主要由编译和链接两大部分所组成。
其中,编译又包括了预处理、编译和汇编。当时,我只是粗略的讲解预处理的过程,那么本文将会带着大家去领略预处理的各项操作。还有一些预处理的奇葩操作。

哈哈

1. 预定义符号

C语言设置了一些预定义符号,可以直接使用,预定义符号也是在预处理阶段就被直接替换掉了。

预处理符号:

__FILE__	//意思:进行编译的源文件
__LINE__	//意思:显示该代码语句所在的行数
__DATE__	//意思:文件被编译的日期
__TIME__	//意思:文件被编译的时间
__STDC__	//意思:如果该C编译器完全遵顼ANSI C的标准,则其值为0。否则就是非定义。

演示案例:
预定义符号的演示

2. #define 定义常量

基本语法:

#define name stuff

举个例子:

//#define 定义常量
#define MAX 10000
#define reg register //为register关键字,创建一个简洁的名字。
#define do_forever for(;;) //用更形象的符号来替换一种实现。
#define CASE break;case	//在写case语句的时候自动把break写上。// 如果#define定义的stuff过长,可以分成几行来写,除了最后一行外,
//每行的后面都加上一个反斜杠\(续航符)
#define DEBUG_PRINT printf("file:%s\tline:%d\t\date:%s\ttime:%s\n", \__FILE__,__LINE__ \__DATE__,__TIME__)

思考一个问题:#define定义标识符时,要不要在最后加上;号?
比如:

#define MAX 1000;
#define MAX 1000

我的建议是不要加;号。你别看上面的代码可以正常的运行,但是针对某些特定的应用场景,可能会引发一些难以察觉的错误。

比如下面的例子:

#define MAX 1000;
int main()
{int max = 0;int condition = 1;if(condition)max = MAX;elsemax = 0;
}

上述代码直接运行会报错,而错误的原因是悬空else的问题。因为MAX本身就拥有了一个;号,而我们在代码写的分号会被是作为一个空语句,也就是说,if之后else之前由两条语句。但是如果要在if后里面写多条语句就得有大括号括起来。否则,就会报语法错误。

3. #define定义宏

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

下面时宏的声明方式:

#define name(parament-list) stuff

其中,parament-list是一个由逗号分隔的符号表,它们可能出现在stuff中。

注意:参数列表的左括号一定要与name紧邻。如果两者之间有空格的话,参数列表就会被编译器解释为stuff中的一部分。

举例:

#define SQUARE(x) x*x

这个宏接受了一个参数x。如果在上述声明过后,把SQUARE(5);置于程序中,与编译器就会用5*5这个表达式来替换SQUARE(5)

但是,我们写的这一个宏有潜在的隐患。为什么这么说呢?
请看下面的例子:

#include<stdio.h>
#define SQUARE(x) x*x
int main()
{int a = 5;printf("%d\n",SQUARE(a + 1));return 0;
}

答案
哎呦,这里的答案不是36吗,为什么这里会打印出11?

其实,这是直接替换文本的弊端,它是直接替换的。也就是说,先前的printf里的参数变为了
printf("%d",a+1*a+1);

这样说的话就比较清晰了,有替换产生的表达式并没有按照我们的预期顺序进行运算求值。

那我们该怎么修改上述的代码,使其能够得到正确的答案呢?
方法很简单,就是加括号,改变运算符的优先级。

#include<stdio.h>
#define SQUARE(x) (x)*(x)
int main()
{int a = 5;printf("%d\n",SQUARE(a + 1));return 0;
}

这样就达到了预期的效果了。

为了巩固大家加括号的意识,我再举一个例子。
这里还有一个宏定义:

#define DOUBLE(x) (x) + (x)

在定义中我们为了避免预算符之间的优先级和结合性,我们给其添上了括号,但是这个宏仍然会出现问题。

int a = 5;
printf("%d",10*DOUBLE(a));

这个会打印出什么结果呢?看上去好像是100,但事实上打印的值确是55。

我们发现替换之后:

printf("%d",10*(5)+(5));

乘法运算的优先级高于加法,所以就会出现55.
为了解决这个问题,我们可以这样写:

#define DOUBLE(x) ((x)+(x))

以上两个例子告诉我们,在写宏时,一定不要节省你的括号。

4. 带有副作用的宏参数

什么叫带有副作用?
请大家看下面几段代码:

int a = 5;
int b = 0; 
b = a + 1; //方案1
b = a++; //方案2

在方案1中,a的值仍然为5 。但在方案2中,a的值就变为6了。相信讲到这里你已经有点感觉了。

所谓带有副作用其实就是以修改参与运算变量的值为代价,实现我们要到达的效果。

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

这里我们设置一段代码来证明带有副作用的宏参数所引发的问题:

#define MAX(a,b) ((a>b)?(a):(b))
...
x = 5;
y = 8;
z = MAX(x++,y++);
printf("x=%d,y=%d,z=%d\n",x,y,z);//输出的结果是什么?

输出的结果为:x=6,y=10,z=9

5. 宏替换的规则

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

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

注意:

  • 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏来说,不能出现递归。
  • 当预处理器搜索#define定义的符号的时候,字符串常量的内容不在搜索范围

限于篇幅的原因,本文就像先讲到这里。后续的内容都在预处理详解(下)中,欢迎大家指点一二。💖💖💖

相关文章:

【C语言】预处理详解(上)

文章目录 前言1. 预定义符号2. #define 定义常量3. #define定义宏4. 带有副作用的宏参数5. 宏替换的规则 前言 在讲解编译和链接的知识点中&#xff0c;我提到过翻译环境中主要由编译和链接两大部分所组成。 其中&#xff0c;编译又包括了预处理、编译和汇编。当时&#xff0c…...

uni-app内置组件(基本内容,表单组件)()二

文章目录 一、 基础内容1.icon 图标2.text3.rich-text4.progress 二、表单组件1.button2.checkbox-group和checkbox3.editor 组件4.form5.input6.label7.picker8.picker-view 和 picker-view-column9.radio-group 和 radio10.slider11.switch12.textarea 一、 基础内容 1.icon…...

linux搭建redis超详细

1、下载redis包 链接: https://download.redis.io/releases/ 我以7.0.11为例 2、上传解压 mkdir /usr/local/redis tar -zxvf redis-7.0.11.tar.gz3、进入redis-7.0.11&#xff0c;依次执行 makemake install4、修改配置文件redis.conf vim redis.conf为了能够远程连接redis…...

Flink-DataWorks第二部分:数据集成(第58天)

系列文章目录 数据集成 2.1 概述 2.1.1 离线&#xff08;批量&#xff09;同步简介 2.1.2 实时同步简介 2.1.3 全增量同步任务简介 2.2 支持的数据源及同步方案 2.3 创建和管理数据源 文章目录 系列文章目录前言2. 数据集成2.1 概述2.1.1 离线&#xff08;批量&#xff09;同步…...

4个从阿里毕业的P7打工人,当起了包子铺的老板

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247483727&idx1&sndb05d8c1115a4539716eddd9fde4e5c9&chksmc0e47813f793f105017fb8551c9b996dc7782987e19efb166ab665f44ca6d900210e6c4c0281&scene21#wechat_redirect 《网安面试指南》h…...

javaweb_07:分层解耦

一、三层架构 &#xff08;一&#xff09;基础 在请求响应中&#xff0c;将代码都写在controller中&#xff0c;看起来内容很复杂&#xff0c;但是复杂的代码总体可以分为&#xff1a;数据访问、逻辑处理、接受请求和响应数据三个部分。在程序中我们尽量让一个类或者一个方法…...

调用 Python 开源库,获取油管英文视频的手动或自动英文srt字幕,以及自动中文简体翻译srt字幕

前提条件 非常抱歉&#xff0c;这个程序就是个雏形&#xff0c;非常不完善&#xff0c;输入需要手动编辑&#xff0c;凑活着可以用&#xff0c;请自己完善吧。 开源声明&#xff1a;此文代码引用了一个开源MIT License的Python库&#xff0c;其他代码是本人自写自用。你可以随…...

UDP协议实现通信与数据传输(创建客户端和服务器)

目录 一、UDP &#xff08;传输层&#xff0c;用户数据报协议&#xff09; 二、服务器Server的创建 三、客户端Client的创建 四、效果实现&#xff08;描述&#xff09; 一、UDP &#xff08;传输层&#xff0c;用户数据报协议&#xff09; UDP&#xff08;User Datagram Pr…...

【红黑树】

红黑树 小杨 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出俩倍&am…...

排序算法——简单选择排序

一、算法原理 简单选择排序是一种基本的排序算法&#xff0c;其原理是每次从未排序的元素中选择最小&#xff08;或最大&#xff09;的元素&#xff0c;然后与未排序部分的第一个元素交换位置&#xff0c;直到所有元素都被排序。 二、算法实现流程 简单选择排序法(Simple Se…...

OpenAI API推出结构化输出功能

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

Python 异步编程:Sqlalchemy 异步实现方式

SQLAlchemy 是 Python 中最流行的数据库工具之一&#xff0c;在新版本中引入了对异步操作的支持。这为使用异步框架&#xff08;如 FastAPI&#xff09;开发应用程序带来了极大的便利。在这篇文章中&#xff0c;简单介绍下 SQLAlchemy 是如何利用 Greenlet 实现异步操作的。 什…...

父类引用指向子类对象

在 Java 中&#xff0c;父类引用可以指向子类对象&#xff0c;这是多态的一种表现。这种特性允许你使用父类的引用来操作子类对象&#xff0c;从而实现更灵活和可扩展的代码设计。 基本概念 多态&#xff1a;父类引用可以指向子类对象。这使得你可以用统一的接口处理不同的对象…...

分享一个基于Spring Boot的面向社区的智能化健康管理系统的设计与实现(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…...

【扒代码】reduction参数是什么

model DensityMapRegressor(in_channels256, reduction8)reduction 参数在 DensityMapRegressor 类中用于决定模型在上采样过程中的层级配置。具体来说&#xff0c;它决定了上采样过程中使用多少个 UpsamplingLayer&#xff0c;从而影响输出的分辨率。 reduction 参数的作用 …...

Python,Spire.Doc模块,处理word、docx文件,极致丝滑

Python处理word文件&#xff0c;一般都是推荐的Python-docx&#xff0c;但是只写出一个&#xff0c;一句话的文件&#xff0c;也没有什么样式&#xff0c;就是36K。 再打开word在另存一下&#xff0c;就可以到7-8k&#xff0c;我想一定是python-docx的问题&#xff0c;但一直没…...

redis的安装与命令

一、redis与memcache总体对比 1.性能 Redis&#xff1a;只使用单核&#xff0c;平均每一个核上Redis在存储小数据时比Memcached性能更高。 Memcached&#xff1a;可以使用多核&#xff0c;而在100k以上的数据中&#xff0c;Memcached性能要高于Redis。 2.内存使用效率 Mem…...

【C++】特殊类设计类型转换

目录 &#x1f4a1;前言一&#xff0c;特殊类设计1. 请设计一个类&#xff0c;不能被拷贝2. 请设计一个类&#xff0c;只能在堆上创建对象3. 请设计一个类&#xff0c;只能在栈上创建对象4. 请设计一个类&#xff0c;不能被继承5. 请设计一个类&#xff0c;只能创建一个对象(单…...

为git 命令行 设置代理环境变量

http://t.csdnimg.cn/cAxkg 国内需要修改pinoko根目录下gitconfig文件&#xff0c;添加 [http]proxy http://127.0.0.1:1080 [https]proxy https://127.0.0.1:1080或者通过命令行配置&#xff1a; git config --global http.proxy http://127.0.0.1:1080 git config --glo…...

自定义linux某些常见配置

1.当前路径 echo "PS1\u\h:\w\$ " >> /etc/profile source /etc/profile 2.ssh使能 1.开启openssh 2.权限赋予chown root.root /var/empty/ 3.开发板作为server echo "PermitRootLogin yes" >> /etc/ssh/sshd_config 3开机自启动脚本 1.init…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...