关于#define的使用方法总结

news2025/1/12 19:00:58

文章目录

  • #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

在这里插入图片描述

宏和函数的区别:

  1. 和函数不同,宏的参数没有数据类型,因为是文本替换
  2. 因为是文本展开,相比函数没有执行调度的开销,效率要高
  3. 使用有参数的宏函数时,参数在替换文字中要用括号包围,以免收到运算符优先级的影响
  4. 函数的参数是有类型的,存在类型检查,但是宏的参数没有类型与类型检查;
  5. 函数可以递归,而宏不可以递归;
  6. 对于参数而言,宏的参数是直接替换的,所以会有一些 参数具有副作用,而函数的参数是临时拷贝的,没有副作用的情况

四、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##n

int 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) #x

7、连接两个宏参数
#define CONCAT(x, y) x ## y

8、得到一个field在结构体(struct)中的偏移量
#define FPOS(type,field) ((dword)&((type *)0)->field)

9、得到一个结构体中field所占用的字节数
#define FSIZ(type,field) sizeof(((type *)0)->field)

10、不使用sizeof,求intdouble等变量类型占用的字节数
#define Mysizeof(Value) (char*)(&value+1)-(cahr *)&value

11、声明以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*/

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

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

相关文章

JVM系列 | 对象的消亡——HotSpot的设计细节

HotSpot 的细节实现 文章目录 HotSpot 的细节实现OopMap 与 根节点枚举根节点类型及说明HotSpot中的实现 OopMap 与 安全点安全点介绍如何保证程序在安全点上&#xff1f; 安全区域记忆集与卡表记忆集卡表 写屏障并发的可达性分析&#xff08;与用户线程&#xff09;并发可达性…

计算机底层知识点(一)晶体管与CPU执行指令之间的联系

该文主要通过详细介绍晶体管在CPU执行指令时的作用。本文所讲解例子为NPN型二极管。这里简单介绍一下&#xff0c;NPN是共阳极&#xff0c;即两个NP结的P结相连为基极&#xff0c;另两个N结分别做集电极和发射极&#xff0c;发射极电流 集电极电流 基极电流。 图5 LED两脚分别…

【Vue3】具名插槽

【Vue3】具名插槽 背景简介开发环境开发步骤及源码 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的日子。本文内…

纯技术手段实现内网穿透,免注册免收费

纯技术手段实现内网穿透&#xff0c;免注册免收费 一、内网穿透二、方法分类2.1 基于隧道协议的内网穿透2.2 基于反向代理的内网穿透2.3 基于SSH的内网穿透具体工具的分类如下&#xff1a;基于隧道协议基于反向代理基于SSH 三、本文方法四、具体操作4.1 安装服务端4.2 安装客户…

【Linux 网络】链路层

文章目录 链路层1 以太网1.1 以太网帧格式1.2 MAC地址1.3 MTU 2. ARP协议2.1 ARP协议的作用2.2 ARP数据报格式2.3 ARP的流程 其他协议3. DNS协议3.1 域名3.2 输入URL后的事情 4. ICMP协议4.1 ICMP 功能都有啥&#xff1f;4.2 基于ICMP的命令ping命令 5. NAT协议5.1 NAT技术背景…

android13关机按钮 去掉长按事件 去掉启动到安全模式 删除关机长按

总纲 android13 rom 开发总纲说明 目录 1.前言 2.界面效果 3.问题分析 4.代码修改 5.编译替换运行 6.彩蛋 1.前言 在Android操作系统中,关机按钮通常具有多种功能,包括短按关机、长按启动语音助手或重启设备等。在某些情况下,用户或设备管理员可能希望自定义关机按…

爬虫中常见的加密算法Base64伪加密,MD5加密【DES/AES/RSA/SHA/HMAC】及其代码实现(一)

目录 基础常识 Base64伪加密 python代码实现 摘要算法 1. MD5 1.1 JavaScript 实现 1.2 Python 实现 2. SHA 2.1 JavaScript 实现 2.2 Python 实现 2.3 sha系列特征 3. HMAC 3.1 JavaScript 实现 3.2 Python 实现 对称加密 一. 常见算法归纳 1. 工作模式归纳 …

码农职场:一本专为IT行业求职者量身定制的指南

目录 写在前面 推荐图书 推荐理由 写在后面 写在前面 本期博主给大家推荐一本专为IT行业求职者量身定制的指南&#xff1a;《码农职场》。 推荐图书 https://item.jd.com/14716160.html 内容简介 这是一本专为广大IT 行业求职者量身定制的指南&#xff0c;提供了从职前…

使用Python实现栅格划分(渔网)

在QGIS中&#xff0c;“渔网”&#xff08;Fishnet&#xff09;是指一种创建规则网格&#xff08;通常是矩形或正方形&#xff09;的工具&#xff0c;这些网格可以用于空间数据的采样、分区或作为其他地理空间分析的基础。渔网工具可以生成一个由多边形组成的图层&#xff0c;每…

文件解析漏洞—IIS解析漏洞—IIS7.X

在IIS7.0和IIS7.5版本下也存在解析漏洞&#xff0c;在默认Fast-CGI开启状况下&#xff0c;在一个文件路径/xx.jpg后面加上/xx.php会将 “/xx.jpg/xx.php” 解析为 php 文件 利用条件 php.ini里的cgi.fix_pathinfo1 开启IIS7在Fast-CGI运行模式下 在 phpstudy2018 根目录创建…

4、postgresql拓展表空间

base是数据保存目录&#xff0c; OID&#xff1a;对象标识符&#xff0c;无符号4字节整数&#xff0c; 数据库的oid在pg_database中&#xff0c;表&#xff0c;索引&#xff0c;序列等OID存储在pg_class中 表空间&#xff1a;pg最大的逻辑存储单元&#xff0c;表索引数据库都…

Linux安装Zabbix7.0并且使用外置Mysql数据库

MySQL 数据库服务版本。必须至少为 8.00.30 # rpm -Uvh https://repo.zabbix.com/zabbix/7.0/alma/9/x86_64/zabbix-release-7.0-5.el9.noarch.rpm # dnf clean all #安装zabbix # dnf install zabbix-server-mysql zabbix-web-mysql zabbix-nginx-conf zabbix-sql-scripts za…

【一图学技术】6.反向代理 vs API网关 vs 负载均衡的原理和使用场景

反向代理 vs API网关 vs 负载均衡 一、概念 ​ &#x1f30f;反向代理&#xff08;Reverse Proxy&#xff09;是一种位于服务器和客户端之间的代理服务器。 ​ 它接收来自客户端的请求&#xff0c;并将其转发给后端服务器&#xff0c;然后将后端服务器的响应返回给客户端。客…

dfs,CF 196B - Infinite Maze

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 https://codeforces.com/problemset/problem/196/B 二、解题报告 1、思路分析 考虑如何判断一条路径可以无限走&#xff1f; 我们对朴素的网格dfs改进&#xff0c;改进为可以dfs网格外的区域 如果存在某个…

Go语言加Vue3零基础入门全栈班10 Go语言+gRPC用户微服务项目实战 2024年07月31日 课程笔记

概述 如果您没有Golang的基础&#xff0c;应该学习如下前置课程。 Golang零基础入门Golang面向对象编程Go Web 基础Go语言开发REST API接口_20240728Go语言操作MySQL开发用户管理系统API教程_20240729Redis零基础快速入门_20231227GoRedis开发用户管理系统API实战_20240730Mo…

大模型下的视频理解video understanding

数据集 Learning Video Context as Interleaved Multimodal Sequences Motivation&#xff1a; 针对Narrative videos, like movie clips, TV series, etc.&#xff1a;因为比较复杂 most top-performing video perception models 都是研究那种原子动作or人or物 understandin…

C++ 布隆过滤器

1. 布隆过滤器提出 我们在使用新闻客户端看新闻时&#xff0c;它会给我们不停地推荐新的内容&#xff0c;它每次推荐时要去重&#xff0c;去掉 那些已经看过的内容。问题来了&#xff0c;新闻客户端推荐系统如何实现推送去重的&#xff1f; 用服务器记录了用 户看过的所有历史…

OpenStack——存储服务

存储侧&#xff1a; 块存储 文件存储 对象存储 存储简介 特点&#xff1a; 1、OS盘只能使用块存储 2、不能实现共享【不能解决两个主机同时去读写同一个block的问题】 3、性能最优 filesystem——文件存储 VIMS&#xff1a;高可用文件系统 ——提供了锁机制 对象存储 ——解…

MySQL搭建主从复制和读写分离(数据库管理与高可用)

集群&#xff1a; 高可用&#xff1b; 负载均衡&#xff1b; 高性能 1、MySQL主库在事务提交时把数据变更&#xff08;insert、delet、update&#xff09;作为事件日志记录在二进制日志表&#xff08;binlog&#xff09;里面。 2、主库上有一个工作线程 binlog dump thread…

蓝桥杯 DNA序列修正

今天再刷蓝桥的题目时&#xff0c;发现这道题目的第二种更为简洁的做法&#xff1b; 首先题目描述如下&#xff1a; 样例输入 5 ACGTG ACGTC 样例输出 2 对于这道题目&#xff0c;我们想的是用两个数组将其分别存储下来&#xff0c;然后再根据A-T、G-C的配对关系将数组二&a…