预处理详解

news2024/11/26 0:51:14

目录

一、预定义符号

 二、#define

1.认识#define

2.使用#define

2.1#define定义常量

2.2#define定义宏

 2.3#define的替换规则

三、宏定义的其他内容

1.#和##

1.1#

1.2##

 2.宏的副作用

3.宏的命名规则

4.undef

 5.条件编译


一、预定义符号

#include<stdio.h>
int main()
{
	printf("%s %s", __DATE__,__TIME__);
	//将程序编译的日期和时间打印出来
}

 二、#define

1.认识#define

#define是c语言中的一个预处理命令,值得一提的是,预处理命令在编译代码的时候是通过替换的方式将代码编译过去的,但由于笔者用的是vs2019编译器,并不可以展示这一过程,但我们可以通过代码的计算来证明这一点。

2.使用#define

2.1#define定义常量

#include<stdio.h>
#define x 99
int main()
{
	int a = x;
	int arr[x] = { 0 };
	//在c语言中对数组长度的定义只能用常量
	//可知#define可以定义常量
	printf("%d\n", x);
	int i = 0;
	for (i = 0; i < x; i++)
	{
		arr[i] = i;
		//简单的赋值
	}
	for (i = 0; i < x; i++)
	{
		printf("%d ",arr[i]);
		//打印内容
	}
}

2.2#define定义宏

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

下面是宏的申明方式:
#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中

值得注意的一点就是参数列表的左括号必须要与name紧紧挨着,如果它们中间有空格,那么这个符号表就会被看作stuff的部分

#include<stdio.h>
#define abc(x) x*x
//定义一个宏,宏的名字为abc,参数为x,执行的操作是x*x
int main()
{
	int a = 3;
	printf("%d",abc(a));
}


就这样,我们成功地定义了一个宏并使用了宏,但我们应该注意的一点就是,在使用宏的时候,其实是在替换,编译器不会那么智能,它只是做替换操作,比方说这串代码

#include<stdio.h>
#define abc(x) x*x
//定义一个宏,宏的名字为abc,参数为x,执行的操作是x*x
int main()
{
	printf("%d",abc(3+1));
}

你大可以猜一猜这个会打印出什么东西,初学者肯定会脱口而出,4*4嘛,16,很合理,但编译器不会这样去算,编译器是先将内容替换为3+1*3+1,再计算,打印出来的显然是7


 要满足我们所需要的操作,也很简单,多加几个括号就行

#define abc(x) ((x)*(x))

 这样它把3+1替换过去就是((3+1)*(3+1)),百分之百是16

 2.3#define的替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

三、宏定义的其他内容

1.#和##

#include<stdio.h>
int main()
{
	printf("abcdef\n");
	printf("abc""def""\n");
}

根据这一串代码我们可以知道,字符串是有自动连接特点的,说完了这一点,接下来就来教学一下如何使宏中的参数也作为字符串打印。

1.1#

#在宏中可以使参数转化为字符串的形式,而根据字符串自动连接的特点,我们就可以实现一些很方便的操作。要注意的一点则是,只有宏参数才能够被#转化为字符串

先不讲真正的例子,先简单的使用一下

#include<stdio.h>
#define test(x,y) printf("%d"#y"acb",x)
int main()
{
	test(5, 123);
}

 

我们可以看到传递过去的123被当作字符串打印出来了,接下来笔者举一个能真正使用这项功能的例子。

#include<stdio.h>
#define print1(x,y) printf("这项装备名叫"#x"价值"#y"\n")
int main()
{
	print1(小明, 100);
	print1(小黄, 200);
	print1(小红, 300);
}

 

通过这串代码我们就可以轻松的实现打印不同装备名字和价值的操作,不需要写那么多次printf,写得又臭又长还不好看。

1.2##

##的作用是将两个符号合成为一个符号,注意##只能够在宏中使用。直接举例

#include<stdio.h>
#define abc(x,y) x##y=3
//##将x,y合成变成了xy因此这串代码的意思是给xy赋值为3
//当然x,y只是代号
int main()
{
	int xy = 0;
	int abc = 0;
	abc(x,y);
	abc(ab, c);
	printf("xy=%d abc=%d\n", xy, abc);
}

 2.宏的副作用

1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
2. 宏是没法调试的,因为在编译过程中,计算机已经把程序中带有宏的内容都根据宏的规则替换为了相应的代码,也就是说,你去调试的话你是看不到具体的变化的,因为这个变化体现在编译过程3. 宏由于类型无关,也就不够严谨。
4. 宏可能会带来运算符优先级的问题,导致程容易出现错。

3.宏的命名规则

在使用宏的时候我们按照规定将宏的所有字母都大写,而函数名则不会将所有的字母都大写。这是约定俗成的规定,也可以称为程序猿的默契。

4.undef

undef的作用则是用来移除一个已经定义好的宏

比方说这串代码,我将a这个宏移除,然后再使用它,编译器就会在编译的过程中报错,提醒你没有定义a,其实undef最主要的作用是给宏重新定义,因为得先移除才能够重新定义。

 5.条件编译

#if可以理解为满足条件之后才会触发后面的内容,而#endif则是结束这次条件编译,#ifdef的含义则是如果这个宏被定义过(还可以写作#if defined)则执行后面的操作。#ifndef的含义则是如果这个宏没被定义过(还可以写作if !defined)则执行后面的操作。

多个分支的条件编译也是可以实现的

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

条件编译可以实现在不同的情况下进行不一样的编译,笔者猜测一款游戏要能够兼容不同的系统,它一定要根据不同的系统进行条件编译,毕竟环境已经不一样了。

条件编译最常用的用法就是避免头文件被多次包含

#ifndef __abc__
#define __abc__
#include<stdio.h>
#endif
//这串代码的含义就是如果__abc__没被定义则定义__abc__,同时引用头文件stdio.h
//如果我们在别的文件中也这样写,便只可能引用一次stdio.h
//__abc__是笔者随便弄的一个名字

而如果你嫌麻烦,你可以用#pragma once这个也可以保证你的文件内容只被引用一次,但是这个写法偏现代,也就是它可能不兼容老版本的编译器。

好了,今天的分享到这里就结束了,感谢各位友友的来访,祝各位友友前程似锦O(∩_∩)O 

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

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

相关文章

通过FPGA实现基于RS232串口的指令发送并控制显示器中目标位置

目录 1.算法理论概述 串口通信模块 指令解析模块 位置控制模块 显示器驱动模块 2.部分核心程序 3.算法运行软件版本 4.算法运行效果图预览 5.算法完整程序工程 1.算法理论概述 通过FPGA实现基于RS232串口的指令发送并控制显示器中目标位置是一种常见的应用场景&#x…

双向不循环链表的认识和基础操作(节点创建,头插头删,尾插尾删,输出和逆置)

头定义&#xff1a; typedef char datatype[20];//datatypechar[20] typedef struct Node {//数据域 数据元素datatype data;//指针域 下一个节点地址struct Node* next;//指针域 上一个节点地址struct Node* prev; }*DoubleLink; 创建节点&#xff1a; DoubleLink create_n…

校园电气安全风险分析及预防措施 安科瑞 许敏

摘要:校园属于人员密集场所&#xff0c;若安全风险排查、管控不到位&#xff0c;可能导致安全事故发生&#xff0c;造成严重事故后果。校园电气设备设施引起的电气火灾和触电等事故&#xff0c;是构成校园安全威胁之一&#xff0c;笔者通过对校园发生的电气安全事故案例原因分析…

一次线上OOM问题的个人复盘

我们一个java服务上线后&#xff0c;偶尔会发生内存OOM(Out Of Memory)问题&#xff0c;但由于OOM导致服务不响应请求&#xff0c;健康检查多次不通过&#xff0c;最后部署平台kill了java进程&#xff0c;这导致定位这次OOM问题也变得困难起来。 最终&#xff0c;在多次review代…

react目录结构

比较全面的react目录结构。 目录详解 assets&#xff1a;放置原始资源文件。 components&#xff1a;存放全局组件。 contants&#xff1a;常量文件夹&#xff0c;存放常量。 i18n&#xff1a;i18n国际化&#xff0c;各种语言的翻译。 pages&#xff1a;页面文件夹。 r…

es添加索引命令行和浏览器添加索引--图文详解

一、添加索引 创建索引 curl -X PUT "localhost:9200/my-index-00001?pretty" 获取索引 curl -X GET "localhost:9200/my-index-000001?pretty" 获取全部的索引 curl -X GET "http://localhost:9200/_cat/indices?v" 获取索引映射 cur…

2023 Pycharm 给项目配置解释器 基于已经创建的conda虚拟环境

我在2019年开始使用Pycharm作为python的IDE&#xff0c;最近配置解释器时&#xff0c;法线网上的方法大概过时了&#xff0c;自己尝试了好多次才发现新版本的Pycharm的解释配置方法&#xff0c;故记于此 背景描述&#xff1a; 我是用conda管理环境的&#xff0c;我已经创建好一…

C++ 单例模式(介绍+实现)

文章目录 一. 设计模式二. 单例模式三. 饿汉模式四. 懒汉模式结束语 一. 设计模式 单例模式是一种设计模式 设计模式(Design Pattern)是一套被反复使用&#xff0c;多数人知晓的&#xff0c;经过分类的&#xff0c;代码设计经验的总结。 为什么要有设计模式 就像人类历史发展会…

Docker容器网络和资源管理控制

Docker容器网络 一、Docker 网络实现原理二、Docker 的网络模式网络模式详解&#xff1a;①host模式②container模式③none模式④bridge模式⑤自定义网络 三、资源控制Ⅰ、CPU资源控制Ⅱ、对内存使用的限制Ⅲ、对磁盘IO配额控制&#xff08;blkio&#xff09;的限制 一、Docker…

前端vue uni-app仿美团下拉框下拉筛选组件

在前端Web开发中&#xff0c;下拉筛选功能是一种非常常见的交互方式&#xff0c;它可以帮助用户快速选择所需的选项。本文将介绍如何利用Vue.js和uni-app框架来实现一个高效的下拉筛选功能。通过使用这两个强大的前端框架&#xff0c;我们可以轻松地创建具有响应式用户操作的下…

Hbuildx下载内置浏览器失败

问题描述 刚开始接触Hbulidx&#xff0c;在下载内置浏览器时&#xff0c;报错 " Hbulidx内置浏览器下载失败 " 原因分析 从网上搜索相关的解决方法&#xff0c;一般都是说检查网络&#xff0c;或者关闭杀毒软件。这并没有解决问题&#xff0c;所以&#xff0c;我就…

【C++】STL之容器适配器——使用deque适配stack和queue

个人主页&#xff1a;&#x1f35d;在肯德基吃麻辣烫 分享一句喜欢的话&#xff1a;热烈的火焰&#xff0c;冰封在最沉默的火山深处。 文章目录 前言一、什么是容器适配器&#xff1f;二、stack的基本函数和模拟实现三、queue的基本函数和模拟实现 四、deque4.1deque的底层结构…

如何解决 Git 合并冲突

在遇到合并冲突时&#xff0c;请不要惊慌。通过一些娴熟的技巧协商&#xff0c;你可以解决任何冲突。 假设你和我正在共同编辑同一个名称为 index.html 的文件。我对文件进行了修改&#xff0c;进行了提交&#xff0c;并将更改推送到 Git 远程仓库。你也对同一个文件进行了修改…

K8S初级入门系列之六-控制器(RC/RS/Deployment)

一、前言 在第一章我们了解到&#xff0c;如果master节点是一个大脑&#xff0c;那么控制器组件就是大脑的总管&#xff0c;用来控制Pod的状态和行为。今天我们就来认识弹性扩缩容相关的控制器ReplicationController&#xff0c;ReplicaSet&#xff0c;Deployment。 二、Repli…

(汽车MCU)FS32K148UJT0VLLT、FS32K148HAT0MLLT、FS32K148UJT0VLUT基于32位Cortex-M4F内核 架构图

S32K 32位汽车通用微控制器 (MCU) 是一系列符合AEC-Q100标准、基于32位ARM Cortex-M4F内核的可扩展MCU&#xff0c;适用于通用汽车和高可靠性工业应用。这些系列提供具有可扩展性的软硬件兼容系列&#xff0c;有多种性能、存储器和特性可供选择。这些MCU提供ISO、CAN FD、CSEc硬…

如何使用curl下载github代码

首先通过chrome打开想要下载的源文件 如图&#xff0c;有那个下载图标时表示不需要鉴权即可下载&#xff0c;一般仓库都会开放只读权限&#xff0c;所以很大概率都有 比如我想下载这个crc32.c文件 那么我就需要知道它在哪个IP中&#xff0c;按下F12打开网络&#xff0c;点击下载…

qt学习——QMainWindow、基本控件

初识qt **QMainWindow——菜单栏和工具栏****状态栏、铆接部件、核心部件****资源文件得添加****模态和非模态对话框创建****消息对话框****其它标准对话框****登录窗口布局****控件按钮组****QListWidget控件****QTreeWidget树控件****QTableWidget控件****其他常用控件介绍**…

踩坑_vertical-align

目录 问题&#xff1a;vertical-align属性语法父元素的基线怎么找呢&#xff1f;特殊元素的基线行盒 解决 问题&#xff1a; 今天在做一个需求时遇到了如下问题&#xff1a; 代码 <style>*{margin:0;padding:0;}#app{width: 300px;height: 117px;background: #FFFFFF;bo…

验证回文串(力扣)双指针 JAVA

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s&#xff0c;如果它是 回文串 &#xff0c;返回 true &#xff1b;否则&#xf…

访客报警定位管理系统:提升安全管理水平的创新解决方案

在当前日益复杂的安全环境下&#xff0c;保障人员安全、提高安全响应能力和管理效率成为了各行各业的首要任务。 作为一种先进的安全管理解决方案&#xff0c;访客报警定位管理系统凭借其独特的优势和广泛的应用场景&#xff0c;正逐渐成为各行业安全管理的重要工具。 那么&a…