C stringizer操作符#

C stringizer操作符#,c,C,#运算符(称为stringizer运算符)的用途是什么, 如何使用它 我的书用下面的方式描述,秃顶部分是我不理解的 stringizer运算符#后面必须跟一个参数,它们被替换为从参数的标记构造而来的字符串文字,这些标记不是首先替换的,即以下输入: #define W 124 #define str(a) (#a) str(W) 生成输出: “W” 如果我们希望使用带有宏定义的字符串文字,则必须使用两个类似函数的宏: #define W 124 #define xstr(a) (#a)

#运算符(称为stringizer运算符)的用途是什么, 如何使用它

我的书用下面的方式描述,秃顶部分是我不理解的

stringizer运算符#后面必须跟一个参数,它们被替换为从参数的标记构造而来的字符串文字,这些标记不是首先替换的,即以下输入:

#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”]

现在