効果のないステートメントがCで合法と見なされるのはなぜですか?


13

この質問が初心者の場合はご容赦ください。次のプログラムを検討してください。

#include <stdio.h>

int main() {
  int i = 1;
  i = i + 2;
  5;
  i;
  printf("i: %d\n", i);
}

上記の例では、ステートメント5;i;はまったく不要に見えますが、コードはデフォルトで警告やエラーなしでコンパイルされます(ただし、gccはでwarning: statement with no effect [-Wunused-value]実行すると警告をスローします-Wall)。それらはプログラムの残りの部分に影響を与えないので、なぜ彼らはそもそもなぜ有効なステートメントと見なされるのですか?コンパイラは単にそれらを無視しますか?そのような発言を許可することには何か利点がありますか?


5
そのような声明を禁止することの利点は何ですか?
Mooing Duck

2
どんな式でも;それの後に置くことでステートメントになることができます。式をステートメントにできない場合についてのルールを追加すると、言語が複雑になります
MM

3
の戻り値を無視するため、コードのコンパイルが失敗するのではprintf()ないでしょうか?声明は、5;基本的には「何でも言う5(何も)を行わず、その結果を無視します。あなたの文がprintf(...)ある『何でもprintf(...)ありませんし、結果を無視する(からの戻り値printf()』)。Cの扱いは、同じ。これには、また、コードすることができます(void) i;場所iですvoid意図的に未使用としてマークするためにキャストする関数へのパラメーター
Andrew Henle

1
@AndrewHenle:それがprintf()最終的に返される値を無視しても、呼び出しには効果があるため、これはまったく同じではありません。対照的5;に、まったく効果がありません。
Nate Eldredge

1
なぜなら、デニス・リッチー、そして彼は私たちに言うために周りにいないからです。
user207421

回答:


10

このようなステートメントを許可する利点の1つは、人間が作成するのではなく、マクロまたは他のプログラムによって作成されたコードから得られることです。

例として、int do_stuff(void)成功すると0を、失敗すると-1を返すことになっている関数を想像してください 。「もの」のサポートはオプションであるため、ヘッダーファイルを使用して

#if STUFF_SUPPORTED
#define do_stuff() really_do_stuff()
#else
#define do_stuff() (-1)
#endif

ここで、可能であれば何かを実行したいが、成功したか失敗したかを実際に気にしないかもしれないコードを想像してください。

void func1(void) {
    if (do_stuff() == -1) {
        printf("stuff did not work\n");
    }
}

void func2(void) {
    do_stuff(); // don't care if it works or not
    more_stuff();
}

ときSTUFF_SUPPORTED0であり、プリプロセッサは、呼を拡大するfunc2だけで読み込むステートメントに

    (-1);

したがって、コンパイラパスは、煩わしいと思われる一種の「余分な」ステートメントを表示します。しかし、他に何ができるでしょうか?の場合#define do_stuff() // nothing、コードfunc1は壊れます。(そして、func2readsだけの空のステートメントがまだあります。;これはおそらくさらに不必要です。)一方、do_stuff()-1を返す関数を実際に定義する必要がある場合は、関数呼び出しのコストが発生する可能性があります。正当な理由もなく。


no-opのよりクラシックなバージョン(または一般的なバージョンを意味するか)は((void)0)です。
ジョナサンレフラー

これの良い例はですassert
Neil

3

Cの単純なステートメントはセミコロンで終了します。

Cの単純なステートメントは式です。式は、変数、定数、および演算子の組み合わせです。すべての式は、変数に割り当てることができる特定のタイプの値をもたらします。

一部の「スマートコンパイラ」は5を破​​棄する可能性があると述べました。そして私; ステートメント。


それらを破棄する以外にそれらのステートメントで何かをするコンパイラーは想像できません。他に何ができるでしょうか?
Jeremy Friesner

@JeremyFriesner:非常にシンプルで最適化されていないコンパイラーは、値を計算して結果をレジスターに置くコードを生成する可能性があります(そこからは無視されます)。
Nate Eldredge

C標準には、「単純なステートメント」という用語はありません。式文は、セミコロン(別売)の式から構成されています。すべての式が値になるわけではありません。タイプの式にvoidは値がありません。
キース・トンプソン

2

許可するより禁止するほうが難しいため、効果のない発言は許可されます。これは、Cが最初に設計され、コンパイラーがより小さく単純になったときに、より適切でした。

式文はセミコロンが続く表現で構成されています。その動作は、式を評価し、結果(ある場合)を破棄することです。通常、目的は式の評価に副作用があることですが、特定の式に副作用があるかどうかを判別するのは必ずしも簡単ではなく、場合によっては不可能です。

たとえば、関数呼び出しは式なので、関数呼び出しとそれに続くセミコロンはステートメントです。このステートメントには副作用がありますか?

some_function();

の実装を見ずに見分けることは不可能ですsome_function

これはどう?

obj;

おそらくそうではありません-がobjとして定義されている場合は、そうですvolatile

許可の任意になされるべき表現式文、セミコロンを追加することで、言語の定義が簡単になります。式に副作用があることを要求すると、言語定義とコンパイラが複雑になります。Cは一貫した規則のセットに基づいて構築され(関数呼び出しは式、代入は式、式の後にセミコロンが続くステートメントです)、プログラマーが意味のないことを実行することを妨げずに、やりたいことを実行できます。


2

効果なしでリストしたステートメントは、式ステートメントの例です。その構文は、C標準のセクション6.8.3p1で次のように指定されています。

 expression-statementexpression opt  ;

セクション6.5はすべて式の定義に特化していますが、大まかに言えばは演算子にリンクされた定数と識別子で構成されています。特に、式には代入演算子が含まれる場合と含まれない場合があり、関数呼び出しが含まれる場合と含まれない場合があります。

したがって、セミコロンが後に続くすべての式は、式ステートメントとして修飾されます。実際、コードのこれらの各行は、式ステートメントの例です。

i = i + 2;
5;
i;
printf("i: %d\n", i);

一部の演算子には、代入演算子のセットや事前/事後の増分/減分演算子などの副作用が含まれており、関数呼び出し演算子に() 、問題の関数の動作によっては副作用がある場合があります。ただし、演​​算子の1つが副作用を持っている必要はありません。

次に別の例を示します。

atoi("1");

これはprintf、例の呼び出しと同じように、関数を呼び出して結果を破棄しますがprintf、関数呼び出し自体とは異なり、副作用はありません。


1

時々そのようなステートメントは非常に便利です:

int foo(int x, int y, int z)
{
    (void)y;   //prevents warning
    (void)z;

    return x*x;
}

または、リファレンスマニュアルで、レジスタを読み取って何かをアーカイブするように指示された場合-たとえば、フラグをクリアまたは設定する(uCの世界では非常に一般的な状況)

#define SREG   ((volatile uint32_t *)0x4000000)
#define DREG   ((volatile uint32_t *)0x4004000)

void readSREG(void)
{
    *SREG;   //we read it here
    *DREG;   // and here
}

https://godbolt.org/z/6wjh_5


*SREGがvolatileの場合*SREG;、C標準で指定されたモデルには影響しません。C規格では、観察可能な副作用があると規定されています。
Eric Postpischil

@EricPostpischil-いいえ、観察可能な効果はありませんが、効果がある場合。Cの可視オブジェクトはどれも変更されていません。
P__J__

C 2018 5.1.2.3 6は、プログラムの観察可能な動作を定義して、「揮発性オブジェクトへのアクセスは、抽象マシンの規則に従って厳密に評価される」と規定しています。解釈や控除の問題はありません。これは、観察可能な動作の定義です。
Eric Postpischil
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.