118

私はこの簡単なプログラムを持っています:

#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 言語の自由形式の能力について言及しているものは見つかりませんでした。

4

5 に答える 5

222

C は自由形式の言語です。つまり、さまざまな方法でフォーマットでき、合法的なプログラムであることに変わりはありません。

たとえば、次のようなステートメント

a = b * c;

のように書くことができます

a=b*c;

または好き

a
=
b
*
c
;

したがって、コンパイラが行を見ると

temp = *a
*a = *b;

それはそれが意味すると思います

temp = *a * a = *b;

もちろん、これは有効な式ではなく、コンパイラーはセミコロンが欠落している代わりにそれについて文句を言うでしょう。有効でない理由aは、 が構造体へのポインターであるため、構造体インスタンス ( ) を構造体 ( ) へのポインターで*a * a乗算しようとしているためです。*aa

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

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

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


これに関連して、フォローアップエラーの概念があります。一部のエラーは、実際にはセミコロンの欠落が原因で、複数のエラーとして報告されます。これが、最初のエラーを修正すると複数のエラーが消える可能性があるため、エラーを修正するときに最初から始めることが重要である理由です。

もちろん、これは一度に 1 つのエラーを修正し、頻繁に再コンパイルすることになり、大規模なプロジェクトでは面倒になる可能性があります。ただし、このようなフォローアップ エラーを認識することは経験に伴うものであり、それらを数回見た後は、実際のエラーを掘り起こし、再コンパイルごとに複数のエラーを修正することが容易になります。

于 2016-10-19T15:12:29.063 に答える
30

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

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

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

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

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

最初と最後*は単項として解釈されますが、2 番目*は 2 進数として解釈されます。構文の観点からは、これで問題ないように見えます。

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

于 2016-10-19T21:13:54.413 に答える
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 つの値を乗算します。

于 2016-10-20T07:50:17.493 に答える
3

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

int foo;
...
float foo;

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

于 2016-10-20T15:16:08.267 に答える