宏扩展正确,但给了我"期望的表达"错误

时间:2014-07-24 00:18:58

标签: c macros c-preprocessor

我已经轻而易举地减少了我的问题:

#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 个答案:

答案 0 :(得分:10)

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

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

#define STRFY(A) #A
STRFY(abc) // -> "abc"
如果你在启用-Wall标志的情况下编译预处理,那么GCC和类似的编译器会警告你这样的错误。

(我假设你只是在尝试编译为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包装是必要的。没有它,没有办法做到这一点(它可以绕过任意数量的行,甚至是你的整个程序,但它必须存在的原因在其他问题中得到很好的解释)。