【问题标题】:Preprocessor invalid preprocessor token error预处理器无效的预处理器令牌错误
【发布时间】:2013-05-20 18:28:34
【问题描述】:

我正在读一本书来学习 C。在那本书中,下面的示例代码给出了 gcc (Debian 4.7.2-4) 4.7.2 的预处理器错误。错误是:

file.c:在函数“main”中:
file.c:16:14: 错误:令牌“我知道 C 语言。\n”在预处理器表达式中无效
file.c:20:14: error: token ""I know BASIC.\n"" 在预处理器表达式中无效

代码是:

#include <stdio.h>

#define C_LANG    'C'
#define B_LANG    'B'
#define NO_ERROR  0

int main(void)
{
   #if C_LANG == 'C' && B_LANG == 'B'
     #undef C_LANG
     #define C_LANG "I know the C language.\n"
     #undef B_LANG
     #define B_LANG "I know BASIC.\n"
     printf("%s%s", C_LANG, B_LANG);
   #elif C_LANG == 'C'
     #undef C_LANG
     #define C_LANG "I only know C language.\n"
     printf("%s", C_LANG);
   #elif B_LANG == 'B'
     #undef B_LANG
     #define B_LANG "I only know BASIC.\n"
     printf("%s", B_LANG);
   #else
     printf("I don't know C or BASIC.\n");
   #endif

   return NO_ERROR;
}

是 gcc 预处理器无法正确执行此操作,还是代码有问题需要更改?

【问题讨论】:

  • 我认为是 printfs 搞砸了。
  • 不,是#elif 行有问题。例如,预处理器将第一个#elif 扩展为#elif "I know the C language.\n" == 'C',然后失败了。 ideone fails it too.
  • 到目前为止给出的任何一个答案都可以。但是,我必须说,如果这是书中的一个例子,您可能需要重新考虑您使用的是哪本书。
  • @cebarth:我认为你是对的。这本书是 Sam 在 24 小时内自学 C。这对于 bash 脚本编写来说非常棒,并且在几年前让我在我的道路上做得很好。当 Apress 的书 C:从新手到专业让我难以理解时,这确实帮助我掌握了指针,但它使用的是带有未知编译器的 Windows。也许预处理器“更聪明”,这让我为 gcc 感到难过。这是第 23 章,因此还有 1 章。我会完成它,但肯定会阅读其他书籍。
  • @narnie I-am-not-a-C-guru 但我建议这样的预处理器代码并不常见/不可取。我认为 gcc 似乎在评估 elif 中的条件表达式很奇怪,即使之前的 if 已被检查为非零,但我可能会建议这种边缘情况并不重要.我想如果你想要这样的代码,你至少会想要使用两个不同的宏定义,就像 BLUEPIXY 建议的那样。另外,我认为如果这本书一开始就明确说明它使用的是不同的编译器,那你不能真的因此而责备它。

标签: c gcc c-preprocessor


【解决方案1】:

正如@cebarth 指出的那样,问题是在第一个#if 中重新定义C_LANGB_LANG 后,#elif 子句失败,因为扩展是:

   #elif "I know the C language.\n" == 'C'
   /*...*/
   #elif "I know BASIC.\n" == 'B'

C 标准对 #if#elif (C99 6.10.1) 这么说:

表单的预处理指令
# if constant-expression new-line groupopt
# elif constant-expression new-line groupopt
检查控制常量表达式的计算结果是否为非零。

没有提到因为之前的检查成功而不评估表达式。

解决此问题的一种方法是在 printf() 之后重新定义它们。

     #undef C_LANG
     #define C_LANG "I know the C language.\n"
     #undef B_LANG
     #define B_LANG "I know BASIC.\n"
     printf("%s%s", C_LANG, B_LANG);
     #undef C_LANG
     #define C_LANG 'C'
     #undef B_LANG
     #define B_LANG 'B'

解决此问题的另一种方法是明确使用#else 而不是#elif

   #if C_LANG == 'C' && B_LANG == 'B'
     #undef C_LANG
     #define C_LANG "I know the C language.\n"
     #undef B_LANG
     #define B_LANG "I know BASIC.\n"
     fprintf(stdout, "%s%s", C_LANG, B_LANG);
   #else
     #if C_LANG == 'C'
       #undef C_LANG
       #define C_LANG "I only know C language.\n"
       printf("%s", C_LANG);
     #elif B_LANG == 'B'
       #undef B_LANG
       #define B_LANG "I only know BASIC.\n"
       printf("%s", B_LANG);
     #else
       printf("I don't know C or BASIC.\n");
     #endif
   #endif

【讨论】:

  • 这不是真正的问题:为什么预处理器在if成功后执行elif代码?
  • 我不认为它正在执行它,而只是解析该行。不过看起来有点傻。
  • @roliu:C 标准只说#if#elif 表达式被求值,并没有给出任何例外,因为某些先前的#if#elif 已经匹配.我会更新答案。
  • “没有提到由于之前的检查成功而不评估表达式。” - 是的,但是GCC does mention it"Then the text after each '#elif' is processed only if the '#elif' condition succeeds after the original '#if' and all previous '#elif' directives within it have failed"
  • @Mike,我认为“文本之后”是指条件块,而不是表达式本身(即“groupopt”,而不是 C99 6.10.1 第 3 项中的“常量表达式”)跨度>
【解决方案2】:
#include <stdio.h>

#define C_LANG    'C'
#define B_LANG    'B'
#define NO_ERROR  0

int main(void)
{
   #if C_LANG == 'C' && B_LANG == 'B'
     #define C_LANG_VALUE "I know the C language.\n"
     #define B_LANG_VALUE "I know BASIC.\n"
     printf("%s%s", C_LANG_VALUE, B_LANG_VALUE);
   #elif C_LANG == 'C'
     #define C_LANG_VALUE "I only know C language.\n"
     printf("%s", C_LANG_VALUE);
   #elif B_LANG == 'B'
     #define B_LANG_VALUE "I only know BASIC.\n"
     printf("%s", B_LANG_VALUE);
   #else
     printf("I don't know C or BASIC.\n");
   #endif

   return NO_ERROR;
}

【讨论】:

    【解决方案3】:

    通常混合预处理器代码和非预处理器代码不是一个好主意,因为很难遵循执行路径(嗯,大多数时候)。

    对于您的特定示例,您可以采取一些措施使事情变得更容易:

    #define C_LANG 
    #define B_LANG  
    #define NO_ERROR
    
    #if defined(C_LANG) || defined (B_LANG)
       #if defined(C_LANG)
           printf ("I know the C language.\n");
       #else
           printf ("I know the BASIC language.\n");
       #endif
    #else
       printf("I don't know C or BASIC.\n");
    #endif
    

    无需使用宏定义。您只需在 C_LANG 或 B_LANG 中添加一个字符即可更改打印的内容:

    #define C_LANGn
    #define B_LANGn 
    

    这样代码可读性更高。

    【讨论】:

      猜你喜欢
      • 2012-12-10
      • 2012-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多