关于#define的使用方法总结
文章目录
- #define 预处理指令
- 一、#define宏定义
- 二、查看预处理文件
- 三、#define 的使用方法
- 四、C语言宏中“#”和“##”的用法
- 五、常见的宏定义总结
- 六、常考题目
#define 预处理指令
#define
是 C 和 C++ 编程语言中的预处理指令,用于定义宏(macro)。宏是一种预处理器功能,它允许程序员定义一个标识符(通常是大写字母),该标识符可以被替换为一段代码、表达式或常量值。
一、#define宏定义
#define叫做宏定义,语法格式:
#define 名字 值
例如:#define PI 3.1415926
注意事项:
- 存储方式:
#define
宏定义不分配内存,它们只是在编译时替换文本。 - 没有分号:
#define
指令末尾不需要分号,分号会被视为值的一部分。 - 作用域:
#define
声明之后在整个文件或宏的作用域内有效,直到#undef
指令出现或者文件结束。
定义一个宏名字之后,可以在其他宏定义中使用,例如
#define OEN 1
#define TWO OEN+ONE
#define THREE OEN+TWO
二、查看预处理文件
gcc -E define.c -o define.i
对 define.c 文件执行预处理操作,并将预处理后的结果输出到 define.i 文件中。
这通常用于查看宏定义展开、文件包含等情况,而不是为了生成可执行文件或目标代码。
- gcc: 这是 GNU 编译器集合的命令行工具,用于编译 C 和 C++ 程序。
- -E: 这是 GCC 的一个选项,代表“预处理”(Preprocess)。使用这个选项时,GCC 将执行预处理操作,但不会进行编译。预处理操作包括宏展开、文件包含(#include)的处理等。
- define.c: 这是要预处理的 C 语言源文件。GCC 将读取这个文件,并应用预处理指令。
- -o: 这个选项后面跟着输出文件的名称。在这里,它指定了预处理后的结果应该被写入哪个文件。
- define.i: 这是预处理后生成的文件的名称。由于使用了 -E 选项,GCC 不会生成目标代码文件(通常是 .o 文件),而是生成一个包含了所有预处理操作结果的文件。
三、#define 的使用方法
1、定义常量:#define
最常见的用法是定义常量,这些常量在程序中可以被多次引用,而其值在编译时就已经确定。
#define PI 3.1415926
2、字符串化操作:将宏转换为字符串。
#define FILE_PATH "/home/orangepi/project/"
使用FILE_PATH替换/home/orangepi/project/
3、头文件保护:#ifndef
、#define
和 #endif
是 C 和 C++ 预处理器指令的一部分,通常一起使用来实现头文件保护(也称为 “include guards” 或 “include sentinels”)。这种机制可以防止头文件被多次包含到同一个源文件中,从而避免编译错误和重定义问题。
#ifndef _HEADER_FILE_H_
#define _HEADER_FILE_H_// 头文件内容,例如函数声明、类定义、宏定义等#endif
/*当你的头文件被包含时,预处理器会检查这个宏是否已经定义。如果尚未定义,它将定义这个宏,并处理头文件中的所有内容。如果已经定义,
预处理器将跳过整个头文件的内容,防止它被再次包含。
这种机制对于避免因重复包含头文件而导致的编译错误非常重要,特别是在大型项目中,头文件之间可能会相互依赖,导致复杂的包含关系。使用
头文件保护可以确保每个头文件只被编译一次。*/
4、宏函数:使用 #define
可以定义宏函数,这些宏在预处理阶段展开,替换为它们的参数表达式,从而减少函数调用的开销。
1.无参宏
#define debug printf("hello world")
int main{debug;return 0;
}
2.带参宏:
语法:不是进行简单的字符串替换,还要进行参数替换
#define宏名(形参列表) 字符串
#define debug(s) printf("%s\n",s)
int main{debug("hello world");return 0;
}
【注意】:要注意算数的优先级
如果宏定义中包含表达式,需要小心处理副作用。
例:
#include <stdio.h>#define M 3+2
#define N (3+2)int main()
{int data = 4;printf("data * M = %d\n",data * M);printf("data * N = %d\n",data * N);return 0;
}
/*运行结果:
data * M = 14
data * N = 20*/
使用gcc -E define.c -o define.i查看生成的预处理文件define.i:
M = 4*3 +2 = 14
N = 4*(3+2) = 20
宏和函数的区别:
- 和函数不同,宏的参数没有数据类型,因为是文本替换
- 因为是文本展开,相比函数没有执行调度的开销,效率要高
- 使用有参数的宏函数时,参数在替换文字中要用括号包围,以免收到运算符优先级的影响
- 函数的参数是有类型的,存在类型检查,但是宏的参数没有类型与类型检查;
- 函数可以递归,而宏不可以递归;
- 对于参数而言,宏的参数是直接替换的,所以会有一些 参数具有副作用,而函数的参数是临时拷贝的,没有副作用的情况
四、C语言宏中“#”和“##”的用法
1、#:字符串化操作符
作用:将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串。
【注意】:其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。
#define example1(instr) printf("this is :%s\n",#instr)
#define example2(instr) #instr
当使用宏定义时:
example1(abc);
//将会展开成:printf("this is:%s\n","abc")string str = example2(abc);
//将会展开成:string str = "abc"
2、##:符号连接操作符
作用:操作符用于连接两个宏参数。当宏在预处理阶段展开时,##
会将两侧的参数连接起来,合并成一个新的标识符或字符串。
#define exampleNum(n) num##nint num9 = 9;
int num = exampleNum(9);
//将会扩展成:int num = num9;
【注意】:
- 当用##连接形参时,##前后的空格可有可无。
- 连接后的实际参数名,必须为实际存在的参数名或是编译器已知的宏定义。
- 如果##后的参数本身也是一个宏的话,##会阻止这个宏的展开。
#include <stdio.h>
#include <string.h>#define STRCPY(a, b) strcpy(a ## _p, #b) int main() {char var1_p[20];char var2_p[30];strcpy(var1_p, "aaaa");strcpy(var2_p, "bbbb");STRCPY(var1, var2);//宏展开为strcpy(var1_p, "var2");//这会将字符串 "var2" 复制到 var1_p 中,而不是将 var2_p 的内容复制到 var1_p 中。//因为 "var2" 不是一个有效的字符串,而是一个文本标记。STRCPY(var2, var1);//宏展开为strcpy(var2_p, "var1");//同样,它尝试将字符串 "var1" 复制到 var2_p 中printf("var1 = %s\n", var1_p);printf("var2 = %s\n", var2_p);return 0;
}
/*运行结果:
var1 = var2
var2 = var1*/
五、常见的宏定义总结
1、得到指定地址上的一个字节或字
#define MEM_B(x) (*((byte *)(x)))
#define MEM_W(x) (*((word *)(x)))2、求最大值和最小值
#define MAX(x,y) (((x)>(y)) ? (x) : (y))
#define MIN(x,y) (((x) < (y)) ? (x) : (y))3、计算一个数的绝对值
#define ABS(a) ((a) < 0 ? -(a) : (a))4、交换两个变量的值
#define SWAP(a, b, type) do { type SWAP_temp = (a); (a) = (b); (b) = SWAP_temp; } while(0)5、计算数组大小
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))6、将宏参数转换为字符串
#define STRINGIFY(x) #x7、连接两个宏参数
#define CONCAT(x, y) x ## y8、得到一个field在结构体(struct)中的偏移量
#define FPOS(type,field) ((dword)&((type *)0)->field)9、得到一个结构体中field所占用的字节数
#define FSIZ(type,field) sizeof(((type *)0)->field)10、不使用sizeof,求int、double等变量类型占用的字节数
#define Mysizeof(Value) (char*)(&value+1)-(cahr *)&value11、声明以1年中有多少秒(忽略闰年)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
使用UL或ul用作后缀,表示将结果强制转换为 unsigned long 类型,以确保表达式的结果作为无符号长整型处理。12、按照LSB格式把两个字节转化为一个Word
#define FLIPW(ray) ((((word)(ray)[0]) * 256) + (ray)[1])13、按照LSB格式把一个Word转化为两个字节
#define FLOPW(ray,val) (ray)[0] = ((val)/256); (ray)[1] = ((val) & 0xFF)14、得到一个变量的地址(word宽度)
#define B_PTR(var) ((byte *) (void *) &(var))
#define W_PTR(var) ((word *) (void *) &(var))15、得到一个字的高位和低位字节
#define WORD_LO(xxx) ((byte) ((word)(xxx) & 255))
#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8))16、断言宏,用于检查程序中的条件
#include <assert.h>
#define ASSERT(condition) assert((condition) && #condition)
六、常考题目
1、已定义#define M(x,y,z) x * y + z, 并且int a = 1, b = 2, c = 3,调用M(a + b, b + c, c + a)的输出结果为(12)
#include <stdio.h>#define M(x,y,z) x * y + z int main(int argc, char* argv[])
{int a = 1, b = 2, c = 3;printf("M(a+b, b+c, c+a) = %d\n",M(a+b, b+c, c+a));//展开后:printf("M(a+b, b+c, c+a) = %d\n",a+b * b+c + c+a);return 0;
}
/*运行结果:
M(a+b, b+c, c+a) = 12*/
计算下列程序的返回值是多少()
#include <stdio.h>#define product(x) (x * x)int main(int argc, char* argv[])
{int i = 3, j, k;j = product(i++);//展开后: j = (i++ * i++);printf("after j,i = %d\n",i);k = product(++i);//展开后: k = (++i * ++i);printf("after k,i = %d\n",i);printf("j = %d, k = %d\n",j,k);return 0;
}
/*运行结果:
after j,i = 5
after k,i = 7
j = 12, k = 49*/
相关文章:

关于#define的使用方法总结
文章目录 #define 预处理指令一、#define宏定义二、查看预处理文件三、#define 的使用方法四、C语言宏中“#”和“##”的用法五、常见的宏定义总结六、常考题目 #define 预处理指令 #define 是 C 和 C 编程语言中的预处理指令,用于定义宏(macro…...
Unity顶点动画(Vertex Animation):创造动态视觉效果
在Unity中,顶点动画(Vertex Animation)是一种强大的技术,它允许开发者直接在顶点级别上操作和变形网格,从而实现各种动态视觉效果。顶点动画不依赖于骨骼绑定,因此非常适合模拟布料、流体、面部表情等复杂的动画效果。本文将探讨顶…...

WSL for Windows
1、安装 超详细Windows10/Windows11 子系统(WSL2)安装Ubuntu20.04(带桌面环境)_wsl安装ubuntu20.04-CSDN博客https://blog.csdn.net/weixin_44301630/article/details/122390018 注意,安装之后首次启动 Ubuntu 时&…...
Matlab freqz 代码简单实现
相关代码打开matlab源码也可以看到,这里做了简单实现,与源码并不完全一样。 实现代码 [h2 w2] freqzfir(data); [h1 w1] freqz(data); h2h2; h12 [h1, h2];[h4 w4] freqziir(b,a, 2001,true); [h3 w3] freqz(b,a, w4, whole); h4 h4; h34 h…...

待办app哪款好?高效待办软件推荐
在快节奏的现代生活中,一款高效的待办事项管理软件对于提升工作效率和个人时间管理至关重要。面对市场上众多的待办app,哪款才是你的最佳选择呢?经过深入体验和对比,我发现敬业签这款高效待办软件是个不错的选择。 敬业签的快速记…...

【OSCP系列】OSCP靶机-BTRsys-2.1(原创)
OSCP系列靶机—BTRsys-2.1 原文转载已经过授权 原文链接:Lusen的小窝 - 学无止尽,不进则退 (lusensec.github.io) 一、主机发现 二、端口扫描 1、快速扫描 2、全端口扫描 3、服务系统探测 4、漏洞探测 80端口扫到了一些目录,有wordpress框…...

攻坚克难岁月长,自主腾飞世界强——回顾近代中国数据库的发展与飞跃
前言 最近看了《中国数据库前世今生》纪录片,感触颇深,也是一直在思考到底该用何种方式起笔来回顾这段筚路蓝缕却又充满民族自豪感的历程。大概构思了一周左右吧,我想,或许还是应该从那个计算机技术在国内刚刚萌芽的年代开始讲起…...
WEB前端12-axios基础
Vue2-axios基础 1.axios基本概念 在现代的前端开发中,处理网络请求是至关重要的一部分。Axios 是一个流行的基于 Promise 的 HTTP 客户端,它可以在浏览器和 Node.js 环境中使用。它的设计简单易用,支持并行请求、拦截器、CSRF 防护等特性&a…...
Ubuntu 防火墙设置
目录 1. 安装防火墙 2. 开启和关闭防火墙 3. 开放端口和服务规则 4. 关闭端口和删除服务规则 5. 查看防火墙状态 1. 安装防火墙 如果已经安装就忽略 # 安装ufw(Uncomplicated Firewall),这是Ubuntu上管理防火墙的一个简单工具 sudo ap…...

JL 跳转指令的理解
一般情况下,JU 和 JC 是最常见的跳转指令;但有时会用到JL 指令,JL 说起来更像是一组指令,类似C,C# 语言中的 switch case 语句,但是有个明显的不同,前者的判断条件可以是任意合理数字,后者范围…...
vue大屏展示组件库datav
主要用于构建大屏数据展示页面,具有多种类型组件可供使用。详情参考 datav官网 一、安装 npm 安装 npm install jiaminghi/data-viewyarn安装 yarn add jiaminghi/data-view二、使用 在main.js中注册为全局组件 import dataV from jiaminghi/data-view Vue.us…...
Vue.js 与 Ajax(vue-resource)的集成应用
Vue.js 与 Ajax(vue-resource)的集成应用 Vue.js 是一款流行的前端JavaScript框架,以其简洁、灵活和高效的特点而受到开发者的喜爱。在实际开发中,与后端服务的通信是不可或缺的,而Ajax技术是实现这一功能的关键。在V…...

【讲解下AI Native应用中的模型微调】
🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…...

【SOC 芯片设计 DFT 学习专栏 -- DFT DRC规则检查】
请阅读【嵌入式及芯片开发学必备专栏】 请阅读【芯片设计 DFT 学习系列 】 如有侵权,请联系删除 转自: 芯爵ChipLord 2024年07月10日 12:00 浙江 文章目录 概述DRC的概念Tessent DRC检查的概述时钟相关检查扫描相关检查BIST规则检查预DFT时钟规则检查 …...
深度学习:如何计算感受野
感受野(Receptive Field)是卷积神经网络(CNN)中的一个重要概念,用于描述输入图像中的一个像素在输出特征图中影响的区域大小。在设计和理解卷积神经网络时,计算感受野有助于理解网络如何对输入数据进行处理…...

【状语从句】
框架 概念,特点主将从现连接词时间条件地点结果方式让步原因目的比较省略倒装 解读 1【概念,特点】 一个完整的句子,去修饰另一个完整句子中的动词,称为状语从句;特点:从句完整,只用考虑连接词是…...

阿里云服务器安装Anaconda后无法检测到
前言 问题如标题所言,就是conda -V验证错误,不过后来发现其实就是虽然安装时,同意了写入环境变量,但是其实还没有写入,需要手动写入。下面也会重复一遍安装流程。 安装 到[Anaconda下载处](Download Now | Anaconda)查…...
在没有源程序的情况时,如何通过控制鼠标按钮控制电脑exe程序?
有时候想控制第三方软件,但是没有源程序,可以控制鼠标键盘自动操作软件达到我们想要的目的 首先建一个功能类包含窗口控制,鼠标控制和输入控制等 csharp using System; using System.Collections.Generic; using System.Linq; using System.…...

如何排查GD32 MCU复位是由哪个复位源导致的?
上期为大家讲解了GD32 MCU复位包括电源复位和系统复位,其中系统复位还包括独立看门狗复位、内核软复位、窗口看门狗复位等,在一个GD32系统中,如果莫名其妙产生了MCU复位,如何排查具体是由哪个复位源导致的呢? GD32 MC…...
【C算法】编程初学者入门训练140道(1~20)
牛客编程初学者入门训练150题 BC1 实践出真知BC2 我是大VBC3 有容乃大BC6 小飞机BC7 缩短二进制BC8 十六进制转十进制BC9 printf的返回值BC10 成绩输入输出BC11 学生基本信息输入输出BC12 字符圣诞数BC13 ASCII 码BC14 出生日期输入输出BC15 按照格式输入并交换输出BC16 字符转…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...