컴파일러가 누락 된 세미콜론을보고하지 않는 이유는 무엇입니까?


115

이 간단한 프로그램이 있습니다.

#include <stdio.h>

struct S
{
    int i;
};

void swap(struct S *a, struct S *b)
{
    struct S temp;
    temp = *a    /* Oops, missing a semicolon here... */
    *a = *b;
    *b = temp;
}

int main(void)
{
    struct S a = { 1 };
    struct S b = { 2 };

    swap(&a, &b);
}

예를 들어 ideone.com에서 볼 수 있듯이 이것은 오류를 제공합니다.

prog.c: In function 'swap':
prog.c:12:5: error: invalid operands to binary * (have 'struct S' and 'struct S *')
     *a = *b;
     ^

컴파일러가 누락 된 세미콜론을 감지하지 못하는 이유는 무엇입니까?


참고 :이 질문과 답변은 이 질문에 의해 동기가 부여되었습니다 . 이와 유사한 다른 질문 이 있지만 C 언어의 자유 형식 용량에 대해 언급 한 내용이 없는데 이것이이 문제와 관련 오류의 원인입니다.


16
이 게시물의 동기는 무엇입니까?
R Sahu

10
@TavianBarnes 발견 가능성. 다른 질문은 이런 종류의 문제를 검색 할 때 찾을 수 없습니다. 그렇게 편집 할 수는 있지만 조금씩 변경해야하므로 완전히 다른 질문이 될 것입니다. IMO.
일부 프로그래머 친구

4
@TavianBarnes : 원래 질문은 오류에 대한 질문이었습니다. 이 질문은 컴파일러가 (적어도 OP에) 오류 위치를 잘못보고하는 것처럼 보이는 이유를 묻습니다.
TonyK

80
숙고 할 점 : 컴파일러가 누락 된 세미콜론을 체계적으로 감지 할 수 있다면 언어 는 처음에 세미콜론이 필요하지 않을 것입니다.
Euro Micelli

5
컴파일러 작업은 오류를보고하는 것입니다. 오류를 수정하기 위해 무엇을 변경해야하는지 파악하는 것은 귀하의 임무입니다.
David Schwartz

답변:


213

C는 자유 형식 언어입니다. 즉, 여러 가지 방법으로 형식을 지정할 수 있으며 여전히 법적 프로그램이 될 것입니다.

예를 들어 다음과 같은 진술

a = b * c;

다음과 같이 쓸 수있다

a=b*c;

또는 좋아

a
=
b
*
c
;

그래서 컴파일러가 라인을 볼 때

temp = *a
*a = *b;

의미한다고 생각한다

temp = *a * a = *b;

이것은 물론 유효한 표현식이 아니며 컴파일러는 누락 된 세미콜론 대신 이에 대해 불평 할 것입니다. 유효하지 않은 이유 a는 구조에 대한 포인터 이기 때문에 *a * a구조 인스턴스 ( *a)에 구조 에 대한 포인터 ( ) 를 곱하려고합니다 a.

컴파일러는 누락 된 세미콜론을 감지 할 수 없지만 잘못된 줄에 완전히 관련없는 오류를보고합니다. 오류가보고 된 줄을 아무리 봐도 오류가 없기 때문에주의해야합니다. 때때로 이와 같은 문제는 이전 줄을 살펴보고 오류가 없는지 확인해야합니다.

때로는 오류를 찾기 위해 다른 파일을 찾아야 할 수도 있습니다. 예를 들어 헤더 파일이 헤더 파일에서 마지막으로 구조를 정의하고 구조를 종료하는 세미콜론이 누락 된 경우 오류는 헤더 파일이 아니라 헤더 파일이 포함 된 파일에 있습니다.

그리고 때로는 더 나빠질 수 있습니다. 두 개 (또는 그 이상의) 헤더 파일을 포함하고 첫 번째 파일에 불완전한 선언이 포함 된 경우 대부분 구문 오류가 두 번째 헤더 파일에 표시됩니다.


이와 관련된 것은 후속 오류 의 개념입니다 . 일반적으로 실제로 세미콜론이 누락되어 발생하는 일부 오류는 여러 오류 로보고됩니다 . 첫 번째 오류를 수정하면 여러 오류가 사라질 수 있으므로 오류를 수정할 때 처음부터 시작하는 것이 중요합니다.

물론 이것은 한 번에 하나의 오류를 수정하고 대규모 프로젝트에서는 번거로울 수있는 빈번한 재 컴파일로 이어질 수 있습니다. 이러한 후속 오류를 인식하는 것은 경험과 함께 제공되는 것이며 몇 번 본 후에 실제 오류를 찾아 내고 재 컴파일 당 하나 이상의 오류를 수정하는 것이 더 쉽습니다.


16
C ++에서 오버로드 된 경우 유효한식이 될 temp = *a * a = *b 있습니다 operator*. (그래도 질문은 "C"로 태그가 지정됩니다.)
dan04

13
@ dan04 : 누군가 실제로 그랬다면 ... 안돼!
Kevin

2
(a) 처음보고 된 오류로 시작하는 것에 대한 조언에 +1; 그리고 (b) 오류가보고 된 곳에서 뒤를 돌아 봅니다. 오류가보고되기 전에 자동으로 줄을 보면 당신이 진짜 프로그래머 라는 것을 알고 있습니다. :-)
TripeHound

@TripeHound 매우 큰 오류 수, 또는 이전에 오류를 던지고있다 컴파일 라인 ... 거기에 특히
주석 마법사

1
일반적으로 메타의 경우와 마찬가지로, 누군가가 이미 요청 - meta.stackoverflow.com/questions/266663/...
이야기꾼 - Unslander 모니카

27

컴파일러가 누락 된 세미콜론을 감지하지 못하는 이유는 무엇입니까?

기억해야 할 세 가지가 있습니다.

  1. C의 줄 끝은 일반적인 공백입니다.
  2. *C에서는 단항 및 이항 연산자가 될 수 있습니다. 단항 연산자로서 "역 참조"를 의미하고, 이항 연산자로서 "곱하기"를 의미합니다.
  3. 단항 연산자와 이항 연산자의 차이는 표시되는 컨텍스트에 따라 결정됩니다.

이 두 가지 사실의 결과는 파싱 할 때입니다.

 temp = *a    /* Oops, missing a semicolon here... */
 *a = *b;

첫 번째와 마지막 *은 단항으로 해석되지만 두 번째 *는 이진으로 해석됩니다. 구문 관점에서 보면 괜찮아 보입니다.

컴파일러가 피연산자 유형의 컨텍스트에서 연산자를 해석하려고 할 때만 구문 분석 후에 오류가 표시됩니다.


4

위에서 몇 가지 좋은 답변이 있지만 자세히 설명하겠습니다.

temp = *a *a = *b;

이것은 실제로 와 x = y = z;둘 다 의 값이 할당되는 경우입니다 .xyz

당신이 말하는 것은 the contents of address (a times a) become equal to the contents of b, as does temp입니다.

요컨대 *a *a = <any integer value>유효한 진술입니다. 이전에 지적했듯이 첫 번째 *는 포인터를 역 참조하고 두 번째는 두 값을 곱합니다.


3
역 참조가 우선하므로 (주소 a의 내용) 시간 (a에 대한 포인터)입니다. 컴파일 오류가 "이진 *에 대한 유효하지 않은 피연산자 ( 'struct S'및 'struct S *'가 있음)"라는 두 가지 유형이 있기 때문에 알 수 있습니다.
dascandy

I 코드, C99을 사전에 전혀 bools 그래서 :-) 그러나 할당의 순서가 정말 내 대답의 요점 아니었지만 당신이 좋은 점 (+1)를 어떻게해야합니까
Mawg는 분석 재개 모니카 말한다

1
그러나이 경우 y에는 변수도 아니고 표현식 *a *a이고 곱셈의 결과에 할당 할 수 없습니다.
Barmar

@Barmar 실제로하지만 컴파일러는 그렇게 멀리 가지 않습니다. 할당 연산자를보기 전에 "binary *"에 대한 피연산자가 유효하지 않다고 이미 결정했습니다.
플러그 워시

3

대부분의 컴파일러는 소스 파일을 순서대로 구문 분석하고 문제가 있음을 발견 한 줄을보고합니다. C 프로그램의 처음 12 줄은 유효한 (오류없는) C 프로그램의 시작일 수 있습니다. 프로그램의 처음 13 줄은 할 수 없습니다. 일부 컴파일러는 자체적으로 오류가 아닌 항목의 위치를 ​​기록하고 대부분의 경우 코드에서 나중에 오류를 트리거하지 않지만 다른 항목과 결합하여 유효하지 않을 수 있습니다. 예를 들면 :

int foo;
...
float foo;

선언 int foo;자체만으로도 완벽 할 것입니다. 마찬가지로 선언 float foo;. 일부 컴파일러는 첫 번째 선언이 나타난 줄 번호를 기록하고 정보 메시지를 해당 줄과 연결하여 프로그래머가 이전 정의가 실제로 잘못된 경우를 식별하도록 도울 수 있습니다. 컴파일러는 또한 a와 같은 항목과 연결된 줄 번호를 유지할 do수 있으며, 연결된 항목 while이 올바른 위치에 나타나지 않는 경우보고 될 수 있습니다 . 그러나 문제의 가능성있는 위치가 오류가 발견 된 줄 바로 앞에있는 경우 컴파일러는 일반적으로 위치에 대한 추가 보고서를 추가하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.