質問
このスレッドでは、CまたはC ++での goto
の適切な使用例を示します。人々が投票した回答に触発されました彼らは私が冗談を言っていると思った。
概要(意図をより明確にするために元のラベルを変更):
infinite_loop:
// code goes here
goto infinite_loop;
他の選択肢よりも優れている理由:
- 具体的です。
goto
は を引き起こす言語構造 無条件分岐。代替案 構造の使用に依存 条件分岐のサポート、 縮退した常に真 条件。 - ラベルは意図を文書化する 余分なコメントなし。
- リーダーはスキャンする必要はありません
初期の
break
の介在コード (ただし、 想定外のハッカーがシミュレートする 初期のgoto
でcontinue
)。
ルール:
- ゴトホフェスはしなかったふりをする 勝つ。上記のことが理解されています 実際のコードでは使用できないため 確立されたイディオムに反します。
- 私たち全員が聞いたことがあると仮定します 「後藤は有害だ」と知っている そのgotoを使って書くことができます スパゲッティコード。
- 例に同意しない場合、 技術的なメリットで批判する 単独(「人々は嫌いだから goto 'は技術的な理由ではありません)。
これについて、大人のように話せるかどうか見てみましょう。
編集
この質問は終了したようです。質の高い回答が生成されました。みんなありがとう、
特に私の小さなループの例を真剣に受け止めた人たち。ほとんどの懐疑論者が懸念していた
ブロックスコープがないためです。 @quinmarsがコメントで指摘したように、中括弧をいつでも囲むことができます。
ループ本体。 for(;;)
および while(true)
は中括弧を与えないことに注意してください
どちらでも無料です(そしてそれらを省略すると厄介なバグが発生する可能性があります)。とにかく、もう無駄にしない
このささいなことであなたの脳力の-私は無害で慣用的な for(;;)
と while(true)
で生きることができますジョブ)。
他の回答を考えると、多くの人が goto
をいつもあなたのものと見ているようです。
別の方法で書き換える必要があります。もちろん、ループを導入することで goto
を回避できますが、
追加のフラグ、ネストされた if
のスタック、またはその他、ただし、 goto
が
おそらく仕事に最適なツールですか?別の言い方をすれば、意図された目的のために組み込みの言語機能を使用することを避けるために、人々はどれほどさを我慢しているのでしょうか?私の意見は
フラグを追加するだけでも高すぎて支払うことができません。変数を使って物事を表すのが好きです
問題または解決策のドメイン。 「 goto
を避けるためだけに」はカットしません。
クリーンアップブロックに分岐するためのCパターンを与えた最初の回答を受け入れます。 IMO、これは、投稿されたすべての回答の goto
の最も強力なケースになります。
あなたがそれを避けるために憎しみが通過しなければならないゆがみによってそれを測定するならば。
解決
ここで、人々が使用していると聞いたことがあります。私は野生でそれを見たことがありません。また、C ++にはこれをより慣用的に行うRAIIがあるため、Cにのみ適用されます。
void foo()
{
if (!doA())
goto exit;
if (!doB())
goto cleanupA;
if (!doC())
goto cleanupB;
/* everything has succeeded */
return;
cleanupB:
undoB();
cleanupA:
undoA();
exit:
return;
}
他のヒント
CでのGOTOの典型的な必要性は次のとおりです
for ...
for ...
if(breakout_condition)
goto final;
final:
gotoなしでネストされたループから抜け出す簡単な方法はありません。
これは、シグナルによって中断される可能性のあるUnixシステムコール用の(Stevens APITUEからの)私の愚かな例です。
restart:
if (system_call() == -1) {
if (errno == EINTR) goto restart;
// handle real errors
}
代替方法は縮退ループです。このバージョンは、英語の「システムコールがシグナルによって中断された場合、それを再起動します」のようになります。
Duffのデバイスがgotoを必要としない場合、どちらも必要ありません! ;)
void dsend(int count) {
int n;
if (!count) return;
n = (count + 7) / 8;
switch (count % 8) {
case 0: do { puts("case 0");
case 7: puts("case 7");
case 6: puts("case 6");
case 5: puts("case 5");
case 4: puts("case 4");
case 3: puts("case 3");
case 2: puts("case 2");
case 1: puts("case 1");
} while (--n > 0);
}
}
Wikipediaの上記のコードエントリ。
Knuthは、「GOTOステートメントを使用した構造化プログラミング」という論文を執筆しました。 こちらから。多くの例があります。
非常に一般的。
do_stuff(thingy) {
lock(thingy);
foo;
if (foo failed) {
status = -EFOO;
goto OUT;
}
bar;
if (bar failed) {
status = -EBAR;
goto OUT;
}
do_stuff_to(thingy);
OUT:
unlock(thingy);
return status;
}
goto
を使用する唯一のケースは、通常はブロックから飛び出し、決してブロックに飛び込むことではありません。これにより、 do {} while(0)
およびネストを増やすその他の構成要素の悪用を回避しながら、読み取り可能な構造化コードを維持します。
gotoに対して一般的なことは何もありませんが、あなたが述べたようなループにそれらを使用したくないいくつかの理由を考えることができます:
- スコープを制限しないため、内部で使用する一時変数は後まで解放されません。
- スコープを制限しないため、バグにつながる可能性があります。
- スコープを制限しないため、将来同じコードで同じ変数名を将来のコードで再利用することはできません。
- スコープを制限しないため、変数宣言をスキップする可能性があります。
- 人々はそれに慣れていないため、コードが読みにくくなります。
- このタイプのネストされたループはスパゲッティコードにつながる可能性がありますが、法線ループはスパゲッティコードにつながりません。
gotoを使用する適切な場所の1つは、いくつかのポイントで中断できるプロシージャです。各ポイントでは、さまざまなレベルのクリーンアップが必要です。 Gotophobesは、gotoを構造化コードと一連のテストで常に置き換えることができますが、過度のインデントを排除するため、これはより簡単だと思います:
if (!openDataFile()) goto quit; if (!getDataFromFile()) goto closeFileAndQuit; if (!allocateSomeResources) goto freeResourcesAndQuit; // Do more work here.... freeResourcesAndQuit: // free resources closeFileAndQuit: // close file quit: // quit!
@ fizzer.myopenid.com:投稿されたコードスニペットは次と同等です:
while (system_call() == -1)
{
if (errno != EINTR)
{
// handle real errors
break;
}
}
間違いなくこの形式が好きです。
やがてこのパターンを嫌うようになりましたが、COMプログラミングに組み込まれています。
#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error}
...
HRESULT SomeMethod(IFoo* pFoo) {
HRESULT hr = S_OK;
IfFailGo( pFoo->PerformAction() );
IfFailGo( pFoo->SomeOtherAction() );
Error:
return hr;
}
良いgotoの例を次に示します。
// No Code
gotoが正しく使用されているのを見てきましたが、状況は通常見苦しいです。 goto
の使用自体が元のものよりもはるかに少ない場合にのみそうです。
@Johnathon Hollandの問題は、あなたはバージョンがあまり明確ではないということです。人々はローカル変数が怖いようです:
void foo()
{
bool doAsuccess = doA();
bool doBsuccess = doAsuccess && doB();
bool doCsuccess = doBsuccess && doC();
if (!doCsuccess)
{
if (doBsuccess)
undoB();
if (doAsuccess)
undoA();
}
}
そして、私はこのようなループを好むが、一部の人々は while(true)
を好む。
for (;;)
{
//code goes here
}
これに関する私の不満は、ブロックスコープを失うことです。 gotoの間に宣言されたローカル変数は、ループが抜け出した場合に有効のままになります。 (たぶん、ループは永遠に実行されると仮定しているのでしょう。しかし、元の質問作成者が尋ねていたとは思わないでしょう。)
スコーピングの問題は、C ++の問題です。一部のオブジェクトは、適切なタイミングで呼び出されるdtorに依存している可能性があるためです。
gotoを使用する一番の理由は、マルチステップの初期化プロセスで、失敗した場合にすべての初期化を取り消すことが不可欠であるためです:
if(!foo_init())
goto bye;
if(!bar_init())
goto foo_bye;
if(!xyzzy_init())
goto bar_bye;
return TRUE;
bar_bye:
bar_terminate();
foo_bye:
foo_terminate();
bye:
return FALSE;
gotoを自分で使用することはありませんが、特定の場合にgotoを使用することがある人とは一度仕事をしました。私の記憶が正しければ、彼の理論的根拠はパフォーマンスの問題に関するものでした。彼はまた、方法についての特定のルールも持っていました。常に同じ関数内にあり、ラベルは常にgotoステートメントの下にありました。
#include <stdio.h>
#include <string.h>
int main()
{
char name[64];
char url[80]; /*The final url name with http://www..com*/
char *pName;
int x;
pName = name;
INPUT:
printf("\nWrite the name of a web page (Without www, http, .com) ");
gets(name);
for(x=0;x<=(strlen(name));x++)
if(*(pName+0) == '\0' || *(pName+x) == ' ')
{
printf("Name blank or with spaces!");
getch();
system("cls");
goto INPUT;
}
strcpy(url,"http://www.");
strcat(url,name);
strcat(url,".com");
printf("%s",url);
return(0);
}
@Greg:
次のような例をしないのはなぜですか:
void foo()
{
if (doA())
{
if (doB())
{
if (!doC())
{
UndoA();
UndoB();
}
}
else
{
UndoA();
}
}
return;
}