代码之家  ›  专栏  ›  技术社区  ›  Francisco Ryan Tolmasky I

宏正确展开,但出现“预期表达式”错误

  •  8
  • Francisco Ryan Tolmasky I  · 技术社区  · 10 年前

    我对我的问题做了一个小小的简化:

    #define STR_BEG "
    #define STR_END "
    
    int main()
    {
        char * s = STR_BEG abc STR_END;
    
        printf("%s\n", s);
    }
    

    编译时,我收到以下错误:

    static2.c:12:16: error: expected expression
        char * s = STR_BEG abc STR_END;
                   ^
    static2.c:7:17: note: expanded from macro 'STR_BEG'
    #define STR_BEG "
    

    现在,如果我 只是 运行预处理器, gcc -E myfile.c ,我得到:

    int main()
    {
        char * s = " abc ";
    
    
        printf("%s\n", s);
    }
    

    这正是我想要的,也是完全合法的结果代码。那怎么回事?

    1 回复  |  直到 10 年前
        1
  •  10
  •   Alex Celeste    10 年前

    宏没有真正“正确”扩展,因为这不是一个有效的C预处理器程序。正如Kerrek所说,预处理器没有 相当地 处理任意字符序列-它处理整个令牌。令牌是标点符号、标识符、数字、字符串等,其形式(或多或少)与构成有效C代码的形式相同。这些定义没有描述有效的字符串,它们会打开字符串,但在行尾之前无法关闭字符串。因此,一个无效的令牌序列被传递给预处理器。事实上,它能够从一个无效的程序中产生输出,这一点可以说是很方便的,但这并不能使其正确,而且几乎可以肯定,它最多只能保证预处理器的垃圾输出。您需要终止字符串,以便它们形成完整的令牌-现在它们形成垃圾输入。

    要在引号中实际包装标记或标记序列,请使用字符串化运算符 # :

    #define STRFY(A) #A
    STRFY(abc) // -> "abc"
    

    GCC和类似的编译器会在编译时警告您类似的错误 使用 -Wall 标志已启用。

    (我假设您只在尝试以C编译时出错,但在两次编译时不会出错,因为在编译器内部,它保留了这些是“坏”标记的信息,如果您写出一个中间文件,然后在第二次编译中编译预处理的源代码,则会丢失这些信息……如果是,这是一个实现细节,不要依赖它。)


    实际问题的一个可能解决方案如下:

    #define LPR (
    #define start STRFY LPR
    #define end )
    #define STRFY(A) #A
    #define ID(...) __VA_ARGS__
    
    ID(
      char * s = start()()()end;  // -> char * s = "()()()";
    )
    

    这个 ID 不过,包装器是必要的。没有这一点是无法做到的(它可以绕过任何行,甚至是整个程序,但它必须存在,原因在其他问题中都有很好的阐述)。