2013-05-20 2 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; 
} 

ли НКУ препроцессор не в состоянии сделать это правильно или это что-то не так с кодом, который нужно изменить?

+0

Я думаю, что это printfs, которые возились. – Marvo

+1

Нет, это строки #elif, которые имеют проблемы. Например, препроцессор расширяет первый #elif до '#elif" Я знаю язык C. \ N "== 'C'', а затем не получается. [ideone тоже терпит неудачу.] (http://ideone.com/TkzvEk) – cebarth

+0

Любой из ответов, данных до сих пор, будет работать. Однако, я должен сказать, если это пример из книги, вы можете пересмотреть, какую книгу вы используете. – cebarth

ответ

2

Как @cebarth указывает, проблема заключается в том, что после того, как вы переопределены C_LANG и B_LANG в первом #if, то #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

Не настоящий вопрос: почему препроцессор выполняет код 'elif' после того, как' if' преуспевает? – 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 

Таким образом, код намного более удобным для чтения.

Смежные вопросы