C 预处理器无效预处理器令牌错误

C 预处理器无效预处理器令牌错误,c,gcc,c-preprocessor,C,Gcc,C Preprocessor,我正在读一本书来学习C。在这本书中,以下示例代码给出了gcc(Debian 4.7.2-4)4.7.2的预处理器错误。错误是: file.c:在函数“main”中: file.c:16:14:错误:标记“我知道c语言。\n”在预处理器表达式中无效 file.c:20:14:错误:标记“I know BASIC.\n”在预处理器表达式中无效 代码是: #include <stdio.h> #define C_LANG 'C' #define B_LANG 'B' #def

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

file.c:在函数“main”中:
file.c:16:14:错误:标记“我知道c语言。\n”在预处理器表达式中无效
file.c:20:14:错误:标记“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;
}
#包括
#定义C_LANG'C'
#定义B_LANG“B”
#定义无错误0
内部主(空)
{
#如果C_LANG='C'和&B_LANG=='B'
#undef C_LANG
#定义C_LANG“我懂C语言。\n”
#布朗德酒店
#定义B_LANG“我知道基本知识。\n”
printf(“%s%s”,C_LANG,B_LANG);
#elif C_LANG=='C'
#undef C_LANG
#定义C_LANG“我只懂C语言。\n”
printf(“%s”,C_LANG);
#elif B_LANG=='B'
#布朗德酒店
#定义B_LANG“我只知道基本知识。\n”
printf(“%s”,B_LANG);
#否则
printf(“我不知道C或BASIC。\n”);
#恩迪夫
返回无错误;
}

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

正如@cebarth指出的,问题在于,在第一个
中重新定义
C_LANG
B_LANG
之后,
\elif
子句失败,因为扩展是:

   #elif "I know the C language.\n" == 'C'
   /*...*/
   #elif "I know BASIC.\n" == 'B'
C标准对
#if
#elif
(C99 6.10.1)作了如下说明:

表单的预处理指令
#如果常量表达式为新行groupopt

#elif常量表达式新行groupopt

检查控制常量表达式的计算结果是否为非零


没有提到因为先前的检查已成功而不计算表达式

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

另一种解决方法是显式使用
#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
#包括
#定义C_LANG'C'
#定义B_LANG“B”
#定义无错误0
内部主(空)
{
#如果C_LANG='C'和&B_LANG=='B'
#定义C语言值“我知道C语言。\n”
#定义B_LANG_值“我知道基本信息。\n”
printf(“%s%s”,C语言值,B语言值);
#elif C_LANG=='C'
#定义C语言值“我只懂C语言。\n”
printf(“%s”,C_LANG_值);
#elif B_LANG=='B'
#定义B_LANG_值“我只知道基本信息。\n”
printf(“%s”,B_LANG_值);
#否则
printf(“我不知道C或BASIC。\n”);
#恩迪夫
返回无错误;
}

一般来说,混合使用预处理器代码和非预处理器代码不是一个好主意,因为很难遵循执行路径(大多数情况下)

对于您的特定示例,您可以做一些事情来简化:

#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
不需要使用宏定义。您只需在eiter C_LANG或B_LANG中添加一个字符即可更改打印内容:

#define C_LANGn
#define B_LANGn 

这样代码的可读性就更好了。

我认为是printfs把事情搞砸了。不,是elif行出了问题。例如,预处理器正在将第一个#elif扩展为
#elif“我懂C语言。\n”==“C”
,然后失败。到目前为止给出的任何一个答案都是可行的。然而,我必须说,如果这是一本书中的一个例子,你可能需要重新考虑你正在使用的书。@cebarth:我认为你是对的。这本书是山姆在24小时内自学C的。这对于bash脚本编写来说是一个很好的例子,几年前,它让我在这方面走得很好。这确实帮助了我在一本关于C:从新手到专业人士的书中找到了一些要点,但它使用的是Windows和一个未知的编译器。也许预处理器“更聪明”,这让我为gcc感到难过。这是第23章,因此还有1章。我会完成它,但肯定也会读其他的书。@narnie I-am-not-a-C-guru,但我认为这样的预处理器代码并不常见/可取。我觉得奇怪的是,
gcc
似乎在
elif
中计算条件表达式,即使前面的
if
已被检查为非零,但我可能会建议这种边缘情况并不重要。我想如果你想要这样的代码,你至少应该使用两种不同的宏定义,就像BLUEPIXY建议的那样。另外,我认为如果这本书一开始就明确指出它使用的是另一个编译器,那么你就不能因此而指责它。真正的问题不是:如果
成功,为什么预处理器在
之后执行
elif
代码?我不认为它在执行它,而只是在解析行。roliu:C标准只规定对
#if
#elif
表达式进行求值,并且没有给出任何异常,因为之前的
#if
#elif
已经匹配。我将更新答案。“没有提到因为先前的检查成功而不计算表达式。”-对,但是<代码>“那么,只有在原始的“#if”和其中所有以前的“#elif”指令失败后“#elif”条件成功时,才会处理每个“#elif”后的文本”
@Mike,我认为“text after”指的是条件块,而不是表达式本身(即“groupopt”,而不是C99 6.10.1第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
#define C_LANGn
#define B_LANGn