トップページ⇒tech
82コメント33KB

絶対ジャンプは本当に悪なのか?

■ このスレッドは過去ログ倉庫に格納されています
0001名無しさん@契約社員NGNG
Cにおける goto はホントに邪道なのだろうか?
俺個人の感覚では多重ループから一気に抜けるべきイベントが
発生したときに限り、ループカウンタをいじくって抜けるより
goto でとばした方がスッキリするのだが。
0002名無しさん@お腹いっぱい。NGNG
シグナルやWIN32構造化例外でも使って下さい。
そのためにあります。
UPSの作動で処理の中断とかはそうやってやりましょう。
0003名無しさん@お腹いっぱい。NGNG
>1
ケースバイケースが前提ですが、極力避けたいです。

とはいえ、gotoでシンプルになり、他の実装方法では実現できない
(なんてことあるのか?)/複雑になる なら禁止する必要はないのでは?

例として出されている多重ループから一気に抜ける場合には、
ループ全体を小さい関数にしてreturnで帰ることを、まず検討する
と思います。
0004名無しさん@お腹いっぱい。NGNG
>俺個人の感覚では多重ループから一気に抜けるべきイベントが
>発生したときに限り、ループカウンタをいじくって抜けるより
>goto でとばした方がスッキリするのだが。

関数にして returnした方がスッキリしませんか?

0005名無しさん@お腹いっぱい。NGNG
とあるプロジェクトで
int foo()
{
 /* 初期化処理 */
 ...

 if(xxx)
  goto exit;
 if(yyy)
  goto exit;
exit:
 /* 終了処理 */
return zzz;
}
のようなコードを多用してた人がいた。
違和感なくもないけど、このスタイルで一貫してて
読みやすかったし、バグもなかった(少なくともgoto関連では)。

gotoを使わないことだけを目的として関数ばらす
ということが必ずしも最善ではないと思う。
0006名無しさん@お腹いっぱい。NGNG
使うべきときには使う。
使うべきでないときには使わない。
それだけだ。
0007名無しさん@お腹いっぱい。NGNG
> 5
うん、俺もCで異常処理の場合はそうする。
# C++の場合は邪悪だが

ただ、今回の1の例は
関数にしてreturnの方が良いと思う
0008名無しさん@お腹いっぱい。NGNG
>6
情報量0の名言ですな
0009名無しさん@お腹いっぱい。NGNG
>>8
しかし、至言だ
0010名無しさん@お腹いっぱい。NGNG
gotoを排除するためだけに関数にするなんてアホだね。
そんな基準で関数化してたら構造がめちゃくちゃだ。
0011名無しさん@お腹いっぱい。NGNG
http://mentai.2ch.net/test/read.cgi?bbs=infosys&key=963119759&st=11&to=11
0012名無しさん@お腹いっぱい。NGNG
具体論を語れない人間の吐く抽象論はもうウンザリ。
まさしく情報量0だな。
0013名無しさん@お腹いっぱい。NGNG
>>12
どれが情報量0なんですか?
11のリンク先ですか?
疑問に思ったもので。。。煽るつもりはまったくありません。
001412NGNG
俺も煽るつもりはないよ。ホントにウンザリしてるの。

逆に聞くけどこのスレと11のリンク先で述べられた抽象論から
何か得るものあった?残念ながら俺は何も得られなかった。

それに対して10の意見なんかは漠然と感じてたことを
きっちり明文化してくれた。今後のコーディングでgotoを
使うかどうか迷ったときの重要な判断材料になると思うよ。
0015名無しさん@お腹いっぱい。NGNG
goto論争は、具体的になるとフレームになるからな。
001612NGNG
なるほど。フレームよりは情報量0のがまだましか。
0017名無しさん@お腹いっぱい。NGNG
ところで、gotoをつかわずに展開できるの証明したのって
だれだっけ。ダイキストラ先生じゃなかったよね??
001813NGNG
>>14
すいません。
「疑問に思った」というのは
「どの投稿が情報量0」なのか「疑問に思った」(確信出来なかった)んです。
ですから、「11のリンク先が情報量0という意見」に「疑問に思った」わけではありません。
誤解させてしまってすみません。

私は11のリンク先が何を言いたいのをわかるような気がしますが
具体的にどうなのよ?となるとやはり情報量0です。
>>5
>>10
などが良いです。納得出来ます。
0019名無しさん@お腹いっぱい。NGNG
思うに、此の手のスレがフレーム化してしまうのは、
価値観の違う人たちが相手をうち負かすため(だけ)に
レスをつけてしまうからだよね。

具体例提示・無批判スレとそのメタスレ(実況・批判スレ)を
分離してしまうと案外(個々人にとって)有益な結果が生まれるかもね。
0020名無しさん@お腹いっぱい。NGNG
>>19
「メタスレ(実況・批判スレ)」は、それはそれでウザイ。
煽りはムシすればいいだけ。
0021名無しさん@お腹いっぱい。NGNG
>>5 が許せるのは、goto 先が一カ所だから。

goto/jmp 等の絶対移動が「スパゲッティ」プログラムを生むのは、飛び先を見ないと
何をやっているのか分からないから。コメントはあくまでも補助にすぎない。

意味的にループするので有ればループを表す表現を使うべきで、そうすることで
読みやすく、間違えづらく、コンパイラ等への最適化のヒントを与えることができる。

だから、制御構造を実現するための絶対移動は使わないようにしていこう。というのが
いわゆる構造化プログラミングの原点。

だから、意味が明確な goto 文を禁止する必要はないし、使うときには他の構造化
表現を駆使した上で、明確な使い方(エラー処理など)にとどめるべき。

あと、簡単な規則として「ソースコードの上に戻るジャンプは禁止」ってのもある。
これはアセンブラでも、コメントを一言入れておくべき点として心に留めておくと良い。
002219NGNG
>20
>「メタスレ(実況・批判スレ)」は、それはそれでウザイ。
ウザいから分離するんじゃん。散らばったレスを無視するより、
スレ自体無視できたほうが楽だし、両スレの情報の精度も高まる。

>煽りはムシすればいいだけ。
これが出来るくらいみなさん大人ならね〜。

>21
1が言っているのはそのレベル
(if@`whileとgotoのどちらを使うべきか)の話ではない。
構造化プログラミングなんて当たり前、
その上でなおgotoを使う価値があるかということでしょ。

経験的にいってgotoについては、Cプログラマの
認識・センス・自制心は(珍しく)そんなに悪くないと思う。

好き嫌いはおいといて、例えば多重ループからgotoで抜けるコードを
読む・書く場合、そんなに理解し難いものかな?バグを誘発するかな?

もしコードが長くて入り組んでて読みづらいとしたら、
それはgotoとは関係無しに分割する必要があるということで、
gotoの有用性とはまた別の話。
0023名無しさん@お腹いっぱい。NGNG
>> 20
そうそう.
goto を使わないようにするために屑なプログラム書く奴も多いよね.

# 俺の経験では VB 出身の奴が多かった.

int afunc() {
if (error) {
/* この間100行 */
if (error) {
/* この間100行 */
} else {
success = 1;
return success;
}
}
/* エラー処理 */
return success;
}

ま@`こういう奴は goto に限らずなが〜いのを書くんだが.
0024名無しさん@お腹いっぱい。NGNG
あ@`間違えた.

int afunc() {
+ int success = 0;
if (error) {
/* この間100行 */
if (error) {
002519NGNG
>22
じゃ、終わりにしましょう。

「...悪くないと思う」
なら良いんじゃない?

「...理解し難いものかな?バグを誘発するかな? 」
どう思うのかな?
理解しやすい場合もあるし、しづらい場合もある。
バグを誘発する場合もしない場合もある。

goto が破壊的なのは、言語システム上のチェック機能がまったく
働かないからでしょう?速度的、美的な理由で、言語の制約を
乗り越えて大域ジャンプを使うのは、それなりの代償を払っている。
ということを頭に置いてコードを書かなきゃ。

で、C++ などでは、例外処理のメカニズムが追加されて、例外処理のための
大域ジャンプを特定の構文で処理できるようになり、コンパイラレベルでの
保守の範囲に入ったわけだね?

こういった、言語システム上のチェックシステムは必ずしも常に必要な
訳ではないので、解っている人は解って使えばよい。

ただ、あまりに大胆に使った場合には、未来の自分を含めた他人が読むとき大変かもしれないね。


さて。goto が有用かどうか。という質問には、
答えなど無いよ。
が正解でしょう。好きに使えばよい。
0026名無しさん@お腹いっぱい。NGNG
Linuxや*BSDのTCP/IP周りのコードは結構gotoであちこちにjumpしていて
追うのがつらい...
0027名無しさん@お腹いっぱい。NGNG
絶対ジャンプはいただけないな。リロケータブルに出来ないもの。
出来るだけ相対ジャンプにしましょう。
0028名無しさん@お腹いっぱい。NGNG
>12
>それに対して10の意見なんかは漠然と感じてたことを
>きっちり明文化してくれた。今後のコーディングでgotoを
>使うかどうか迷ったときの重要な判断材料になると思うよ。

ふむ。君はまだまだそういった初歩的な例を勉強する必要があるってことだ。
だが世の中にはその程度の具体例は聞き飽きてうんざりしているものも
いるんだが、それはどうしたらいいんだろ
0029>27NGNG
俺もそう思ったけど黙ってたのに・・
0030名無しさん@お腹いっぱい。NGNG
1は「絶対ジャンプ」という言葉を使ってみたかったんだな。
0031>27@`29NGNG
>27@`29
アセンブラの絶対ジャンプと勘違いしてない?
Cコンパイラのasm出力を見たことある?
>28
具体例を示せ
もし示すことができずにこういうことを書き込むのは
レベルが低い証拠だぞ
0032名無しさん@お腹いっぱい。NGNG
なんかややこしくなってるけど
プロジェクトのコーディング規約に従えばいいと思うけど
goto自体が悪なんではなく 無秩序にgotoを使う人が悪であって
それを抑制するために一般的には使うなと言われているんですし

>>31
>>27は受けねらいで書き込んだんじゃないの?
Cに絶対ジャンプはないよな 1>>はどうして絶対ジャンプって書いたんだろ
0033名無しさん@お腹いっぱい。NGNG
31はバカ?
絶対ジャンプなんていうアセンブラの用語を1が使っているから
ネタにしてるんじゃん。知ったかぶりは恥ずかしいよ。
というより、1=31だろ。

ifもgotoはアセンブラのジャンプ命令に変換される。
その命令が相対ジャンプか絶対ジャンプかは、
通常、コンパイラによるものであり、ソースプログラムでは操作できない。
0034名無しさん@お腹いっぱい。NGNG
>アセンブラの絶対ジャンプと勘違いしてない?
C言語の絶対ジャンプというのを厨房のボクにおしえてください
0035名無しさん@お腹いっぱい。NGNG
1は、ただ「goto文」と言うより「絶対ジャンプ」って言ったほうが
カッコいいって思ったんだろ?
まぁ、背伸びしたい年頃なんだろうから、あまりツッコミ入れないで
目をつぶってやりなよ。
0036ヤングNGNG
ジャンプ
0037>34NGNG
void (*a)(void);
a=(void*)0x00000000;
a(); /* 0番地へジャンプ */
0038おまえら、NGNG
Cの絶対ジャンプも知らないで書き込むのは
レベルが低い証拠だぞ
0039>37NGNG
それはコールっす。
004037NGNG
どーちがうの?
0041名無しさん@お腹いっぱい。NGNG
コールは戻ってくるだしょ。
0042これならどや?NGNG
jmp_buf jp;
memset(jp@` 0@` sizeof(jp));
longjmp(jb@` 0);
//番地指定は不可能だが・・
0043<38NGNG
setjmp()@`longjmp()の事が言いたいのかな?
004443NGNG
かむった…
0045名無しさん@お腹いっぱい。NGNG
これをgotoを使わずに実現するにはどうすればいいですか?

again:
  if( (rc = read(fd@` &c@` 1)) == 1){
    /*成功。関係ないので省略*/
  }else if (rc == 0){
    /*接続終了。これも関係ないので省略*/
  }else{
    if(errno == EINTR) /*readシステムコール自体がエラーを起こしたわけではないので*/
      goto again;  /*もう一度readを呼び出すために戻る*/
    return (-1);
  }
004638>43NGNG
ちゃうちゃう。
>レベルが低い証拠だぞ
をコピペしたかっただけ。
004745NGNG
念のため書いておきますが、これは私が書いたコードではありません。
とある有名なネットワークプログラミング本に載っていたソースコードです。
0048>45NGNG
その流れは、while(1)とbreakを使ってるサンプルを見かけるな。
#そうすべきという意味ではない
0049>45NGNG
これならwhile+フラグにするな。
つーか昨日そう書いた。
0050名無しさん@お腹いっぱい。NGNG
>>25
 確認だけど、goto文とsetjmp/longjmpを混同してたりしない?
 いや、gotoの話で大域ジャンプとか書いてるからさ。

 で、元の話だと、多重ループから一気に抜ける場合、
多重ループ部分を関数化して、抜けるべき位置でreturn
だな。オレも。
005137NGNG
setjmp()@`longjmp()の事を絶対ジャンプって呼ぶの?
初耳でした。この場合の「絶対」はなんのことなの?
005248追記NGNG
もちろん、socketを扱ったサンプルで。
ネット上でも見かけるし、比較的有名な本でも
while(1)を使っているものもある。
#繰り返すが、どちらが良いかではなく使う側の趣向の問題
0053>45NGNG
do-whileはどうよ?
0054>45NGNG
プログラマの思考により近いのはgotoなんだろうな。
エラー時のリトライをループで書くのはちょっと妙な感じ。

でもループで書いちゃうんだよな。
0055みんなNGNG
せっかくネタスレになりかけたのに、
フレームが大好きだねぇ。

ハンガリアン記法
コーディングスタイル(括弧の位置@`*の位置)
リフレッシュレート
NULLの使い方
オブジェクト指向

頑張ってね。
0056名無しさん@お腹いっぱい。NGNG
>>45

俺ならこう書くな。gotoは結構使うほうだがこの程度では
使うまでも無いと思う。

while ((rc = read(fd@` &c@` 1)) < 0 && errno == EINTR) ;

switch (rc) {
case 0:
  ...
case 1:
  ...
default:
  ...
};
0057名無しさん@お腹いっぱい。NGNG
>>45
/* 成功 or 接続終了まで処理続行 */
while(((rc = read(fd@` &c@` 1)) == 1) || rc == 0) {
  if(errno != EINTR)
    return (-1);
}

if(rc == 1) {
  /*成功。関係ないので省略*/
}else if(rc == 0) {
  /*接続終了。これも関係ないので省略*/
}

# 俺だったらこうする
0058名無しさん@お腹いっぱい。NGNG
>>51
 K&R第二版では「非局所的ジャンプ」ってなってるな。
 絶対ジャンプとは言わんだろ。
 ってーか「どこへなりと絶対にジャンプする」の意味か(W
0059名無しさん@お腹いっぱい。NGNG
つーか、31はどう説明するのだろうか。
0060名無しさん@お腹いっぱい。NGNG
D. Knuth は goto が好きで、「文芸的プログラミング」中で
goto を使わないとかえってプログラムが
読みにくくなる例を挙げている (p.47 のを C風に書き直した):
for(i=1; i<=m; i++) {
if (a[i] == x) goto found;
}
notfound: i=m+1; m=i;
a[i]=x; b[i]=0;
found: b[i]++;
0061名無しさん@お腹いっぱい。NGNG
>>60
このようなケースのために、ループの後に else 節が使えるのって、
python だっけ?
しかしコメントを使えば、以下のgotoを使わない版で・のほうが
充分分かりやすいと思う。

for (i = 1; i <= m && a[i] != x; ++i) ;

if (i > m) {
  /* Not found */
  ...
}
++b[i];
0062判明せりNGNG
goto xxx; // 絶対ジャンプ
if (0) goto xxx; // 非絶対ジャンプ
if (rand()==0) goto xxx; // ごくまれにジャンプ
0063名無しさん@お腹いっぱい。NGNG
>>62
おおっ、そうか。極めて納得。
00641っすNGNG
いろいろな意見ありがとうございました。
いろんな人の考えを知ることができただけでもこのスレッドを立てた
意味があったと思います。
キツキツのコーディング基準を持っている客先はホントに苦労するんですよね。

私が普通の事務系プロジェクトでソースコードを書く時に守ろうとしている
ことをプログラム構造的な物に限って順位を高い順に挙げると・・・

1.読みやすさ(nearly equal メンテしやすさ)を重視する。
  目安として、客先プログラマの8割以上に理解してもらえるコードを書く。
  コメントは自分で見てうっとおしいくらい付けておく。
  変数のネーミングはハンガリー記法を使う。
  複雑な省略記法は使わない。
2.それ自身で意味を持たない(再利用できない)関数は、できれば作らない。
  (但し、100行を越えるような大きな機能ブロックを分割することに
   おいてはその限りではない。)
3.関数内の return はなるべく一つのみにする。
  #return が複数箇所にある関数は、後方参照の goto のある関数より
  可読性が悪いと思います。
4.goto はなるべく使わない(まあ当然ではありますが)
5.再帰はなるべく使わない。
  これは、冗談じゃないのですが、今までの客先で、再帰が読める人が
  半数以下の会社が多かったのが理由です。
  だから、10 行で書ける再帰が 30 行の非再帰で書けるなら、
  無条件で非再帰で書いてます。
6.なるべくワーニングレベルのメッセージも出ないようにする。
  顧客に安心感を与えるため。

他にもいろいろありますが、まあこの辺で・・・

goto 不要論の人は、おそらく上記4の優先順位が私よりももっと
高いんでしょうね。
それから、goto と絶対ジャンプが違うというのはここで初めて知りました。
ソースコード上の特定ラベルに制御を移す、という意味で私は
絶対ジャンプという言葉を使ったのですが、間違ってたんですね (^^;
さ、仕事だ仕事。
0065名無しさん@お腹いっぱい。NGNG
>>60
なぜ、1オリジンなの?
っていう話題は、ここではしちゃいけないね。
sage
0066>65NGNG
オリジナルがPascalだからじゃない。
Pascalは
n[30..90]: Integer
なんて恐ろしい配列も作れます
006766NGNG
まちがえた
n: array[30..90] of Integer;
だった。Cと混同してしまった...
006843>38NGNG
>ちゃうちゃう。
>>レベルが低い証拠だぞ
>をコピペしたかっただけ。
ををう、そうだったのか。なっとく(^^
0069名無しさん@お腹いっぱい。NGNG
少年ジャンプは本当に○×△なのか。
0070名無しさん@お腹いっぱい。NGNG
>>69
違います。
少年ジャンプは友情・努力・勝利です。
0071邪道NGNG
アセンブラで作る仕事ん時、Cの吐き出すアセンブラソースを再利用するってテク使ったなぁ
なぜって、そのCPU用のCコンパイラが、まともな実行ファイル吐き出せないからさ!
アセンブラソースを吐き出させて、ちゃんとコーディングが正しいかチェックするんだぜ!(嘘だろってな
そんときには、悪いが goto 使いまくったっすね。結局アセンブラに直せば jump 命令なんだしさ!

で、後日談で、そのCコンパイラ、goto 文(の絡んだ構造)を正しくコンパイルできなかったって話!!
馬鹿みたい<自分
0072名無しさん@お腹いっぱい。NGNG
>>71
H8?
インラインアセンブラを使うと・・・ってやつ?
0073名無しさん@お腹いっぱい。NGNG
>5.再帰はなるべく使わない。
....
>  だから、10 行で書ける再帰が 30 行の非再帰で書けるなら、
>  無条件で非再帰で書いてます。

うわぁお、マジですか。非再帰で書く方が読みにくいと思うけど...
いろんな条件の仕事があるんですねぇ。
まだまだ世間知らずの未熟者 > 自分
0074名無しさん@お腹いっぱい。NGNG
>>64
 3.のreturnはなるべく1つにってのはちょいと異議ありだなあ。

if (ERR_HOGE == hoge()) {
  return -1;
if (ERR_AKYA == akya()) {
  return -1;
if (ERR_PUMO == pumo()) {
  return -1;

process_without_error();

return 0;

 なんてコードは書きません?
 あ、もちろんC++じゃなくてCでの話。

 5.についてはまあご愁傷さまとしか言い様がないですが、
あとはまあおおむね同意。
 版画利庵は・・・ポインタの頭にp付けるくらいで、
完全準拠はしないけど。
使わないけど、
007574NGNG
ギャース、}閉じ忘れやまもり。鬱だ氏のう。
00761っすNGNG
74さんの return については、まぁ自分の中のコーディング基準だから
どちらがいいとは断言できませんよね。
私が return を関数の終わりにまとめたい理由は、関数内での「終了処理」
の部分に無条件に埋め込みたいロジックのある場合なんかに、その関数を全て
読まなくても流し読みでパッと埋め込むべき位置を特定できるからなんですよ。
goto はラベルがあるから流し読みでもまず見落とすことはありませんしね。
まあ、これは自分が過去に書いたコードだけにしか適用できない訳ですが、
自分なりにメンテのしやすさを試行錯誤した結果、前記のコーディング
基準に落ち着いた訳です。
74さんが例に挙げられている構文は、私なら戻り値を変数にして代入してます。
74さんが return を複数箇所に書く時に想定しているメリットというのを
教えていただけますか?あ、いやマジで。
007774NGNG
例えば、↓だとネストが深くなって見辛い。
return_code = -1;
if (ERR_HOGE <> hoge()) {
  if (ERR_AKYA <> akya()) {
    if (ERR_PUMO <> pumo()) {
      process_without_error();
      return_code = 0;
    }
  }
}
return return_code;

エラー条件の判定に変数を使うと、通して最後まで読まないと
エラーの時どうするか判りにくい。
without_err = -1;
if (ERR_HOGE <> hoge()) {
  without_err = 0;
}
if (ERR_AKYA <> akya()) {
  without_err = 0;
}
if (ERR_PUMO <> pumo()) {
  without_err = 0;
}

if (without_err) {
  process_without_error();
}

return without_err;

ということで、74の例でreturnを複数置いてるのは、
関数の一連の流れにおいて、(エラーによって)処理を
中止して上流に変えるというのが判りやすいから。

わたしは、常に実行するべき終了処理があるなら、上流で書きます。
と、こんな感じです。
00781っすNGNG
なるほど

>エラー条件の判定に変数を使うと、通して最後まで読まないと
>エラーの時どうするか判りにくい。

これは確かにありますね、納得です。
一人だけの判断では視野が狭くなってるんでしょうね。
気づきませんでした。ありがとう。

でも・・・

>わたしは、常に実行するべき終了処理があるなら、上流で書きます。

これに関しては???です。
これだと、関数を再利用できる一つのブラックボックスとして扱うことが
難しくなるような気がするんですが、どうでしょ?
007974NGNG
 関数単位でブラックボックス化するのは限界があるでそ。
 ファイル単位で機能を纏めるって前提で、
子関数をstatic宣言して、C++のprivate風に隠蔽すれば
問題無いでがしょ。Cでの定石かと・・・。

hoge.h:
extern void root_func(void);

hoge.c:
static int sub_func(void);
{
  // hoge
}
static void final_process(void);
{
  // hoge
}

void root_func(void);
{
  sub_func();
  final_process();
}
00801っすNGNG
うーん、制御構造をちょっと変えれば関数単位の独立性も
上がるというのに、それを崩してまで終了処理をコール元に
書こうとは俺は思わないなぁ。
まあこの辺はポリシーの問題なので、そういう意見もあると
いう風に認識させていただきます。

それとファイル単位で機能をまとめるというのは、C に限らず
どの言語でも一般常識ですよねー。
0081名無しさん@お腹いっぱい。NGNG
>72
 71じゃないけど某社のH8コンパイラで割り込みが入るはずのない場所で
 割り込みが入る現象には泣かされた。ちょうどいいタイミングで修正版
 が出て助かったけど。おんなじ奴かな。
008274NGNG
>>80
 らぢゃー。
 まーこのへんの機能分割は、手間と可読性のトレードオフでも
あるので、ケースバイケースですわね。
 関数5個・各関数100行位が別けて書く分岐点かなあ、わたしの場合。
■ このスレッドは過去ログ倉庫に格納されています