关于宏##的使用注意一点

原文:关于宏##的使用注意一点

在看《C语言高级编程》时,里面有个关于宏##的题目:

1.已知#define A “menu”
#define B “osd”,
若请使用宏A,B表示出字符串”menuosd”

答案:1 答案1:#define C A B
答案2:#define _C_(a,b) a##b
#define C(a,b) _C_(a,b)

然后我实际动手测试了一下,先来第一种:

#include <stdio.h>
#define A "menu"
#define B "osd"
#define STR A B

int main(int argc, char *argv[])
{
char *p = STR;
return 0;
}
gcc -E hell.c -o hello.i
cat hello.i

结果:

int main(int argc, char *argv[])
{

char *p = "menu" "osd";

return 0;
}

第一个答案其实预编译后给出的结果是不完全符合要求的。

然后是第二种:

#include <stdio.h>
#define A "menu"
#define B "osd"

#define _C_(a,b) a##b
#define C(a,b) _C_(a,b)

int main(int argc, char *argv[])
{
char *p = C(A,B);
printf("%s\n", p);
return 0;
}

首先,为什么要定义两个宏,一个不能解决问题吗?是的,不能。为什么?看这个链接:[短小精悍的宏](http://www.cnblogs.com/wb-DarkHorse/archive/2013/04/27/3046749.html)
然后再次按照上边的命令进行预编译,但是给出了错误信息:pasting "menu" and "osd" does not give a valid preprocessing token gcc
这就奇怪了。然后google了一下,发现了相同的问题:
[问题](http://stackoverflow.com/questions/4667779/preprocessor-macro-gcc-pasting-x-and-x-does-not-give-a-valid-preprocessing-toke)

并且里面说了,这种情况在VS里面不会报错,可以直接工作。so?

#include <stdio.h>
#define A "menu"
#define B "osd"

#define _C_(a,b) a##b
#define C(a,b) _C_(a,b)

int _tmain(int argc, _TCHAR* argv[])
{
char *p = C(A,B);//STR;
printf("%s\n", p);
return 0;
}

果然给出了结果:menuosd

为什么gcc和VS会对这个问题给出差异的结果呢?看这个问题:
[that‘s why](http://stackoverflow.com/questions/1206624/differences-in-macro-concatenation-operator-between-visual-c-and-gcc?rq=1)

根据C标准,用##操作后的结果必须是一个已经预定义过的符号。否则是未定义的。所以gcc和vs对于这个未定义行为表示了不同的看法,前者是给出错误,后者一笑而过。那什么是已经预定过的符号呢? 它包含了这些:头文件名, 等式, 预处理数字, 字符常数, 字符串值, 标点符号, 单个非空字符

在我们的例子中,_C_(a,b)用##连接后,应该是产生menuosd,但是这是一个未预定义的字符串,所以产生了一个未定义的行为。我们再看一个例子:

#define A 2
#define _CONS(a,b) (a##e##b)
#define CONS(a,b) _CONS(a,b)

int main(int argc, char *argv[])
{
printf("%f\n", CONS(A, A));
return 0;
}

这个时候gcc不会给出错误提示了。结果:200.0000
为什么这个时候不给出错误提示呢?我的理解是,CONS(A, A)替换后成为2e2,而这时一个常量,符合C标准。

ok,给出一个链接,详细的解释了gcc中##的用法:
[gcc concatenation](http://gcc.gnu.org/onlinedocs/gcc-4.3.3/cpp/Concatenation.html#Concatenation)

时间: 2024-11-03 22:14:03

关于宏##的使用注意一点的相关文章

EMMC终将被UFS替代?宏旺半导体的一点小分析

现今只要说到手机闪存,就会提到eMMC与UFS,宏旺半导体这两款嵌入式存储flash均有生产,手机使用的闪存不同,手机价格差别也是蛮大的,撇去其它参数,两者在手机应用上的体验就是速度差异.谈到体验,我们就不得不提一下eMMC 5.1.UFS 2.0.UFS 2.1三者实际使用时的速度,一般来说,eMMC 5.1的速度会在200MB/s左右,UFS2.0则可以达到500MB/s左右,而UFS2.1的速度更是高达700+MB/s.单从数值上看,eMMC 5.1和UFS 2.1之间相差2倍之多.从上面

[转帖]微内核 宏内核

https://www.cnblogs.com/libao/articles/2748867.html Linux 是宏内核,指导linus 开发的 minix 其实是 微内核, 微内核稳定 但是影响性能. Windows NT 是微内核 单位后期为了提高性能 兼有宏内核的特性了. 微内核与宏内核比较内核按照体系结构分为两类:微内核(microkernel)与宏内核(macrokernel). 微内核的系统有WindowNT,Minix,Mach,etc.宏内核的系统有Unix,Linux,et

七个高效的文本编辑习惯(以Vim为例)

七个高效的文本编辑习惯 如果你花很多时间输入纯文本.写程序或HTML,那么通过高效地使用一个好的编辑器,你可以节省大部分时间.本文将提供指导和提示,让你更迅速地做这些工作,并且少犯错误. 本文用开源文本编辑器Vim(Vi IMproved)来演示如何高效编辑,本文方法同样适用于其他的编辑器.选择合适的编辑器,实际上是进行高效编辑的第一步.我们避免去讨论哪个编辑器最适合你,因为这个话题将占用太多篇幅.如果你不知道选用那个编辑器,或者你对目前使用的编辑器不满意,那就试试Vim:你将不会失望. 第1部

最佳vim技巧

最佳vim技巧----------------------------------------# 信息来源----------------------------------------www.vim.org         : 官方站点comp.editors        : 新闻组http://www.newriders.com/books/opl/ebooks/0735710015.html : Vim书籍http://vimdoc.sourceforge.net/cgi-bin/vim

为mit scheme添加for循环语句

Mit-Scheme不支持syntax-case, 只能用它的er-macro-transformer来编写.Mit-Scheme的宏系统比较低级,不支持模式匹配和literal. 使用pmatch能得到一个可用的模式匹配,为了简洁这里使用了pmatch,没有pmatch也可以编写同样的宏,但啰嗦一点.没有literal就导致代码里面满是反引号和逗号.完成的代码看起来很啰嗦,但编写时却比较简单,先写一个与使用syntax-case时相同的transformer,然后再重命名. 宏不会对参数求值.

Rust中文翻译21

4.7 错误处理 有时候程序会发生错误.对于不可避免的事情发生时最好有一个计划来处理.Rust有丰富的处理错误的方法. 你的程序会出现两种类型的错误:失败和崩溃.我们先讨论两者的区别,然后讨论如何处理他们.然后,我们讨论把错误升级为崩溃. Page 107 4.7.1 失败和崩溃 Rust使用两种错误的类型:失败和崩溃.失败是一种可以被挽回的错误.崩溃却不行. "挽回"是什么意思呢?在多数情况下,很有可能会发生错误.例如,一个parse函数: "5".parse()

《卓有成效的程序员》----读书笔记

目录: 快捷键的使用 文件快速检索 1.本书概况 转自图灵社区 一.机制1.加速法则 :使用各种工具或技巧,使自己工作效率最优化关注本质,而非形式键盘输入总比导航快 简化启动面板,将最常用功能最优先化花点时间来学习你手边所有隐藏的快捷键环境切换会消耗时间,尽量减少环境切换记住操作的历史,忘记历史就意味着你得重新再输入一遍嵌入图形化工具的命令提示符让你鱼与熊掌兼得在资源管理器中嵌入命令提示符使环境切换更容易开发时优先使用键盘而非鼠标-----------------快速切换至地址栏 Alt+D在上

C++学习 之 初识命名空间

声明:            本人自学C++, 没有计算机基础,在学习的过程难免会出现理解错误,出现风马牛不相及的现象,甚至有可能会贻笑大方. 如果有幸C++大牛能够扫到本人的博客,诚心希望大牛能给予批评与指正!不胜感激!            学习的过程分为初识.入门.进阶三个阶段.            因为对C++没有什么了解,这样的学习设定可能也有失准确性.望兄弟们多指点.谢谢! 目录:科普:标识符.作用域1. 命令空间出现的由来及什么是命名空间及全局命名空间2. 命令空间的调用3. 定

【译】PHP 内核 — 字符串管理

(Strings management: zend_string 译文) 原文地址:http://www.phpinternalsbook.com/php7/internal_types/strings/zend_strings.html 原文仓库:https://github.com/phpinternalsbook/PHP-Internals-Book 原文作者:phpinternalsbook 译文出自:https://github.com/suhanyujie 本文永久链接: 译者:su