详解#define

news2024/11/17 22:25:52

我们要知道,#define后面定义的标识符只进行替换而不进行计算,我们不能根据惯性自动给它计算了,这样可能会出错。


目录

1.关于#define

1.1#define定义标识符

1.2#define定义宏

1.3#define的替换规则

2.#和##

1.#

2.##

3.带副作用的宏参数

4.宏和函数的比较

5.命名的约定

6.#undef:可以移除宏定义


1.关于#define

1.1#define定义标识符

使用格式:#define name stuff

例如:#define MAX  1000   (这个是我们经常用到的)

          #define reg   register    为register创建一个简短的名字reg

          #define do_forever  for(  ; ; )   用更形象的符号来替换一种实现

          #define CASE break;case    写case语句的时候自动把break写上

我们可以举个代码例子来理解

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

//CASE break 来举例

#define CASE break;case//它的意思就是把原来case的位置,替换为break;case
int main()
{
	int input = 0;
	int flag = 0;
	printf("请选择:>");
	scanf("%d ", &input);
	switch (input)
	{
	case 1:
		flag = 1;
	CASE 2:  
		flag = 2;
	CASE 3:
	    flag = 3;
	CASE 4:
		flag = 4;
	default:
		break;
	}
	printf("flag=%d\n", flag);
	return 0;
}

我们可以看到,对应CASE的位置被替换为 break;case  这样我们就不必每次都写break,因为我们有时会忘记写break,这样就很方便了,是不是很奇妙。

还有一点,在define定义标识符的时候,后面一般不要加“ ;”有时会出错误

还记得我们以前说过的悬空else吗,我们来举个例子:

#define MAX 100;

int main()
{
	int m = 0;
	scanf("%d", &m);
	if (m >= 0)
		m = MAX;
	else
		m = -1;
	printf("%d\n", m);
	return 0;
}

1.2#define定义宏

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

2.宏的申明方式:#define  name(parament_list) stuff

其中: name(parament_list)是指宏的参数

        parament_list是一个由逗号隔开的符号表,它可能出现在stuff中

        stuff是宏的内容

3.参数列表的左括号必须与name紧邻,如果二者之间有任何空白存在,参数列表就会被解释为stuff的一部分

我们看个例子:

#define SQUARE(x) x*x

int main()
{
	printf("%d\n", SQUARE(5));
	printf("%f\n", SQUARE(5.0));
	return 0;
}

但是这种定义方式存在隐患,我们之前说过#define定义的内容只进行替换而不计算,如果此时,我们把5改为5+1,它的值又是多少呢?

我们不妨先猜测一下,相信大多数人都回答36,但我们试着编译一下,发现结果和我们想象中不同

#define SQUARE(x) x*x

int main()
{
	printf("%d\n", SQUARE(5+1));
	return 0;
}

我们发现结果是11,并不是我们想想中的36,我们来解释一下

SQUARE(5+1)就是 5+1*5+1 =11

所以此时,我们又得到了几个注意点:

在写宏时,我们要勇于加括号,防止代码中进行替换时,代码结果可能出现错误

当我们希望计算结果是一个整体时,建议整体给stuff加括号

例如:#define SQUARE(x)   ((x)*(x))这样就不会出现错误了

同时,宏的参数也可以是多个

例子:

#define MAX(x,y) ((x)>(y)?(x):(y))

int main()
{
	int m = MAX(100, 443);
	printf("m=%d\n", m);
	return 0;
}

1.3#define的替换规则

虽然我们前面说define时可能已经说过了它的替换规则,但是在这里还是要给大家总结一下,方便大家总结:

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

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

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

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

注意:

1.宏参数和#define定义中可以出现其他#define定义的符号,但对于宏,不能出现递归

2.当预处理器搜索到#define定义的符号的时候,字符串常量的内容并不被搜索

2.#和##

1.#

 1.#它把参数插入到字符串中

2.用#把一个宏参数变为对应的字符串

这样说可能难以理解,我们直接看代码例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

#define PRINT1(x) printf("the value of "#x" is %d\n",x)

#define PRINT2(format,x) printf("the value of "#x" is "format" \n",x)

int main()
{
	int a = 10;
	int b = 20;
	float f = 3.14f;
//#把宏参数插入到字符串中
	PRINT1(a);
	PRINT1(b);

//把宏参数变成对应的字符串
	
	PRINT2("%f", f);
	return 0;
}

2.##

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

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

但是我们要注意,这样的连接必须产生一个合法的标识符,否则其结果就是未定义的

看个代码例子:

#define CAT(x,y,z) x##y##z

int main()
{

	int iampig = 2023;
	printf("%d\n", CAT(i,am,pig));
	return 0;
}

3.带副作用的宏参数

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

#define MAX(x,y) ((x)>(y)?(x):(y))
int main()
{
	int a = 4;
	int b = 6;
	int m = MAX(a++, b++);//带有副作用的参数
	printf("a=%d  b=%d  m=%d \n", a, b,m);
	return 0;
}

我们本以为打印出来的会是5,7,6.为什么会这样呢?我们来分析一下

但如果我们把它封装成函数,它就不会有这个副作用,这是因为函数会先计算再传参,和宏不一样,只替换不计算

我们看个函数例子:

int Max(int x, int y)
{
	return (x > y ? x : y);
}
int main()
{
	int a = 4;
	int b = 6;
	int m = Max(a++, b++);
	printf("a=%d  b=%d  m=%d \n", a, b,m);
	return 0;
}

所以,接下来就引出了函数和宏的对比,让我们接着学习

4.宏和函数的比较

我们从以下七点进行比较:

1.代码长度

宏:每次使用时,宏代码都会被插入到程序中,除了非常小的宏之外,程序的长度会大幅度增长

函数:函数代码只出现于一个地方,每次使用这个函数时,都会调用那个地方的同一份代码

2.执行速度
宏:更快

函数:存在函数的调用和返回的额外开销,所以相对慢一些

3.操作符的优先级

宏:宏参数的求值是在所以周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多加括号

函数:函数参数只在函数调用的时候求值一次,它的结果值传递给函数,表达式的求值结果更容易预判

4.带有副作用的参数

宏:参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果

函数:函数参数只在传参的时候求值一次,结果更容易控制

5.参数类型

宏:宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用任何参数类型

函数:函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使它们执行的任务是相同的

6.调试

宏:宏是不方便调试的

函数:函数是可以逐语句调试的

7.递归

宏:不能递归

函数:可以递归

5.命名的约定

一般来讲,函数和宏的使用语法很相似,所以C语言没有规定它们的使用语法

我们约定俗成

宏命名就大写,函数名不要全大写

但有时宏也有小写特例,我们要注意

6.#undef:可以移除宏定义

这个函数是可以移除宏定义的,我们看个例子:


#define MAX 100

int main()
{
	printf("%d\n",MAX);//可以执行
#undef MAX
	//printf("%d\n",MAX);//不可执行
	return 0;
}

当我们#undef MAX后,在打印MXA,VS会提示我们MXA未定义,这就是它移除了宏定义。


好了,关于程序环境和预处理方面的知识,我们已经全部学完了,希望大家可以理解,关于C语言的理论方面的知识点到这里也是已经给大家全部说完了,对C语言说声拜拜。

我们下期再见!!!

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

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

相关文章

1.4 8位加法器

1.半加器 2.全加器 半加器: 完整模拟1位加法 1.A,B 接受端,接受1或0 , 2个电信号 2.异或门 做为结果: 1^10, 0^00, 1^01, 0^11 与编程中的: 异或一致 3.与门 做为进位: 1&11,1&00,0&10, 0&01 与编程中的: 与一致 4.半加器实现1位的加法运算,比如:A端: …

11 月 25 日 ROS 学习笔记——3D 建模与仿真

文章目录 前言一、在 ROS 中自定义机器人的3D模型1. 在 rviz 里查看3D模型2. xacro 二、Gazebo1. urdf 集成 gazebo2. 综合应用1). 运动控制及里程计2). 雷达仿真3). 摄像头信息仿真4). kinect 深度相机仿真5). 点云 前言 本文为11 月 25 日 ROS 学习笔记——3D 建模与仿真&am…

Vue框架学习笔记——键盘事件

文章目录 前文提要键盘事件&#xff08;并不是所有按键都能绑定键盘事件&#xff09;常用的按键不同的tab和四个按键keyCode绑定键盘事件&#xff08;不推荐&#xff09;Vue.config.keyCode.自定义键名 键码 神奇的猜想div标签和click.enterbutton标签和click.enter 前文提要 …

redhat9.3配置国内yum阿里源

由于新建的Redhat9.3在未注册激活之前是没有yum源的配置文件的&#xff0c;所以需要我们自己新建一个yum源文件的配置文件 vim /etc/yum.repos.d/aliyun_yum.repo 内容如下&#xff1a; [ali_baseos] nameali_baseos baseurlhttps://mirrors.aliyun.com/centos-stream/9-str…

BART 并行成像压缩感知重建:联合重建

本文使用 variavle-density possion-disc 采样的多通道膝盖数据进行并行重建和压缩感知重建。 0 数据欠采样sampling pattern 1 计算ESPIRiT maps % A visualization of k-space dataknee = readcfl(data/knee); ksp_rss = bart(rss 8, knee);ksp_rss = squeeze(ksp_rss); figu…

河南省第五届“金盾信安杯”网络与数据安全大赛实操技能赛 部分wp(自己的一些思路和解析 )(主misc crypto )

芜湖 不评价 以下仅是自己的一些思路和解析 有什么问题或者建议随时都可以联系我 目录 题目一 来都来了 操作内容&#xff1a; flag值&#xff1a; 题目二 Honor 操作内容&#xff1a; flag值&#xff1a; 题目三 我看看谁还不会RSA 操作内容&#xff1a; flag值&a…

Three.js 3D模型爆炸分解

你是否曾想展示一些很酷的发动机的 3D 模型? 这是一种可行的方法,使用一些非常基本的数学:一个简单的分解视图。 为此,我们将使用 React-Three-Fiber,但它可以与原生 Three.js 配合使用(它与 r3f 生态系统没有深度绑定,只是对我来说迭代速度更快)。 1、准备3D模型 首…

Java 设计模式——建造者模式

目录 1.概述2.结构3.实例3.1.产品类3.2.抽象建造者类3.3.具体建造者类3.4.指挥者类3.5.测试 4.优缺点5.使用场景6.模式扩展7.创建者模式对比 1.概述 建造者模式 (Builder Pattern) 是一种创建型设计模式&#xff0c;用于创建复杂对象。它将对象的构建过程分离成独立的部分&…

在 Linux 中重命名文件和目录

目录 前言 使用 mv 命令重命名文件和目录 通过组合 mv、find 和 exec 命令重命名与某个模式匹配的多个文件 使用 rename 命令轻松重命名多个文件 总结 前言 在这篇基本命令行教程中&#xff0c;你将学习在 Linux 终端重命名文件和目录的各种方法。 如何在 Linux 终端中重命…

从前序与中序遍历序列构造二叉树(C++实现)

从前序与中序遍历序列构造二叉树 题目思路分析代码代码讲解 题目 思路分析 我们可以通过递归实现的二叉树构建函数。它根据给定的先序遍历序列和中序遍历序列构建一棵二叉树&#xff0c;并返回根节点。可以创建一个_build 函数&#xff0c;该函数负责构建二叉树的节点&#xff…

个人测试用例以及测试金句分享

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ☁️测试必背金句 界面易用可双安&…

Ants

描述 输入 L10 n3 x{2,6,7} 输出 min4 max8思路 最短时间肯定是每只蚂蚁都朝离自己最近的端点去爬行&#xff0c;这样不会出现蚂蚁相遇的情况 最长时间肯定是每只蚂蚁都朝离自己最远的端点去爬行&#xff0c;但这样会发生蚂蚁相遇的情况 对于最长时间中相遇情况的分析 我们先…

力扣:提莫攻击

代码&#xff1a; class Solution { public:int findPoisonedDuration(vector<int>& timeSeries, int duration){//根据数组中给出的元素的值来进行判断&#xff01;//若后面元素-前面元素>d 中了d秒&#xff01;// <d 中了差的秒数&…

RNN 网络结构及训练过程简介

本文通过整理李宏毅老师的机器学习教程的内容&#xff0c;介绍 RNN&#xff08;recurrent neural network&#xff09;的网络结构。 RNN 网络结构, 李宏毅 RNN RNN 的特点在于存储功能&#xff0c;即可以记忆前面时刻的信息。 最简单的 RNN 结构如下&#xff1a; 当然&a…

MUYUCMS v2.1:一款开源、轻量级的内容管理系统

MuYuCMS&#xff1a;一款基于Thinkphp开发的轻量级开源内容管理系统&#xff0c;为企业、个人站长提供快速建站解决方案。它具有以下的环境要求&#xff1a; 支持系统&#xff1a;Windows/Linux/Mac WEB服务器&#xff1a;Apache/Nginx/ISS PHP版本&#xff1a;php > 5.6 (…

JDK源码系列:StringBuffer与StringBuilder对比

一、源码分析StringBuffer与StringBuilder的区别 1、StringBuffer是多线程安全的&#xff0c;StringBuilder是多线程不安全的 多线程安全指的是 多个线程同时对一个对象进行append 等操作&#xff0c;不会出现覆盖、丢失的情况。 看下StringBuffer是如何做到多线程安全的&#…

吉他初学者学习网站搭建系列(3)——如何实现吉他在线调音

文章目录 背景知识teoriapitchytone效果 背景知识 学过初中物理就会知道&#xff0c;声音是由空气振动产生的。振动产生波&#xff0c;所以声音就是不同振幅和频率的波构成的。振幅决定了声音的响度&#xff0c;频率决定了声音的音高。想更进一步了解的可以访问这个网页wavefo…

C++ STL map迭代器失效问题

最近在开发过程中&#xff0c;定位一个问题的时候&#xff0c;发现多线程场景下大量创建和销毁某个C:\Windows\System32\reg.exe时出现了383个进程创建消息处理的接口&#xff0c;和384个进程销毁处理消息的接口都在等待锁&#xff0c;另外一个线程也在等锁&#xff0c;后面看了…

【数据结构实验】查找(二)基于线性探测法的散列表

文章目录 1. 引言2. 实验原理2.1 散列表2.2 线性探测法 3. 实验内容3.1 实验题目&#xff08;一&#xff09;输入要求&#xff08;二&#xff09;输出要求 3.2 算法实现三、实验设计3.3 代码整合 4. 实验结果 1. 引言 本实验将通过C语言实现基于线性探测法的散列表 2. 实验原理…

车载通信架构 —— 传统车内通信网络CAN(可靠性为王)

车载通信架构 —— 传统车内通信网络CAN(可靠性为王) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非…