コンパイラがセミコロンがないことを報告しないのはなぜですか?


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 2016年

10
@TavianBarnesの発見可能性。この種の問題を検索しても、もう1つの質問は見つかりません。その方法で編集することもできますが、少し変更する必要があるため、IMOとはまったく別の質問になります。
一部のプログラマー、

4
@TavianBarnes:元の質問はエラーを求めていました。この質問は、コンパイラーが(少なくともOPに対して)エラーの場所を誤って報告しているように見える理由を尋ねています。
TonyK 2016年

80
熟考する:コンパイラーが欠落しているセミコロンを体系的に検出できれば、言語は最初からセミコロンを必要としません。
Euro Micelli、2016年

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

コンパイラーは欠落しているセミコロンを検出できませんが、まったく関係のないエラーを間違った行に報告します。エラーが報告されている行をいくら見ても、エラーは発生しないため、これは重要です。このような問題では、前の行を調べて、問題がなくエラーがないかどうかを確認する必要がある場合があります。

エラーを見つけるために別のファイルを調べる必要がある場合もあります。たとえば、ヘッダーファイルが構造を定義している場合、ヘッダーファイルで最後に行う構造で、構造を終了するセミコロンがない場合、エラーはヘッダーファイルではなく、ヘッダーファイルを含むファイルにあります。

そして、時にはさらに悪化します:2つ(またはそれ以上)のヘッダーファイルをインクルードし、最初のファイルに不完全な宣言が含まれている場合、おそらく2番目のヘッダーファイルに構文エラーが示されます。


これに関連するのは、フォローアップエラーの概念です。一部のエラーは、通常セミコロンがないために発生し、複数のエラーとして報告されます。最初のエラーを修正すると複数のエラーが消える可能性があるため、エラーを修正するときは上から始めることが重要です。

もちろん、これにより、一度に1つのエラーが修正され、頻繁に再コンパイルされるため、大規模なプロジェクトでは扱いにくい場合があります。このようなフォローアップエラーの認識は、経験に伴うものですが、数回目にした後で、実際のエラーを掘り下げて、再コンパイルごとに複数のエラーを修正する方が簡単です。


16
C ++では、オーバーロードされた場合に有効な式になるtemp = *a * a = *b 可能性がありますoperator*。(ただし、質問には「C」というタグが付けられています。)
dan04

13
@ dan04:誰かが実際にそうしたなら... NOPE!
ケビン

2
(a)最初に報告されたエラーから開始することに関するアドバイスのための+1。(b)エラーが報告された場所から後方を見る。エラーが報告される前の行を自動的に見ると、あなたは本物のプログラマであることがわかります:-)
TripeHound

@TripeHound特に、非常に多数のエラーがある場合、または以前にコンパイルされた行がエラーをスローしている場合...
Tin Wizard

1
通常、メタの場合と同様に、誰かがすでに尋ねた- meta.stackoverflow.com/questions/266663/...
落語- Unslanderモニカ

27

コンパイラーが欠落しているセミコロンを検出しないのはなぜですか?

覚えておくべきことが3つあります。

  1. Cの行末は、通常の空白です。
  2. *Cでは、単項演算子と二項演算子の両方を使用できます。単項演算子としては「逆参照」を意味し、二項演算子としては「乗算」を意味します。
  3. 単項演算子と二項演算子の違いは、それらが見られるコンテキストから決定されます。

これら2つの事実の結果は、解析したときです。

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

最初と最後*は単項として解釈されますが、2番目は*はバイナリとして解釈されます。構文の観点からは、これは問題ありません。

エラーが発生するのは、コンパイラーがオペランドタイプのコンテキストで演算子を解釈しようとする解析後のみです。


4

上記のいくつかの良い答えですが、詳しく説明します。

temp = *a *a = *b;

これは実際のケースでx = y = z;両方xyの値が割り当てられますz

あなたが言っていることですthe contents of address (a times a) become equal to the contents of b, as does temp

つまり、*a *a = <any integer value>有効なステートメントです。前に指摘したように、1 *つ目はポインターを逆参照し、2つ目は2つの値を乗算します。


3
逆参照が優先されるため、(アドレスのコンテンツa)回(aへのポインター)です。コンパイルエラーは、「バイナリ*( 'struct S'と 'struct S *'を持っている)には無効なオペランドです」という2つのタイプを示しているため、わかります。
dascandy 2016年

割り当ての順序は本当に私の答えのポイントではありませんでしたが、私のコードはありませんboolsので、C99を事前:-)しかし、あなたは良い点(1)を作るのですか
MawgはREINSTATEモニカ言う

1
しかし、この場合、yは変数ではなく、式*a *aであり、乗算の結果に割り当てることはできません。
Barmar

@Barmarは確かにそうですが、コンパイラーはそこまで到達しません。割り当て演算子を調べる前に、「バイナリ*」へのオペランドは無効であるとすでに決めています。
プラグウォッシュ2017

3

ほとんどのコンパイラはソースファイルを順番に解析し、何かが間違っていることを発見した行を報告します。Cプログラムの最初の12行は、有効な(エラーのない)Cプログラムの開始である可能性があります。プログラムの最初の13行ではできません。一部のコンパイラーは、発生したもの自体のエラーではない場所を記録し、ほとんどの場合、コードの後半でエラーをトリガーしませんが、他の何かとの組み合わせでは有効でない場合があります。例えば:

int foo;
...
float foo;

宣言int foo;自体は完全に問題ないでしょう。同様に宣言float foo;。一部のコンパイラーは、最初の宣言が出現した行番号を記録し、情報メッセージをその行に関連付けて、プログラマーが以前の定義が実際に誤ったものであるケースを識別できるようにします。コンパイラーは、のようなものに関連付けられた行番号を保持することもdoできます。これは、関連付けられたものwhileが適切な場所に表示されない場合に報告されます。ただし、問題が発生する可能性の高い場所が、エラーが検出された行の直前である場合、コンパイラーは通常、その位置に関する追加のレポートを追加する必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.