Что случилось с этим кодом C 1988 года?
Я пытаюсь скомпилировать этот фрагмент кода из книги "Язык программирования C" (K и R). Это версия версии UNIX
#include <stdio.h>
#define IN 1; /* inside a word */
#define OUT 0; /* outside a word */
/* count lines, words and characters in input */
main()
{
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF) {
++nc;
if (c == '\n')
++nl;
if (c == ' ' || c == '\n' || c == '\t')
state = OUT;
else if (state == OUT) {
state = IN;
++nw;
}
}
printf("%d %d %d\n", nl, nw, nc);
}
И я получаю следующую ошибку:
$ gcc wc.c
wc.c: In function ‘main’:
wc.c:18: error: ‘else’ without a previous ‘if’
wc.c:18: error: expected ‘)’ before ‘;’ token
Второе издание этой книги с 1988 года, и я довольно новичок в C. Возможно, это связано с версией компилятора или, может быть, я просто говорю глупости.
Я видел в современном C-коде другое использование функции main
:
int main()
{
/* code */
return 0;
}
Является ли это новым стандартом или могу ли я использовать основной тип?
Ответы
Ответ 1
Ваша проблема связана с вашими определениями препроцессора IN
и OUT
:
#define IN 1; /* inside a word */
#define OUT 0; /* outside a word */
Обратите внимание, как у вас есть конечная точка с запятой в каждом из них. Когда препроцессор расширяет их, ваш код будет выглядеть примерно так:
if (c == ' ' || c == '\n' || c == '\t')
state = 0;; /* <--PROBLEM #1 */
else if (state == 0;) { /* <--PROBLEM #2 */
state = 1;;
Эта вторая точка с запятой заставляет else
не иметь предыдущего if
в качестве соответствия, потому что вы не используете фигурные скобки. Итак, удалите точки с запятой из определений препроцессора IN
и OUT
.
Урок, извлеченный здесь, состоит в том, что инструкции препроцессора не должны заканчиваться точкой с запятой.
Кроме того, вы всегда должны использовать фигурные скобки!
if (c == ' ' || c == '\n' || c == '\t') {
state = OUT;
} else if (state == OUT) {
state = IN;
++nw;
}
В вышеприведенном коде не существует неопределенной погрешности else
.
Ответ 2
Основная проблема с этим кодом заключается в том, что это не код из K & R. Он содержит точки с запятой после определений макросов, которых нет в книге, которые, как указывали другие, меняют смысл.
За исключением случаев, когда вы пытаетесь понять код, вы должны оставить его в покое, пока не поймете это. Вы можете только безопасно изменять код, который понимаете.
Вероятно, это была опечатка с вашей стороны, но она иллюстрирует необходимость понимания и внимания к деталям при программировании.
Ответ 3
После макросов не должно быть точек с запятой,
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
и, вероятно, это будет
if (c == ' ' || c == '\n' || c == '\t')
Ответ 4
Определения IN и OUT должны выглядеть следующим образом:
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
Точки с запятой вызывают проблему! Объяснение простое: как IN, так и OUT являются препроцессорными директивами, по сути, компилятор заменит все вхождения IN одним и всеми вхождениями OUT с 0 в исходном коде.
Поскольку исходный код имел точку с запятой после 1 и 0, когда IN и OUT были заменены в коде, дополнительная точка с запятой после номера вызывала неверный код, например, эту строку:
else if (state == OUT)
Завершен следующим образом:
else if (state == 0;)
Но вы хотели, чтобы это было:
else if (state == 0)
Решение: удалите точку с запятой после чисел в исходном определении.
Ответ 5
Не совсем проблема, но декларация main()
также датирована, она должна быть чем-то подобной.
int main(int argc, char** argv) {
...
return 0;
}
Компилятор примет значение int для функции w/o, и я уверен, что компилятор/компоновщик будет работать вокруг отсутствия объявления для argc/argv и отсутствия возвращаемого значения, но они должны быть есть.
Ответ 6
Как вы видите, в макросах возникла проблема.
GCC имеет опцию остановка после предварительной обработки. (-E) Эта опция полезна, чтобы увидеть результат предварительной обработки. На самом деле техника важна, если вы работаете с большой базой кода в c/С++. Обычно make файлы будут иметь цель для остановки после предварительной обработки.
Для быстрой справки: вопрос SO охватывает варианты - Как увидеть исходный файл C/С++ после предварительной обработки в Visual Studio?. Он начинается с vС++, но также имеет параметры gcc, указанные ниже.
Ответ 7
Попробуйте добавить явные скобки вокруг кодовых блоков. Стиль K & R может быть неоднозначным.
Посмотрите на строку 18. Компилятор сообщает вам, где проблема.
if (c == '\n') {
++nl;
}
if (c == ' ' || c == '\n' || c == '\t') { // You're missing an "=" here; should be "=="
state = OUT;
}
else if (state == OUT) {
state = IN;
++nw;
}
Ответ 8
Простым способом является использование скобок типа {} для каждого if
и else
:
if (c == '\n'){
++nl;
}
if (c == ' ' || c == '\n' || c == '\t')
{
state = OUT;
}
else if (state == OUT) {
state = IN;
++nw;
}
Ответ 9
Как указывалось в других ответах, проблема заключается в #define
и точках с запятой. Чтобы свести к минимуму эти проблемы, я всегда предпочитаю определять числовые константы как const int
:
const int IN = 1;
const int OUT = 0;
Таким образом вы избавляетесь от многих проблем и возможных проблем. Он ограничен только двумя вещами:
-
Ваш компилятор должен поддерживать const
- который в 1988 году, как правило, не был прав, но теперь он поддерживается всеми обычно используемыми компиляторами. (AFAIK const
"заимствован" из С++.)
-
Вы не можете использовать эти константы в некоторых специальных местах, где вам понадобится строка-подобная константа. Но я думаю, что ваша программа не в этом.