2013-05-20 113 views
1

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

file.c: In function ‘main’:
file.c:16:14: error: token ""I know the C language.\n"" is not valid in preprocessor expressions
file.c:20:14: error: token ""I know BASIC.\n"" is not valid in preprocessor expressions

的代码是:

#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的预处理程序不能正确地做这个或者是蹊跷的是需要改变的代码?

+0

我在想这是打乱了事情的printfs。 – Marvo

+1

不,这是#elif行有问题。例如,预处理器正在将第一个#elif扩展为'#elif“我知道C语言。\ n”=='C''然后失败。 [ideone也不行。](http://ideone.com/TkzvEk) – cebarth

+0

到目前为止给出的答案都可以使用。但是,我必须说,如果这是一本书的例子,那么您可能需要重新考虑您正在使用的书。 – cebarth

回答

2

由于@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):

Preprocessing directives of the forms
# if constant-expression new-line groupopt
# elif constant-expression new-line groupopt
check whether the controlling constant expression evaluates to nonzero.

没有不评价,因为提早办理入住手续的表达何况有成功。

解决此问题的一种方法是在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 
+2

不是真正的问题:为什么预处理器在'if'成功后执行'elif'代码? – rliu

+0

我不认为它正在执行它,而只是解析该行。虽然看起来有点傻。 – cebarth

+0

@roliu:C标准只说明##if和#elif表达式被评估,并且不会因为之前的#if或elif已经匹配而给出任何异常。我会更新答案。 – jxh

0
#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; 
} 
0

一般混合预处理代码和非预处理程序代码是不是一个好主意,因为它很难跟随执行路径(当然,最次)。

为了您的具体的例子有什么可以做,以使事情更容易:

#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 

这样的代码是更具可读性。