【C语言进阶】程序编译中的预处理操作

📚作者简介:爱编程的小马,正在学习C/C++,Linux及MySQL..

📚以后会将数据结构收录为一个系列,敬请期待

● 本期内容讲解C语言中程序预处理要做的事情

目录

1.1 预处理符号

1.2 #define

1.2.1  #define定义标识符

1.2.2 #define定义宏

1.2.3 #define替换规则

1.3 #和## 

1.4 带副作用的宏参数 

1.5 宏与函数的区别与联系


1.预处理详解

1.1 预处理符号

__FILE__     //进行编译的源文件

__LINE__    //文件当前的行号

__DATE__  //文件被编译的日期

__TIME__   //文件被编译的时间

__STDC__  //如果编译器遵循ANSIC,其值为一,否则未定义

打印出来看一看,很明显,vs2019不支持ANSIC 

1.2 #define

1.2.1  #define定义标识符

 语法:#define name stuff 

#define MAX 100     //定义MAX的值为100
#define reg register //为register这个关键字创建简短的名字
#define ro for(;;)   //打印for这个死循环
#define CASE break;case  //在case语句后换成break;case
#define debug_print printf("file:%s\tline:%d\t\date:%s\ttime:%s\t",__FILE__,__LINE__,__DATE__,__TIME__);   
#include<stdio.h>
int main()
{printf("%d\n", MAX);debug_print;//ro//{//	//printf("hehe\n");//}//int a = 0;//switch (a)//{//case1:  //不推荐这种写法,可读性太差//CASE2://CASE3://}return 0;
}

提问:

 在定义#define的时候,是否要加上;号

比如:

#define MAX1 100
#define MAX2 100;
#include<stdio.h>
int main()
{printf("%d\n", MAX1);printf("%d\n", MAX2);return 0;
}

究竟是MAX1可以还是MAX2可以呢?看看运行结果

解析:实际上在预处理的时候,会将#define定义的标识符进行替换,直接替换成表达式。比方说,MAX1处会被直接替换为100,MAX2处会被替换为100;在这个地方语句就出问题了,怎么两个分号呢?所以MAX2是不能这么写的。

1.2.2 #define定义宏

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

宏的定义:

#define name(parament-list) stuff 

如果两者之间有任何空白存在,那么参数列表就会解释为stuff的一部分

举个例子:

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

上述代码输出一定是25,因为在预处理过程中会直接替换,int c = a*a ,那么我再问问大家,如果我传的是a+5呢? 

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

 

解析:为什么我传递的是a+5是10,应该是输出100的,为什么最后打印了一个35?原因是这样的,还是替换 int c = a+5*a+5 ,输出就是35,怎么解决呢,要加括号。 

加上括号之后,就对了。

但是呢,我又突发奇想 ,又问大家一个问题,看下面这个代码,会输出什么?

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

按理来说,调用DOUBLE这个宏应该是2x也就是20,再乘10就是200。那我们来看看是否是输出200呢?

 解析:可以看到输出的是110,并不是200,为什么呢?其实上就是替换 int c = 10*(a+5)+(a+5),实际上是输出110而不是我们想要的200,怎么办呢?其实还是加括号,再宏定义处再加括号即可

所以呢在创建宏的时候,一定要不要吝啬括号,一定要多加括号。

1.2.3 #define替换规则

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

1、在调用宏时,首先对参数进行检查,看看是否任何由#define定义的符号。如果是,它们首先被替换。

2、替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换

3、最后,再次对结果文件进行扫描,看看他是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注意:

1、宏函数和#define定义中可以可以出现其他#define定义的符号。但是对于宏,不可以递归

2、当预处理器搜索宏的时候,字符串常量不被搜索。

可以看个例子:

#define DOUBLE(x) ((x)+(x))
#include<stdio.h>
int main()
{printf("DOUBLE(5)");
}

 

1.3 #和## 

(1)首先我们一起来看这样一段代码

#include<stdio.h>
int main()
{char* p = "hello bit\n";printf("%s", p);printf("hello ""bit");return 0;
}

 

可以看到,字符串是有自动连接的特点的。那么我有个设想,是说我们可以根据数据类型来打印对应的数据,比方说我给一个宏传 3 整形,他就可以给我打印出3;比方说我给一个宏传3.5这个浮点型,就可以给我打印出3.500000,看我如何实现

#define PRINT(FORMAT,VALUE) printf("the value of "#VALUE" is "FORMAT"\n",VALUE);
#include<stdio.h>
int main()
{int a = 10;//printf("the value of a is %d\n", a);PRINT("%d",a);return 0;
}

这个代码是可以这么理解,就是宏参数前面加上#号,可以把一个宏参数变成对应的字符串。

这个代码的输出结果就是,PRINT("%d",a)会被替换为printf("the value of "a" is "%d"\n",10)

 (2)##

##的作用是:

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

2、它允许宏定义从分离的片段创建标识符

 举个栗子:

#include<stdio.h>#define ping(num1,num2) num1##num2 
int main()
{int chinano1 = 2024;int r = ping(china,no1);printf("%d", r);return 0;
}

 

解析:宏会直接替换,int r = chinano1,所以最后会打印出2024。

1.4 带副作用的宏参数 

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

副作用是什么呢?例如:

int x = 5;
//我想让y变成6,有几种方法
//1、
int y = x+1 //无副作用
//2、
int y = ++x; //有副作用的

举个栗子来证明下如果是带副作用的宏参数,会有什么问题:

#include<stdio.h>
#define MAX(a,b) ((a)>(b)?(a):(b))
int main()
{//传递有副作用的宏参数int a = 5;int b = 6;int c = MAX(a++,b++);printf("a = %d\n", a);printf("b = %d\n", b);printf("c = %d\n", c);return 0;
}

输出结果:

 

为什么是这个结果,不是6和7比较返回7吗?其实不是的,因为你调用了宏就是替换,

int c = (a++)>(b++)?(a++):(b++),可以看到,宏参数每往后走一步就加加,如果一直反复调用实际上是很危险的。

1.5 宏与函数的区别与联系

属性#define定义的宏函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,其他的代码量会大幅增长函数代码只出现在一个地方,每次调用,都去那一个地方找
执行速度更快存在函数的调用与返回,要慢一些
操作符优先级宏的求值是要在上下文联系中的,除非加上括号,否则会产生不可改变的后果函数参数只在函数调用的时候求值一次,函数表达式结果更好预测
带有副作用的参数参数可能被替换到任意位置,所以带有副作用的参数会带来不可预料的结果

函数参数只在传参的时候求值一次,因此表达式结果更好预测

参数类型宏的参数与类型无关,只要表达式合法,都可以用函数参数对类型要求十分严格,如果函数类型不同,就需要不同的函数参数,即使他们执行的类型是相同的。
调试宏是不方便调试函数是可以逐语句调试的
递归宏是不能递归的函数是可以递归的

 


  总结

上文就是C语言编程预处理阶段的相关知识了,C语言进阶系列正式完结!!!

如果这份博客对大家有帮助,希望各位给小马一个大大的点赞鼓励一下,如果喜欢,请收藏一下,谢谢大家!!!
制作不易,如果大家有什么疑问或给小马的意见,欢迎评论区留言。

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

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

相关文章

数据结构---线性表(顺序表)附代码

目录&#xff1a; 数据结构相关概念 1、什么是数据结构&#xff1f; 2、为什么需要数据结构&#xff1f; 顺序表 1、顺序表的概念及结构 1.1 线性表 1.2 顺序表 2、顺序表分类 3、动态顺序表的实现 什么是数据结构&#xff1f;&#xff1f; 数据结构是由 “数据”和 …

Unity 合并子物体获得简化Mesh

合并子物体获得简化Mesh &#x1f959;环境&#x1f96a;Demo &#x1f959;环境 PackageManager安装Editor Coroutines 导入插件&#x1f448; &#x1f96a;Demo 生成参数微调&#xff1a;Assets/EasyColliderEditor/Scripts/VHACDSettings/VHACDSettings.asset

MATLAB数值类型

MATLAB 数值 MATLAB支持各种数字类&#xff0c;包括有符号和无符号整数以及单精度和双精度浮点数。默认情况下&#xff0c;MATLAB将所有数值存储为双精度浮点数。 您可以选择将任何数字或数字数组存储为整数或单精度数字。 所有数值类型都支持基本数组运算和数学运算。 转换…

pyqt拖入图片并显示

pyqt拖入图片并显示 介绍效果代码 介绍 像拖入文本一样&#xff0c;把图片拖入到窗体中显示。 效果 代码 import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout from PyQt5.QtGui import QPixmap, QDragEnterEvent, QDropEvent from PyQt5.Q…

Ollama配置webui连接大预言模型

Ollama配置Web UI连接大预言模型 默认ollama安装后&#xff0c;chat对话只有命令行界面&#xff0c;交互体验较差。借助open-webui可以通过web界面连接ollama&#xff0c;从而实现类似chatgpt式的web交互体验。 使用家用PC实践记录如下&#xff1a; 1. 环境配置 本次使用的操作…

笔记-mathtype公式在PDF或打印出来显示不全

原文中的公式&#xff1a; 纸质版打印出来的公式有缺失 问题描述&#xff1a;mathtype公式编辑器所编辑的公式转成PDF或者打印出来有缺失 以下是解决方法的具体描述。 目录 一、准备工作二、操作步骤 一、准备工作 1、工具&#xff1a;mathtype、微软word 二、操作步骤 …

数据可视化在不同行业中有哪些应用?

数据可视化即通过图表的形式将数据的内在信息有逻辑性地呈现给用户&#xff0c;使用户更容易发现数据中蕴藏的规律&#xff0c;找出问题&#xff0c;进而做出决策&#xff1b;另一方面&#xff0c;数据可视化项目也是一张重要的名片&#xff0c;是企业数字化建设效果的呈现。本…

el-select下拉框修改背景色

效果图&#xff1a; 1.el-select标签添加teleported属性&#xff0c;并设置为false 2.设置css样式

偏微分方程算法之九点紧差分法

目录 一、研究目标 二、理论推导 三、算例实现 四、结论 一、研究目标 我们已经在专栏中介绍了椭圆型偏微分方程的五点菱形差分格式&#xff0c;这里我们继续以该方法为背景&#xff0c;探讨如何提高五点法的精度&#xff0c;即从二阶精度提升到四阶精度。 研究目标现继续以…

qt5-入门-2D绘图-基础

参考&#xff1a; QPainter_w3cschool https://www.w3cschool.cn/learnroadqt/k7zd1j4l.html C GUI Programming with Qt 4, Second Edition 本地环境&#xff1a; win10专业版&#xff0c;64位&#xff0c;Qt 5.12 代码已经测试通过。其他例子日后更新。 目录 基础知识penb…

高德地图API入门使用vue

文章目录 最终效果一、在高德的开放平台申请key二、下载依赖&#xff1a;三、完整代码 最终效果 页面显示高德地图 一、在高德的开放平台申请key 在高德的开放平台申请key&#xff1a; https://console.amap.com/dev/key/app 申请的类型为web端&#xff08;js api&#xff…

LeetCode45:跳跃游戏Ⅱ

题目描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n …

如何防止源代码泄露?6种企业防泄密解决方案

在数字化转型浪潮中&#xff0c;源代码成为企业宝贵的核心资产&#xff0c;其安全性直接关系到企业的生存和发展。源代码泄露不仅会导致商业秘密外泄&#xff0c;还可能造成严重的经济损失和品牌信誉下降。为此&#xff0c;采用高效的防泄密措施&#xff0c;如华企盾DSC数据防泄…

php7.4在foreach中对使用数据使用无法??[]判读,无法使用引用传递

代码如下图&#xff1a;这样子在foreach中是无法修改class_history的。正确的应该是去掉??[]判断。 public function actionY(){$array [name>aaa,class_history>[[class_name>一班,class_num>1],[class_name>二班,class_num>2]]];foreach ($array[class_…

Linux环境下的编译和调试

本文目录 一、编译1. gcc/g编译器2. gcc/g安装3. 代码编译过程4. gcc编译 二、调试1. 下载gdb调试器2. gdb 调试器使用步骤 一、编译 1. gcc/g编译器 对于.c 格式的 C 文件&#xff0c;可以采用 gcc 或 g编译。 对于.cc、.cpp 格式的 C文件&#xff0c;应该采用 g进行编译。 …

密文域可逆信息隐藏技术综述(上)

加密图像可逆信息隐藏是一种加密原始图像后&#xff0c;在密文图像中可逆地隐藏附加数据&#xff0c;并且在数据提取后&#xff0c;原始图像可以被无损重建的技术。RDH-EI的分类如图1所示。 按对图像的加密方法&#xff0c;现有RDH-EI算法可分为对称加密域和非对称(公钥)加密域…

form1弹出子窗体form2,拖动子窗体判断是否离开父窗体区域,含源码(学习笔记)

一、效果&#xff08;进入和离开&#xff09; 子窗体到达父窗体边缘时变色。 二、代码分析 判断父窗体的目的&#xff0c;可以控制子窗体要随父窗体走。上面代码需要加以处理。 如&#xff1a;this.Location new Point(parentPoint.X distanceFromEdge, this.Location.Ydis…

抖音上线“星绘”APP,它有着什么样的特殊之处?

抖音上线“星绘”APP 前言 就在4月25日&#xff0c;抖音在App Store上线了一款名为“星绘”的App。该App以妙鸭相机为对标产品&#xff0c;拥有着强大的AI生图能力&#xff0c;而产品中的“AI分身”也十分有特色&#xff0c;那么这款“星绘”App究竟如何呢&#xff1f;我们接着…

2024最新UI发卡盗U/支持多语言/更新UI界面/支持多个主流钱包

本文来自&#xff1a;2024最新UI发卡盗U/支持多语言/更新UI界面/支持多个主流钱包 - 源码1688 应用介绍 简介&#xff1a; 2024最新UI发卡盗U/支持多语言/更新UI界面/支持多个主流钱包 自行检查后门&#xff0c;最好是部署智能合约后用合约地址来授权 包含转账支付页面盗U授…

Dokcer容器分布式搭建LNMP+wordpress论坛

目录 引言 一、架构环境 二、搭建容器 &#xff08;一&#xff09;自定义网络 &#xff08;二&#xff09;搭建nginx容器 1.文件准备 2.查看与编辑文件 3.生成镜像 4.创建容器 &#xff08;三&#xff09;搭建MySQL容器 1.文件准备 2.查看与编辑文件 3.生成镜像 …