Programming

이 1988 C 코드의 문제점은 무엇입니까?

procodes 2020. 8. 25. 21:01
반응형

이 1988 C 코드의 문제점은 무엇입니까?


저는 "The C Programming Language"(K & R) 책에서이 코드를 컴파일하려고합니다. UNIX 프로그램의 베어 본 버전입니다 wc.

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

이것은 새로운 표준입니까 아니면 유형없는 메인을 사용할 수 있습니까?


문제는 당신의 처리기 정의 함께 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위 코드에는 모호함 이 없습니다 .


이 코드의 주요 문제점 은 K & R의 코드 아니라는 것입니다 . 다른 사람들이 지적했듯이 의미를 변경하는 책에는없는 매크로 정의 뒤에 세미콜론이 포함됩니다.

코드를 이해하기 위해 변경하는 경우를 제외하고는 코드를 이해할 때까지 그대로 두어야합니다. 이해하는 코드 만 안전하게 수정할 수 있습니다.

This was probably just a typo on your part, but it does illustrate the need for understanding and attention to details when programming.


There should not be any semicolons after the macros,

#define IN   1     /* inside a word */
#define OUT  0     /* outside a word */

and it should probably be

if (c == ' ' || c == '\n' || c == '\t')

The definitions of IN and OUT should look like this:

#define IN   1     /* inside a word  */
#define OUT  0     /* outside a word */

The semicolons were causing the problem! The explanation is simple: both IN and OUT are preprocessor directives, essentially the compiler will replace all occurrences of IN with a 1 and all occurrences of OUT with a 0 in the source code.

Since the original code had a semicolon after the 1 and the 0, when IN and OUT got replaced in the code, the extra semicolon after the number produced invalid code, for instance this line:

else if (state == OUT)

Ended up looking like this:

else if (state == 0;)

But what you wanted was this:

else if (state == 0)

Solution: remove the semicolon after the numbers in the original definition.


As you see there was a problem in macros.

GCC has option for stopping after pre-processing. (-E) This option is useful to see the result of pre-processing. In fact the technique is an important one if you are working with large code base in c/c++. Typically makefiles will have a target to stop after pre-processing.

For quick reference : The SO question covers the options -- How do I see a C/C++ source file after preprocessing in Visual Studio?. It starts with vc++, but also has gcc options mentioned down below.


Not exactly a problem, but the declaration of main() is also dated, it should be like something this.

int main(int argc, char** argv) {
    ...
    return 0;
}

The compiler will assume an int return value for a function w/o one, and I'm sure the compiler/linker will work around the lack of declaration for argc/argv and the lack of return value, but they should be there.


Try adding explicit braces around code blocks. The K&R style can be ambiguous.

Look at line 18. The compiler is telling you where the issue is.

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

A simple way is to use brackets like {} for each if and else:

if (c == '\n'){
    ++nl;
}
if (c == ' ' || c == '\n' || c == '\t')
{
    state = OUT;
}
else if (state == OUT) {
    state = IN;
    ++nw;
}

As other answers pointed out, the problem is in #define and semicolons. To minimize these problems I always prefer defining number constants as a const int:

const int IN = 1;
const int OUT = 0;

This way you get rid of many problems and possible problems. It is limited by just two things:

  1. Your compiler has to support const - which in 1988 wasn't generally true, but now it's supported by all commonly used compilers. (AFAIK the const is "borrowed" from C++.)

  2. You can't use these constants in some special places where you would need a string-like constant. But I think your program isn't that case.

참고URL : https://stackoverflow.com/questions/8640818/whats-wrong-with-this-1988-c-code

반응형