4

私には一種の奇妙な願いがあります。そこにあるコンパイラまたは言語拡張機能でこれが許可されているかどうかはわかりません。

次のように、関数呼び出し内で変数を宣言できるようにしたいと考えています。

int test(int *out_p) {
    *out_p = 5;
    return 1;
}

int main()
{
    if (int ret = test(int &var)) { // int var declared inside function invocation
        fprintf(stderr, "var = %d\n", var); // var in scope here
    }
    return 0;
}

var のスコープは ret のスコープに従うためです。別の例として (私が現在取り組んでいるプロジェクトから)、私は

cmd_s = readline();
int x, y, dX, dY, symA, symB;
if (sscanf(cmd_s, "placeDomino:%d %d atX:%d y:%d dX:%d dY:%d",
                           &symA, &symB, &x,  &y,   &dX,  &dY) == 6) {
    do_complicated_stuff(symA, symB, x, y, dX, dY);
} else if (sscanf(cmd_s, "placeAtX:%d y:%d dX:%d dY:%d", &x, &y, &dX, &dY) == 4) {
    do_stuff(x, y, dX, dY);
    /* symA, symB are in scope but uninitialized :-( so I can accidentally
     * use their values and the compiler will let me */
}

そして、私は書きたいと思います

cmd_s = readline();
if (sscanf(cmd_s, "placeDomino:%d %d atX:%d y:%d dX:%d dY:%d",
                    int &symA, int &symB, int &x, int &y, int &dX, int &dY) == 6) {
    do_complicated_stuff(symA, symB, x, y, dX, dY);
} else if (sscanf(cmd_s, "placeAtX:%d y:%d dX:%d dY:%d", int &x, int &y, int &dX, int &dY) == 4) {
    do_stuff(x, y, dX, dY);
    /* Now symA, symB are out of scope here and I can't
     * accidentally use their uninitialized values */
}

私の質問は、これをサポートするコンパイラはありますか? 正しい方法でこすれば、gccはそれをサポートしますか? これを含む C または C++ (ドラフト) 仕様はありますか?

編集: 私の最初のコード例では、私の int ret の宣言も C99 では役に立たないことに気付きました。私はforループに甘やかされていると思います。私もその機能が欲しいです。想像

while(int condition = check_condition()) {
    switch(condition) {
        ...
    }
}

またはそのようなもの。

4

4 に答える 4

4

C99 では、ブロック スコープ宣言以外に、変数を宣言する方法が基本的に 2 つあります。これらの変数は、定義上、変数が出現するステートメントに制限されています。

  • 複合リテラルは の形式(type name){ initializers }で、現在のブロックに存在するローカル変数を宣言します。たとえば、関数呼び出しの場合は、 を使用できますtest(&(int){ 0 })
  • forスコープ変数は、forステートメント自体と依存するステートメントまたはブロックのスコープのみを持ちます。

ローカルif変数を使用した式は、次のような奇妙なことを行うことができます

for (bool cntrl = true; cntrl; cntrl = false)
   for (int ret = something; cntrl && test(&ret); cntrl = false) {
      // use ret inside here
   }

そのようなものはすぐに読めなくなるので注意してください。一方、オプティマイザーはそのようなコードを非常に効率的に本質的なものに減らしtest、ブロックの内側forが一度しか評価されないことを簡単に見つけます。

于 2012-02-27T10:34:00.727 に答える
0

これをサポートするコンパイラはありません。これがどこに意味があるのか​​わかりません。

ソースコードの行数を減らすことは、必ずしもより効率的なプログラムにつながるとは限りません。これはよくある誤解です。Cの場合の99%では、このようなステートメントをよりコンパクトなステートメントに書き直すことは意味がありません。それは読みにくいコードにつながるだけで、最終的にはまったく同じマシンコードを取得します。

あなたがすべきことはこれです:

void some_func (void) // good example
{
  ... lots of code here

  int ret;
  int var;

  ret = test(&var);
  if(ret == SOMETHING)
  {
    fprintf(stderr, "var = %d\n", var); // var in scope here
  }
}

あなたがすべきではないことはこれです:

void some_func (void) // bad example
{
  ... lots of code here

  {
    int ret;
    int var;

    if((ret = test(&var))
    {
      fprintf(stderr, "var = %d\n", var); // var in scope here
    }
  }
}

良い例と悪い例では、まったく同じマシンコードが生成されます。これを理解することは非常に重要です!

まず第一に、悪い例で変数のスコープを縮小しても、より効果的なプログラムにはなりません。コンパイラは、変数が初めて使用されたときと、それ以上使用されなくなったときを非常によく知ることができます。どちらの例でも、コンパイラは変数retとvarをCPUレジスタまたはスタックに格納します。

また、変数が関数の途中(C99 / C11のみ)で宣言されているか、最初(C90またはC99 / C11)で宣言されているかは、効率にとってはまったく問題ではないことに注意してください。スコープの途中で変数を宣言することは、プログラミングスタイルの機能にすぎません。コードの読者に、この変数がこの時点から重要になり始めることを伝えています。コンパイラは、人間がコードを読み取るのとは対照的に、宣言をどこに書き込んだかを気にしません。

retを省略し、test()の結果を確認しただけでも、違いはありませんでした。関数の結果は、プログラマーによって明示的に宣言された変数または暗黙的に一時変数のいずれかに保存する必要があります。コンパイラによって作成されます。マシンコードは同じですが、ret変数がない場合はデバッグが難しくなります。

例の主な違いは、悪い例には、条件内の割り当て(MISRA-C:2004 13.1)や非ブール変数のゼロに対する暗黙のテスト(MISRA-C:2004 13.2)など、広く認識されている悪いプログラミング手法が含まれていることです。 。それに、不必要であいまいな追加のローカルスコープを追加します。

したがって、私のアドバイスは、いくつかのアセンブラー(または逆アセンブル)を研究し、Cコードが実際にマシンコードに変換される方法を学ぶことです。それを知っていると、コードを改善しているという誤った仮定でコードを不必要に難読化する必要性を感じることはありません。

于 2012-02-27T14:29:16.737 に答える
0

次のように、空のスコープを使用します。

int test(int *out_p) {
    *out_p = 5;
    return 1;
}

int main()
{
    {
        int var, ret;
        if (ret = test(&var)) {
            fprintf(stderr, "var = %d\n", var); // var in scope here
        }
    }
    // var not in scope
    return 0;
}
于 2012-02-27T10:19:53.703 に答える
0
while(int condition = check_condition()) {
    switch(condition) {
        ...
    }
}

また

if (int ret = test(int &var))

私の質問は、これをサポートするコンパイラはありますか? 正しい方法でこすれば、gccはそれをサポートしますか? これを含む C または C++ (ドラフト) 仕様はありますか?

ifこれは C ではありません。ステートメントまたはステートメントの句はwhile式でなければならず、宣言にすることはできません。

for最初の節の反復ステートメントについては、C99 以降の宣言のみを使用できます。

for (clause-1; expression-2; expression-3)

句-1 は、宣言または式にすることができます。

于 2012-02-27T10:33:15.747 に答える