C 预处理器无效预处理器令牌错误
我正在读一本书来学习C。在这本书中,以下示例代码给出了gcc(Debian 4.7.2-4)4.7.2的预处理器错误。错误是: file.c:在函数“main”中: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
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