C stringizer操作符#
#运算符(称为stringizer运算符)的用途是什么, 如何使用它 我的书用下面的方式描述,秃顶部分是我不理解的 stringizer运算符#后面必须跟一个参数,它们被替换为从参数的标记构造而来的字符串文字,这些标记不是首先替换的,即以下输入:C stringizer操作符#,c,C,#运算符(称为stringizer运算符)的用途是什么, 如何使用它 我的书用下面的方式描述,秃顶部分是我不理解的 stringizer运算符#后面必须跟一个参数,它们被替换为从参数的标记构造而来的字符串文字,这些标记不是首先替换的,即以下输入: #define W 124 #define str(a) (#a) str(W) 生成输出: “W” 如果我们希望使用带有宏定义的字符串文字,则必须使用两个类似函数的宏: #define W 124 #define xstr(a) (#a)
#define W 124
#define str(a) (#a)
str(W)
生成输出:
“W”
如果我们希望使用带有宏定义的字符串文字,则必须使用两个类似函数的宏:
#define W 124
#define xstr(a) (#a)
#define str(b) (xstr(b))
哪一个产生输出
“124”
原因是在str的替换列表中替换参数之前,参数b的参数被完全替换,这意味着调用xstr将使用124作为参数,然后在xstr中字符串化,以便理解这一点,想想预处理器将如何处理这些事情。在第一个例子中,我们可以看到
#define W 124
#define str(a) (#a)
str(W)
将按如下方式处理str(W)->#W->“W”
现在我们来看第二个例子
#define W 124
#define xstr(a) (#a)
#define str(b) (xstr(b))
str(W)
将按如下方式处理:
str(W)->xstr(124)->#124
,最后是“124”
要理解这一点,请考虑预处理器将如何处理这些事情。在第一个例子中,我们可以看到
#define W 124
#define str(a) (#a)
str(W)
将按如下方式处理str(W)->#W->“W”
现在我们来看第二个例子
#define W 124
#define xstr(a) (#a)
#define str(b) (xstr(b))
str(W)
将按如下方式处理:str(W)->xstr(124)->#124
,最后“124”
。#a强制编译器将a的值(在本例中是宏参数)放在双引号中。字符串的这种引用发生在宏中的替换之前。这样你就得到了“W”
您可以通过将源代码传递给C预处理器来看到这一点;这里不需要有有效的C程序。在Linux主机上,忽略cpp产生的“#”和空行绒毛,我们得到:
宏字符串.c
(“W”)
添加间接级别允许扩展W值。由于外部宏中没有“#”,因此在扩展内部宏之前,该宏中会发生替换,从而创建字符串
如果注释掉xstr定义,它可能会变得更直观:
#define W 124
/* #define xstr(a) (#a) */
#define str(b) (xstr(b))
cpp macro-strings.c
(xstr(124))
#定义W 124
/*定义xstr(a)(#a)*/
#定义str(b)(xstr(b))
宏字符串.c
(xstr(124))
删除注释会生成正确的((“124”))
请记住,编译器会将相邻的双引号字符串组合成一个字符串:
"Hello," " " "world!" is the same as "Hello, world!"
“你好”,“世界!”与“你好,世界!”
当宏中生成一个或多个字符串时,这可能变得非常重要 a强制编译器将a的值(在本例中是宏参数)放在双引号中。字符串的这种引用发生在宏中的替换之前。这样你就得到了“W”
您可以通过将源代码传递给C预处理器来看到这一点;这里不需要有有效的C程序。在Linux主机上,忽略cpp产生的“#”和空行绒毛,我们得到:
宏字符串.c
(“W”)
添加间接级别允许扩展W值。由于外部宏中没有“#”,因此在扩展内部宏之前,该宏中会发生替换,从而创建字符串
如果注释掉xstr定义,它可能会变得更直观:
#define W 124
/* #define xstr(a) (#a) */
#define str(b) (xstr(b))
cpp macro-strings.c
(xstr(124))
#定义W 124
/*定义xstr(a)(#a)*/
#定义str(b)(xstr(b))
宏字符串.c
(xstr(124))
删除注释会生成正确的((“124”))
请记住,编译器会将相邻的双引号字符串组合成一个字符串:
"Hello," " " "world!" is the same as "Hello, world!"
“你好”,“世界!”与“你好,世界!”
当宏中生成一个或多个字符串时,这可能变得非常重要 如果时间不是什么大问题,那么理解往往来自“自己动手”的方法。在这里,如果问题是“预处理器在做什么?”,为什么不编写一个简化的玩具预处理器并研究结果呢。我忘了名字,但有一条“法律”,说“只有你能实施它,你才能真正理解” 当然,编写一个成熟的C/C++预处理器并不是很有吸引力。因此,下面的代码试图获得一个足够大的简化模型的最低限度的实现,它能够展示基本上正在发生的事情。选择了一种简洁的语言来保持行数较低。如果您求助于抽象语法树级别而不是使用语法,则不需要解析器
type MacroExpr =
| MacroDef of string * string list * MacroExpr
| MacroCall of string * (string * MacroExpr) list
| String of string
| Stringize of MacroExpr
| ArgRef of string
let rec run (macros : MacroExpr list) (call : MacroExpr) : MacroExpr =
let value_of arg_name =
match call with
| MacroCall(n,args) -> snd (List.find (fun a -> fst a = arg_name) args)
| _ -> failwith "bla bla"
match call with
| MacroCall(n,al) ->
let m = macros |> List.find (fun m -> match m with | MacroDef(n1,_,_) -> n1 = n | _ -> false)
let mname,margs,mbody =
match m with
| MacroDef(a,b,c) -> a,b,c
| _ -> failwithf "macros argument contains something else: %A" m
let rec x_to_string = function
| String s -> s
| Stringize x -> x_to_string x
| ArgRef(an) -> x_to_string (eval (snd(al |> List.find (fun (n,_) -> n = an))))
| MacroDef(_,_,_) -> failwith "No nested macros supported."
| MacroCall(n,_) -> n
and eval = function
| String s -> String s
| Stringize x -> String (x_to_string x)
| ArgRef(an) -> String (x_to_string (snd(al |> List.find (fun (n,_) -> n = an))))
| MacroDef(_,_,_) -> failwith "No nested macros supported."
| MacroCall(n,al1) ->
run macros (MacroCall(n, al1 |> List.map (fun (an,ax) -> an,eval ax)))
match mbody with
| ArgRef(an) -> snd(al |> List.find (fun (n,_) -> n = an))
| Stringize(x) -> String(x_to_string x)
| String s -> String s
| MacroCall(mn,al1) ->
run macros (MacroCall(mn, al1 |> List.map (fun (an,ax) -> an,eval ax)))
| MacroDef(_,_,_) -> failwithf "No nested macros supported. Found one in %s: %A" n mbody
| _ -> failwithf "'call' argument shall be a MacroCall expression but is a %A" call
let macros =
[
MacroDef ("W", [], String "124")
MacroDef ("str1", ["a"], Stringize (ArgRef "a"))
MacroDef ("str2", ["a"], MacroCall("str1", ["a",ArgRef "a"]))
]
let calls =
[
MacroCall("W",[])
MacroCall("str1",["a",MacroCall("W",[])])
MacroCall("str2",["a",MacroCall("W",[])])
]
calls
|> List.map (fun c -> run macros c)
macros
变量包含我们要探索的预定义宏的列表。它们是根据(最小、低技术)MacroExpr
类型定义的
calls
变量包含使用相同技术的宏调用列表
即使我们现在忽略函数run
的实现,我们也会看到一些有趣的东西:
“str2”的宏定义将其参数“a”转发给对str1的宏调用。ArgRef-对“str2”参数列表的引用。遵循急切求值原则的语言不会将该ArgRef传递给下一个宏求值,而是首先尝试解析它。毕竟,否则“str1”的计算必须知道调用方“str2”的arglist
因此,从str2调用str1的参数是[“a”,MacroCall(“W”,[])]
,而直接调用带有[“a”,MacroCall(“W”,[])]
的宏str1首先通过计算宏“W”到字符串“124”
来解决
代码的最后两行运行所有调用,生成的结果与预期不符:
val it:MacroExpr list=[String“124”;String“124”;String“W”]
现在