Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/127.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 试图理解C预处理器_C++_C_C Preprocessor_Stringification - Fatal编程技术网

C++ 试图理解C预处理器

C++ 试图理解C预处理器,c++,c,c-preprocessor,stringification,C++,C,C Preprocessor,Stringification,为什么这些代码块会产生不同的结果 一些常见代码: #define PART1PART2 works #define STRINGAFY0(s) #s #define STRINGAFY1(s) STRINGAFY0(s) 案例1: #define GLUE(a,b,c) a##b##c STRINGAFY1(GLUE(PART1,PART2,*)) //yields "PART1PART2*" 案例2: #define GLUE(a,b) a##b##* STRINGAFY1(GLUE(

为什么这些代码块会产生不同的结果

一些常见代码:

#define PART1PART2 works
#define STRINGAFY0(s) #s
#define STRINGAFY1(s) STRINGAFY0(s)
案例1:

#define GLUE(a,b,c) a##b##c  
STRINGAFY1(GLUE(PART1,PART2,*))
//yields
"PART1PART2*"
案例2:

#define GLUE(a,b) a##b##*
STRINGAFY1(GLUE(PART1,PART2))
//yields
"works*"
案例3:

#define GLUE(a,b) a##b
STRINGAFY1(GLUE(PART1,PART2*))
//yields
"PART1PART2*"
我正在使用VS.NET2005SP1中的MSVC++

编辑: 目前我认为,在扩展宏时,预处理器的工作方式如下: 步骤1: -拿尸体 -删除##运算符周围的所有空格 -如果找到与参数名称匹配的标识符,则分析字符串: -如果它位于##运算符旁边,则将标识符替换为参数的文本值(即传入的字符串) -如果它不在##运算符旁边,请首先对参数值运行整个解释过程,然后用该结果替换标识符。 (忽略stringafy单个“#”案例) -删除所有##运算符

步骤2: -获取结果字符串并对其进行任何宏分析

现在,根据这一点,我认为所有3种情况都应该产生完全相同的结果字符串:

第1部分第2部分*

因此,在步骤2之后,应导致

工作*


但至少应该会产生同样的结果。

它完全按照你告诉它的去做。第一个和第二个将传入的符号名称粘贴到一个新符号中。第三个取2个符号并粘贴它们,然后您自己将*放在字符串中(最终将计算为其他值)

结果的问题到底是什么?你期望得到什么?这一切似乎都像我预期的那样起作用


当然,还有一个问题,你为什么要这样玩弄符号咀嚼的黑暗艺术

案例1和案例2没有定义的行为,因为您试图将
*
粘贴到一个预处理器令牌中。根据预处理器的关联规则,这会尝试将令牌
PART1PART2
(或仅
PART2
)和
*
粘合在一起。在您的情况下,这可能会无声地失败,这是未定义事物时可能出现的结果之一。标记
PART1PART2
后跟
*
将不再考虑用于宏扩展。然后,字符串化生成您看到的结果

我的gcc在您的示例中表现不同:

/usr/bin/gcc -O0 -g -std=c89 -pedantic   -E test-prepro.c
test-prepro.c:16:1: error: pasting "PART1PART2" and "*" does not give a valid preprocessing token
"works*"
因此,总结一下您的案例1有两个问题

  • 粘贴两个没有结果的标记 在有效的预处理器令牌中
  • ##
    运算符的求值顺序
在案例3中,编译器给出了错误的结果。它应该

  • 评估要使用的参数
    STRINGAFY1
  • 为此,它必须展开
    GLUE
  • GLUE
    产生
    PART1PART2*
  • 必须再次扩大
  • 结果是
    works*
  • 然后传递给
    STRINGAFY1

  • 据我所知,在宏扩展器粘贴/替换了中的参数,并整理出所有粘贴“##”运算符之后,所有三个运算符都应该产生完全相同的字符串“PART1PART2*”,考虑到在将扩展体解析为子宏之前应该如何进行此操作,我希望所有3oh是的,我这样做只是为了试图准确地理解预处理器是如何工作的,我永远不会写这样糟糕的代码:)构造
    a##b
    不会展开
    a
    b
    ,而
    \c
    不会展开
    c
    。请看,Philipp不太确定您在那里说的是什么,也就是说##运算符不允许在粘贴单个参数之前对其进行扩展,我的不会单独扩展到任何内容,我希望粘贴后的结果字符串能够扩展。现在我相信粘贴操作和对结果的解析是完全独立的操作,这就是为什么,看到粘贴操作如何在所有3种情况下产生相同的字符串,我发现奇怪的是每次的答案都不一样。前2种情况是未定义的行为,
    ##
    的结果必须是一个预处理令牌(关于构成预处理令牌的内容列表,请参见C标准第6.4节),那么案例1和案例2不应该产生相同的结果吗?什么是“有效的预处理令牌”?也许是一个标识符?在这种情况下,我看不出它们中的任何一个是如何工作的。我想案例2——唯一有效的案例——是唯一一个将合法标识符传递给所有参数的案例…@matt,没有案例1是未定义的,因此您无法知道编译器选择什么来解决该问题。预处理器应该将输入拆分为标记。有效标记是(除其他外)标识符、标点符号本身、所有两个或三个字符运算符(如
    我仍然有点搞不清楚案例1和案例2之间的区别,两者都尝试相同的操作,只是在一种情况下“”来自参数,而在另一种情况下,它是文本。考虑到literal###参数和parameter###参数的工作原理应该完全相同,我对它们如何产生不同的结果感到有点困惑。也许在案例2中,它很早就注意到“”不会创建标识符,并中止粘贴,而隐藏在参数中的编译器会被捕获吗?