关于#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 字符转…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...