#define的使用

article/2025/9/24 21:54:43

目录

前言

1. #define定义标识符

2. #define定义宏

3. #define 替换规则 

4. 宏和函数对比 


前言

在很多C程序中,我们会在一个源文件的开头看到 #define 的字眼,和define的英文解释相同——可以理解为给xxx下定义,这里可以是标识符甚至是一个函数,接下来的内容将会对#define进行详解

1. #define定义标识符

使用#define可以用来定义标识符,所谓标识符可以简单理解为一个能够表示一定内容的符号,其格式为

#定义   标识符 内容
#define name  stuff

简单介绍几个例子

#define MAX 100            // 将 MAX 这个标识符和 100 这个数字关联起来
#define REG register       // 为 register 这个关键字,创建一个简短的名字REG
#define STR "test_string"  // 用 STR 这样一个名字来代替 test_string 这样一个字符串int main()
{REG int a = MAX;       // 这里的 reg 被解释成 register关键字,MAX 被解释成 100printf("%d\n", a);printf("%s\n", STR);     return 0; 
}

这样一段代码的输出是

由于预先对标识符进行了定义,在程序的预处理阶段, 编译器会将所有定义的标识符替换成相应的内容并生成 .i 文件,使用vs打开 .i 文件,可以看到我们的源代码在经过预处理后变成了下面的代码

int main()
{register int a = 100;       printf("%d\n", a);printf("%s\n", "test_string");return 0;
}

可以直观地看出,#define定义标识符的本质就是替换 


2. #define定义宏

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

#define 定义宏可分为两种

  1. 一种是不带参数的宏定义,这也就是第1小节提到的使用#define定义标识符
  2. 第二种是带参数的宏定义,其定义格式如下
#定义   宏名(参数表)           内容
#define name( parament-list ) stuff

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

#define ADD(a,b) a + b

这个例子定义了一个名为ADD的宏 ,宏的参数有两个 a 和 b,这个宏所要实现的功能是将 a 和 b 相加

如何使用这个宏呢?对比下面两段代码

#define ADD(a,b) (a + b)int main()
{int a = 10;int b = 20;int sum = ADD(a, b);printf("%d\n", sum);return 0;
}
int ADD(int x, int y)
{return x + y;
}int main()
{int a = 10;int b = 20;int sum = ADD(a, b);printf("%d\n", sum);return 0;
}

其实,宏的使用和我们已知的C语言中的函数类似,都需要进行传参,对比函数版本的求两数之和,可以看到在main函数中,两者的实现方式是几乎一样的,但在具体的实现方式却很不一样

宏定义的程序中,预处理器会将ADD(a,b)替换成 (a + b),.i文件中的代码为

int main()
{int a = 10;int b = 20;int sum = (a + b);printf("%d\n", sum);return 0;
}

宏定义是将 a + b这样一个求和操作重新命名并置于main函数中,而ADD函数是通过函数调用来实现求和,使用了新的函数栈帧 

注意 

  • 参数列表的左括号必须与name紧邻
  • 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分 
#define ADD (a,b)在这个宏定义中,ADD与()并不直接相连,中间存在空格,因此(a,b)不是宏ADD的参数,而是变成了stuff

考虑下面的代码

#define SQUAREA(x) (x * x)
#define SQUAREB(x) ((x) * (x)) int main()
{int ansa = SQUAREA(3 + 1);int ansb = SQUAREB(3 + 1);printf("%d\n%d\n", ansa, ansb);return 0;
}

ansa 和 ansb 的输出分别是 7 和 16

这一点似乎出人意料,实际上,宏就是替换,并且是相当直接的替换,上述代码中最重要的一行代码被分别解释为

int ansa = (3 + 1 * 3 + 1)     // 7
int ansb = ((3 + 1) * (3 + 1)) // 16

因此,所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用

3. #define 替换规则 

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

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

注意 

  1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归
  2.  当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索

4. 宏和函数对比 

一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。那我们平时的一个习惯是: 

  • 把宏名全部大写
  • 函数名不要全部大写 

宏通常被应用于执行简单的运算,比如在两个数中找出较大的一个 

#define MAX(a, b) ((a)>(b)?(a):(b))

那为什么不用函数来完成这个任务?原因有二: 

  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹 
  2. 更为重要的是函数的参数必须声明为特定的类型。
    所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的

宏的缺点:

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度
  2. 宏是没法调试的
  3. 宏由于类型无关,也就不够严谨
  4. 宏可能会带来运算符优先级的问题,导致程容易出现错

宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到

#define MALLOC(num, type)\
(type *)malloc(num * sizeof(type))
...
//使用
MALLOC(10, int);//类型作为参数
//预处理器替换之后:
(int*)malloc(10 * sizeof(int));

宏和函数的对比 

属性#define定义宏 函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码
执行速度更快存在函数的调用和返回的额外开销,所以相对慢一些
操作符优先级宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测
带有副作用的参数参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果函数参数只在传参的时候求值一次,结果更容易控制
参数类型宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是不同的
调试宏是不方便调试的函数是可以逐语句调试的
递归宏是不能递归的 函数是可以递归的

http://chatgpt.dhexx.cn/article/EOtlK2TB.shtml

相关文章

#define 的使用

#define 多数情况下我们是用来定义宏的,但是实际上,#define 作为预处理指令,也可以用来定义标识符,类似于 typedef,但是其本质和 typedef 是不同的。 目录 一、#define 定义标识符 1、定义和使用 2、#define 定义标…

C语言之#define用法入门详解

一、#define的基本语法 在C语言中,常量是使用频率很高的一个量。常量是指在程序运行过程中,其值不能被改变的量。常量常使用#define来定义。 使用#define定义的常量也称为符号常量,可以提高程序的运行效率。 其常见的用法包括两种&#xff0…

python微信聊天机器人_Python搭建一个微信聊天机器人

前言 因为班群里总是挺静的,所以想着要不放个机器人试试能不能活跃活跃气氛,然后就begin网上查阅资料试着建立个机器人。 语言环境开发环境:Ubuntu16.04 编程语言:Python2.7 步骤第一步:首先安装python2.7语言 su…

Python大佬用20行代码带你打造一个微信聊天机器人(附代码)

近来,打开微信群发消息,就会秒收到一些活跃分子的回复,有的时候感觉对方回答很在理,但是有的时候发现对方的回答其实是驴唇不对马嘴,仔细深究发现,原来对方是机器人。今天,小编就带大家用20行代…

16行代码实现微信聊天机器人,自动智能回复,打团了让它来陪老婆聊天

在我们的生活和工作当中,很多时候我们并不能及时地回复消息,尤其是业务比较多的人,客户给我们发消息我们不回又不好,但又没有那么多精力时时回复,这个时候智能机器人就能帮助我们解决很多问题。 像电商类的客服,像大的QQ群/微信群管理员,以及我们打游戏的时候,挂着脚本…

利用python实现微信聊天机器人(需自己设置关键字及回复内容)

本人为python小白写此帖纪念用python写的第一个脚本 微信聊天机器人 1、通过查找截图进行鼠标点击操作 2、通过直接输入屏幕中的坐标进行鼠标点击操作(在复制他人最新信息时采用指定坐标双击) # 双击并复制信息 pyautogui.doubleClick(394,…

php个人微信聊天机器人,wxpy将个人微信号变成微信聊天机器人

一、实验环境:win10 python3 wxpy 微信版本:6.6.5 1.1、从官网下载python最新版本并进行安装。 1.2、进入python安装目录Scripts/文件夹下,使用easy_install.exe pip 命令安装pip 1.3、使用pip install wxpy 命令安装wxpy 二、源码文件demo…

一个15分钟的视频,教你用Python创建自己的微信聊天机器人文中赠送全部源代码

你好,我是程序员晚枫。 在社区交流群(👉传送门)里的朋友都知道,社区有一个智能聊天机器人: 1、24小时在线的智能客服, 2、根据大家的提问,自动回复对应的学习资料, 3、对…

微信聊天机器人

做一个微信聊天自动回复的机器人 # -*- coding: utf-8 -*- """ Created on Tue Jun 4 18:30:39 2019author: yyp """#-*- coding:utf-8 -*- import itchat import requestsdef get_response(msg):apiurl http://i.itpk.cn/api.php #//moli机器…

Python Wechaty 微信聊天机器人 padlocal协议搭建指南

python版本推荐3.9 微信机器人框架:Wechaty Python-Wechaty中文手册 开发前需要准备: Linux服务器(CentOS 8.0,Python环境,Docker)本地Python开发环境(我个人使用VSCode)申请一个…

用Python写了一个微信聊天机器人

在我们的生活和工作当中,很多时候我们并不能及时地回复消息,尤其是业务比较多的人,客户给我们发消息我们不回又不好,但又没有那么多精力时时回复,这个时候智能机器人就能帮助我们解决很多问题。 像电商类的客服&#…

Python创建微信聊天机器人

最近想着做一个微信机器人,主要想要实现能够每天定时推送天气预报或励志语录,励志语录要每天有自动更新,定时或当有好友回复时,能够随机推送不同的内容。于是开始了分析思路。博主是采用了多线程群发,因为微信对频繁发…

三步轻松打造微信聊天机器人(附源码)

最近微信公众平台开发是热门,我也跟风做了一个陪聊的公众号。 其实类似的自动回话程序早就有了,比如前一阵很火的小黄鸡(还是小黄鸭来着?)。但尽管是跟风,也要体现一些不同。别人做的都是中文陪聊&#xff…

实现微信聊天机器人-中级篇

一、ChatterBot工作原理 Chatbot Engine的技术框架采用了开源项目ChatterBot (https://github.com/gunthercox/ChatterBot)。 ChatterBot是一个python库,利用它可以轻松的创建对话软件。 一个未经训练的ChatterBot实例开始不知道如何沟通,每次用户输入一个语句,ChatterBo…

微信聊天机器人,不使用iChat,可以群聊

目录 1. 微信聊天界面截图 2. 图片文字识别 3. 获取最新消息 3.1 独聊 3.2 群聊 4. 机器人聊天系统 5. 成果展示 6. 全部代码 本文参考大神【喵王叭】的文章:python实现微信、QQ聊天自动回复【纯物理】_喵王叭的博客-CSDN博客_python自动回复纯物理方式实现微…

tomcat的开发模式和生产模式

tomcat这两种模式对大家的影响主要是在jsp的开发当中。大家都知道jsp需要先编译成servlet源文件并编译为字节码,即生成.class文件才可以执行,所以在jsp文件首次运行时先要进行编译导致运行较慢,之后的访问都会直接进入执行阶段。 在开发模式下…

chrome开发模式下清除缓存问题

介绍: 在开发的时候经常会遇到这样的问题,后台js修改,前台怎么也清除不了缓存的问题,包括使用ctrlf5都没有办法处理,今天教大家一种方式,在chrome中采用开发模式下彻底清除缓存问题 一、打开chrome,随便访…

微信小程序-云开发模式pk传统开发模式【详细】

👨‍🎓作者:bug菌 ✏️博客:CSDN、掘金等 💌公众号:猿圈奇妙屋 🚫特别声明:原创不易,转载请附上原文出处链接和本文声明,谢谢配合。 🙏版权声明:文章里可能部分文字或者图片来源于互联网或者百度百科,如有侵权请联系bug菌处理。 小伙伴们,说起开发微…

软件开发模式之敏捷开发(scrum)

简介 这几年关于敏捷开发在互联网企业中越来越广泛被使用到,运用的比较多的当属scrum敏捷开发和xp敏捷开发,人人都在谈论敏捷开发。那什么才是敏捷开发呢? 目录 什么是敏捷开发?传统的开发模式和敏捷开发模式的对比&#xff1f…

特性分支开发模式 or 主干开发模式,团队该如何选择?

对于一个具有一定规模的开发团队而言,团队 Devops 的建设都是迈向高效开发的必经之路,即便没有达到 Devops 建设的程度,为了团队中开发之间的高效协作,基于版本管理工具来选择团队合理的代码分支开发模式都是非常重要的一环。对于…