C 语言拾锦 —— ##和#预处理标记的用法及原理分析

c++ map用法_C语言中"##"的独特用法 来自公众号:裸机思维C语言中一个重要的内容,就是##的用法,可能绝大部分 C 程序员都不知道"##"隐藏用法,下面就来给大家讲讲。一、##的“表”用法想必很多人都知道"##"的用法——它本质上是一个“胶水运算”(连接字符串的作用),用于把参数宏中的“形参”与其它没有天然分割的内容粘连在一起,例如:#define def_u32_array(__name, __size) u... 阅读详情

C 语言中"##“和”#"用法解密

一、概述

  • C语言中指针和宏是很容易让人用错的,特别是指针,即使工作多年的老鸟也很有可能在这上面马失前蹄。当然了宏也不例外,宏中有一些有意思的用法,如果我们没有了解到或者不是非常清楚的话,就很容易犯错或者说是看不懂别人的代码。
  • 下面一起来看一下C语言中的"##“和”#"的用法

二、"##“和”#"的用法及原理分析

  • 很多介绍这两者使用的文章里面都有这样一句话:使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起。那么具体是怎么实现的呢?他们的功能仅仅是如此吗?接下来让我们结合代码来一探究竟。
  • 我们需要明白的是,#和##都属于预处理标记,即编译器会在预处理阶段进行相关的替换或者处理。
"#"的用法及原理
用法及原理探究

先说结论:

  1. "#"的功能是将其后面的宏参数进行字符串化操作。
  2. 宏定义函数的参数与预处理标记 ‘#’ 之间出现的每一个空格都会被删除,并删除第一个预处理标记之前和最后一个预处理标记之后的空白字符,但是宏定义函数参数中的空格会保留。
  3. 空参数转化为空,即宏定义函数入参为空,那么展开的时候也为空。

我们先来看一个例子,理解其第一点用法,并且熟悉下这种用法:

#include <stdio.h>

#define MAKE_STR(s) (#s)

int main(void)
{
	printf("hello");
	printf(MAKE_STR(hello));

	return 0;
}

将其进行编译后,两个printf都会输出:hello
在这里插入图片描述
那么它具体是怎么实现的呢?其实很简单就是在其宏变量被替换,在其左右两侧加上双引号。为了探究这个过程,我们在用gcc编译的时候加上-E选项来看下编译器的预处理过程:
在这里插入图片描述
可以看到,gcc编译器的预处理过程和我们的猜想是一致的,#预处理标记的作用确实只是在我们指定的参数两旁加上双引号使其成为一个字符串了。这里也可以看出#预处理标记的工作原理了。

可以看到上面的例子中没有换行,我们将其晚上一下。提供两种思路:

  1. 在转换的字符串中加\n换行符。
  2. 在宏函数中加入"\n"换行

第一种方式,验证如下:

#include <stdio.h>

#define MAKE_STR(s) (#s)

int main(void)
{
    printf("hello\n");
    printf(MAKE_STR(hello\n));

    return 0;
}

输出如下:
在这里插入图片描述
可以看到,这种方式需要我们再每个字符后边加上\n,并不智能,那么有没有简单的办法呢?有的,利用C语言会自动将相邻的两个字符串合并的特性,第二种方法如下:

#include <stdio.h>

#define MAKE_STR(s) (#s"\n")

int main(void)
{
    printf("hello\n");
    printf(MAKE_STR(hello));

    return 0;
}

运行结果如下:
在这里插入图片描述
显然,第二种方法会显得更加聪明一点儿。

2. "#"用法深入

先来看下上面提到的第二点: 宏定义函数的参数与预处理标记 ‘#’ 之间出现的每一个空格都会被删除,并删除第一个预处理标记之前和最后一个预处理标记之后的空白字符,但是宏定义函数参数中的空格会保留。
先看例子:

#include <stdio.h>

#define MAKE_STR(s) ("123" # s "456\n")

int main(void)
{
    printf("hello world\n");
    printf(MAKE_STR(hello world));

    return 0;
}

运行结果为:
在这里插入图片描述
可以看到,#和宏定义参数s之间的空格被删除掉了。字符串"123"和字符串"456"和# s之间的空格也被删除掉了。
但是宏参数hello world之间的空格并没有被删除。
注意:'#'只能用于传入参数的宏定义中,且必须置于宏定义体中的参数名前。

"##"的用法

"##"被称为预处理拼接标记,宏定义展开的时候,用来将其左右两边两个token连接为一个token。注意这里连接的对象是token就行,不一定是宏的变量。

先看一下其基本用法,下面的例子是给定底数和指数,并打印出相应的指数的值。

#include <stdio.h>

#define EXP(x,y) (x##e##y)

int main(void)
{
    printf("hello world\n");
    printf("%e\n",EXP(1,3));

    return 0;
}

运行结果如下:
在这里插入图片描述
注意"##"表示将左边的字符串和右边的字符串连接起来,但是只能黏贴C语言除了关键字以外合法标识符。再来两个我们经常会用用到的例子:

#define def_array(__name,__size) {unint8_t array_##__name[__size]}

def_array(myarray,64)
将其进行宏展开:
uint8_t array_myarray[64];
/*************************************/
#define log_info(__STRING, __VA_ARGS__)    printf(__STRING,##__VA_ARGS__)

需要注意的**##只能用在宏定义中**,用在其他地方会报错。如下:
在这里插入图片描述

再深入一点,若是我们遇到函数指针和结构体数组结合的情景,是不是就可以更加灵活的选择对我们想要调用的函数进行选择呢?有很多代码里会根据command来选择执行不同的函数,会做一个回调函数表。

又或者,我们用预处理拼接符 ## 批量生成函数或者变量。这样的话,用上"##"是不是就可以让我们的代码更加简洁了呢?

##__VA_ARGS__有什么作用 ##__VA_ARGS__的笔记 阅读详情

相关推荐

C语言中的预处理器 # 和 ##

return 0;上述代码中,我们使用了 #define 定义了一个名为 CONCAT 的宏,它接收两个参数 x 和 y,并将它们连接成一个新的标识符。然后,在 main 函数中使用该宏声明一个名为 foo1 的整数变量,并将它的值设置为 10,最后使用 printf 输出该变量。综上所述,# 和 ## 是C语言中非常有用的预处理指令,它们可以让我们在程序中快速地定义字符串常量、连接标识符等。当我们需要在代码中使用这些指令时,可以参照上述示例进行使用。

CodeNexus的博客 330

C语言中"##"的独特用法

关注、星标公众号,不错过精彩内容作者:傻孩子转自:裸机思维市面上有很多比较火的编程语言,比如Python、 JAVA、 Go等,你可能觉得C语言很古老、很落后。如果你有这种想法,那可能你...

strongerHuang 1万+

unity中VideoPlayer播放视频,使用Shader除去黑底且无黑边

1. 创建一个Shader 和 一个 材质球;Shader中复制如下代码:(此Shader代码为“ChromaKeyKit”插件中的ChromaKey_Alpa_Genera代码,但因只需要除去视频黑底而挂一个插件有点不合适) // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "ChromaKeyKit/ChromaKey/ChromaKey_Alpha_General"

weixin_42065078的博客 1747

【C】 25_#和##操作符分析

#运算符 # 运算符用于在预处理期将宏参数转换为字符串 # 的转换作用是在预处理期完成的,因此只在宏定义中有效 编译器不知道 # 的转换作用 用法 #define STRING(x) #x printf("%s\n", STRING(Hello word)); 实例分析: #运算符的基本用法 #include <stdio....

weixin_34128501的博客 445

VScode高效开发STM32:头文件路径配置全解析

本文详细解析了在VScode中高效开发STM32项目的核心步骤——头文件路径配置。针对常见的“找不到头文件”问题,文章深入剖析了IntelliSense的工作原理,并提供了手把手创建和解读c_cpp_properties.json配置文件的完整指南,包括includePath、defines、compilerPath等关键项的设置,帮助开发者彻底解决红色波浪线错误,实现精准的代码提示与跳转。

weixin_28835789的博客 341

拼团系统设计-人群标签的设计与思考

最近在学习一个拼团项目中涉及到的人群模块的设计,写下一些实现与思考。

wywcool的博客 971

c/c++中的宏

c/c++中宏的学习

StudyWinter的博客 1万+

【ros】ROS1的安装和相关使用方法

主要学习ros相关内容

Greary的博客 2379

【C语言进阶深度学习记录】二十一 # 和 ## 号操作符的使用与分析

文章目录1 # 运算符1.1 # 运算符的基本用法1.2 # 运算符的妙用2 ## 运算符2.1 ##运算符的基本用法2.2 ##运算符的工程运用3 总结 1 # 运算符 #运算符用于在预处理期将宏参数转换为字符串 #的转换是在预处理期间完成的,因此,只在宏定义中有效 编译器并不知道 # 的转换作用 用法如下: 1.1 # 运算符的基本用法 代码:25-1.c #include &amp;l...

厚积薄发 6553

记录一次Spring Framework 漏洞修复的过程

摘要:针对Spring Boot 2.6.13项目在阿里云漏洞扫描中发现的Spring Framework目录遍历漏洞(CVE-2024-38819),首先分析了漏洞成因,确认5.3.23版本存在风险。解决方案包括升级Spring Boot至2.7.18(对应Spring Framework 5.3.41)或6.0+版本,但后者需JDK17支持。临时方案通过配置HttpFirewall限制特殊字符,最终借助阿里云防护服务暂时解决问题。建议长期解决方案仍需升级至安全版本(Spring Boot 6.1.14或

极客栈 1491

31、工程中不确定性量化与优化的全面解析

本文全面解析了工程中不确定性量化与优化的关键方法。针对数据充足与不足两种情况,分别介绍了概率理论和非概率方法在不确定性建模中的应用;系统阐述了采样方法、FORM/SORM、泰勒级数近似等不确定性传播技术;深入比较了RBDO与RDO两种主流优化框架的原理、步骤及适用场景,并通过两杆桁架案例展示了具体实现过程。文章旨在为工程师提供一套完整的从不确定性分析到优化设计的方法论,提升工程系统的可靠性与稳健性。

php55的博客 121

关于C语言中“##“的独特用法

转自:微点阅读https://www.weidianyuedu.com 市面上有很多比较火的编程语言,比如Python、 JAVA、 Go等,你可能觉得C语言很古老、很落后。如果你有这种想法,那可能你只是一个初学的菜鸟。之前给大家分享《C语言中几种特殊标准定义和用法》忘记了C语言中一个重要的内容,就是##的用法,可能绝大部分 C 程序员都不知道"##"隐藏用法,下面就来给大家讲讲。 一、##的“表”用法 想必很多人都知道"##"的用法——它本质上是一个“胶水运算”(连接字符串的作用),用于...

ysds20211402的博客 5633

C语言中“#“和“##“的用法

C语言中"#"和"##"的用法 1. 前言 使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起. 2. 一般用法 #include<cstdio> #include<climits> using namespace std; #define STR(s) #s #define CONS(a,b) int(a##e##b) int main() { printf(STR(vck)); // 输出字符串"vck" printf(

君君的博客 1万+

微软商店报错“此产品当前不可用”?终极修复指南

微软商店报错“此产品当前不可用”?终极修复指南

安德洛夫 3813

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

#和##用法总结

C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念)。下面对常遇到的宏的使用问题做了简单总结。 关于#和## 在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量 通过替换后在其左右各加上一个双引号。比如下面代码中的宏: #define WARN_IF(EXP) do{ if (EXP) fprintf(stderr, "Warning: " #EXP “/n”); }

爱吃肉的猫 4万+

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

# 和 ## 运算符

# 和 ## 运算符

weixin_42136255的博客 9908

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

C语言中宏的使用

关于#和##在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。比如下面代码中的宏: #define WARN_IF(EXP) / do{ if (EXP) / fprintf(stderr, "Warning: " #EXP "/n")

萌芽 1452

C语言的“##“用法以及意义

C语言 “”## 使用方法

newzhpfree的博客 3828

C语言中“##“的用法

在第一个printf()的最后多了一个","。另一方面,"__name"与"["是具有天然分隔的——编译器不会认为"__name"与"["是连接在一起的,因此这里并不需要画蛇添足的使用"##"运算——如果你这么做了,预编译器会毫不犹豫的告诉你语法错误。,或者换句话说,在使用参数宏的时候,预编译器是看不懂C语法的——在它眼中,除了它所认识的少数符号外,其它东西都是无意义的字符串——由于在处理括号内部的内容时,它只认识","和"...",因此当括号中的内容每增加一个",",与编译器就认为多了一个参数。

xinyuerr的博客 2014

define宏定义中的#,##,@#及/符号

<br />1、# (stringizing)字符串化操作符。其作用是:将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串。其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。<br />如:<br />#define example(instr) printf("the input string is:/t%s/n",#instr)<br />#define example1(instr) #instr<br />当使用该宏定义时:<br />example(abc); 在编译时将会展

xdsoft365的专栏 4万+

# 和 ## 的区别

# :1、最常见的是 #不会替换参数,只替换其所对应的字符 2、 把宏参数变成一个字符串; ## :把两个宏参数连接到一起(只能两个) #include <stdio.h> #include <stdlib.h> #define ONE 1 //1.最常见的是 #不会替换参数,只替换其所对应的字符; #define toString(str) #str //2.转字符串 #define hehe(x,y) x##y //连接 int ma...

枕上青灯竹书香的博客 3583

C 语言 #、##、__VA_ARGS__

‘#’ 和 ‘##’ 属于预处理标记。‘#’ 和 ‘##’ 用于类似函数的宏定义中(或者简称为宏定义函数)。 ‘__VA_ARGS__’ 是 C99 引入的用于支持宏定义函数中使用可变参数。 操作符 ‘#’ 在宏定义展开的时候,标记 ‘#’ 用于将 ‘#’ 后面的宏定义函数中的参数转化为对应的字符串。宏定义函数的参数与预处理标记 ‘#’ 之间出现的每一个空格都会被删除,并删除第一个预处理标...

嵌入式软件开发工程师 1601

#和##的用法

简介 C语言中经常用到 # 和 ##, 很多初学者不知道他们什么什么意思, 这儿我简单科普一下. #的用法 当做 replacement-text令牌, 转换为引号引起来的字符串 ... #define TO_STR(x) #x int main() { int value = 123; printf("TO_STR(value) = %s\n", TO_STR(value)); printf("TO_STR(123) = %s\n", TO_STR(value)); }

iot小胡的博客 10万+

c语言中#和##的用法

本文主要讲述c语言的一点基础语法和在内核的应用中其中的一点例子。 #,##分别在c语言中是怎么作用? 文章代码编译的环境: 桌面环境:Ubuntu10.04 内核:linux2.6.32 编译器:gcc4.4.3 一、基本的用法 1、#.参数名以#作为前缀则结果将被扩展为由实际参数的带引号的字符串。 如: #define dprint(expr)printf(#expr"=%d\

phenixyf的专栏 7260

C语言宏中“#”和“##”的用法

对于这两个语法的功能都比较简单,且都是在预处理阶段做一些工作 : #主要是将宏参数转化为字符串 ##主要是将两个标识符拼接成一个标识符

诊断协议那些事儿's blog 4714
上一篇: 从零开始学Linux设备驱动--Linux设备模型
下一篇: Makefile基本语法知识(全)
浮云流响
博客等级 码龄8年 305粉丝 94原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值