【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は慎んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板2こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.20170

goto文は是か非か
投稿者---スタイル(2005/02/28 12:08:27)


スタイルといいます。
C言語勉強中です。

先日、ファイルの内容を他のファイルに出力する以下のようなプログラムを作成しました。
友人に見せたところ、「goto文を使っているからダメだ」と言われました。
個人的には、エラー時にいちいちファイルクローズを行うのが嫌で、まとめたつもりだったのですが、
それでも良くないと言われました。
ネットで調べてみると、goto文はとにかく使うなという人や、使いどころによるという人でまちまちなようでした。
皆さんはどのようにしていますか?今後の参考に教えて下さい。
#個人的には、下記のソースで、正常時でも「err」に入っているのがとても気になるのですが、
#良い方法が思いつきませんでした。

#include<stdio.h>

int main()
{
    int i;
    char buf[100];
    FILE *fp1, *fp2;
    
    fp1=fopen("data1.txt", "r");
    if(fp1==NULL)goto err;
    fp2=fopen("data2.txt", "w");
    if(fp2==NULL)goto err;
    
    for(i=0;i<10;i++){
        if(fgets(buf,100,fp1)==NULL)goto err;
        if(fputs(buf,fp2)==EOF)goto err;
    }
    printf("処理を終了しました\n");

err:
    fclose(fp1);
    fclose(fp2);
    printf("ファイルクローズしました\n");
    return(0);
}



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:goto文は是か非か 20171 Ban 2005/02/28 12:29:18
<子記事> Re:goto文は是か非か 20172 nop 2005/02/28 12:30:06
<子記事> Re:goto文は是か非か 20174 Craft 2005/02/28 13:38:15
<子記事> Re:goto文は是か非か 20176 shu 2005/02/28 15:18:54


No.20171

Re:goto文は是か非か
投稿者---Ban(2005/02/28 12:29:18)


>友人に見せたところ、「goto文を使っているからダメだ」と言われました。
-- snip --
>個人的には、エラー時にいちいちファイルクローズを行うのが嫌で、まとめたつもりだったのですが、
>それでも良くないと言われました。

私は使い方次第派なので、このスタンスに私は賛同します。
(が、絶対に禁止というルールのプロジェクトや、
そういう新年をお持ちの方たちも多数存在するのは確かです)

>#個人的には、下記のソースで、正常時でも「err」に入っているのがとても気になるのですが、

名前を err ではなく cleanup 等にすればいいと思います。



今のままのコードだと、fp1 で失敗した時に、fp2 が不定値のままクローズされます。
両方を事前に NULL にしておき、クローズのまえで NULL ないかを確認、
NULL でなかった場合にのみ実際にクローズするべきだと思います。


この投稿にコメントする

削除パスワード

No.20173

Re:goto文は是か非か
投稿者---Ban(2005/02/28 12:30:13)


typo訂正。

>私は使い方次第派なので、このスタンスに賛同します。
>(が、絶対に禁止というルールのプロジェクトや、
> そういう信念をお持ちの方たちも多数存在するのは確かです)



この投稿にコメントする

削除パスワード

No.20172

Re:goto文は是か非か
投稿者---nop(2005/02/28 12:30:06)


>皆さんはどのようにしていますか?今後の参考に教えて下さい。

goto自体は悪いとは思わないが、
使い方によっては止めろと言いたい。

>#個人的には、下記のソースで、正常時でも「err」に入っているのがとても気になるのですが、
>#良い方法が思いつきませんでした。

「err」より終了処理と言う意味合いのラベル名がよいかもしれない。

> FILE *fp1, *fp2;
> fp1=fopen("data1.txt", "r");
> if(fp1==NULL)goto err;
>err:
> fclose(fp2);

これは非常によくない。
fp1がNULLだった時、fp2には何が入っているかわからない。

 「時々エラーが発生する事がある」

と言う事態に悩まされる事になりそう。


この投稿にコメントする

削除パスワード

No.20174

Re:goto文は是か非か
投稿者---Craft(2005/02/28 13:38:15)


ソース例に提示された使い方は悪くないと思います。
コード上の問題は、他の方がご指摘いただいているとおりです。

絶対やっちゃいけないこと
・while/forなどのループへの飛び込みをするgoto
・ifなどの条件文内へ飛び込むgoto(ifの意味があいまいになる)
・goto→gotoと続くような処理(プログラムが追跡できなくなる)

避けるべきこと
・処理の頭や途中へ戻るgoto(ループや関数への置き換えができないか検討要)
・gotoの使いすぎ

このあたり注意してればいいのではないかと個人的には思ってます。
(私はまずgotoはつかいませんが)


この投稿にコメントする

削除パスワード

No.20177

Re:goto文は是か非か
投稿者---スタイル(2005/02/28 15:40:26)


>ソース例に提示された使い方は悪くないと思います。
>コード上の問題は、他の方がご指摘いただいているとおりです。
>
>絶対やっちゃいけないこと
>・while/forなどのループへの飛び込みをするgoto
>・ifなどの条件文内へ飛び込むgoto(ifの意味があいまいになる)
>・goto→gotoと続くような処理(プログラムが追跡できなくなる)
>
>避けるべきこと
>・処理の頭や途中へ戻るgoto(ループや関数への置き換えができないか検討要)
>・gotoの使いすぎ
>
>このあたり注意してればいいのではないかと個人的には思ってます。
>(私はまずgotoはつかいませんが)

皆さん、早速の回答ありがとうございます。

今回のgoto文の使い方は、特にいけないというわけではないということで、安心しました。
今回のプログラムで、エラー処理をまとめたのは、自分のアイデアではなく、以前どこかのホームページで
「私はエラー処理を最後にまとめて書くのが好きなので、javaではgoto文がなくで残念だ。」
というのが頭に残っていたのでこう書きました。なので、周りからgoto文を否定されまくったので不安になっていました。

また、皆さんの指摘を受けて、試しにdata1.txtを消して実行したら落ちました。
そこで、以下のように修正してみました。

#include<stdio.h>

int main()
{
    int i,ret=1;
    char buf[100];
    FILE *fp1=NULL, *fp2=NULL;
    
    fp1=fopen("data1.txt", "r");
    if(fp1==NULL)goto cleanup;
    fp2=fopen("data2.txt", "w");
    if(fp2==NULL)goto cleanup;
    
    for(i=0;i<10;i++){
        if(fgets(buf,100,fp1)==NULL)goto cleanup;
        if(fputs(buf,fp2)==EOF)goto cleanup;
    }
    printf("処理を終了しました\n");
    ret=0;

cleanup:
    if(fp1!=NULL)fclose(fp1);
    if(fp2!=NULL)fclose(fp2);
    printf("ファイルクローズしました\n");
    printf("return=%d\n",ret);
    return(ret);
}


goto文の取扱いについて、自分のスタイルを決定する前に、
「なぜgoto文を使ってはいけないか?」
を議論している掲示板を見つけましたので、それをじっくり見てみます。
#うーん、レスがたくさん付いてて読みきれなーい。
#うー、言ってることが理解できないよー。
前途多難ですが。。。

あと、Cプログラミング診断室という本でgotoの使ってはいけない例というのを見つけたので、
これも参考にしてみようと思います。
#うーん、これも前途多難だ。。。自分でやってはいけないgoto文を書いてみたらいいのかなぁ。



この投稿にコメントする

削除パスワード

No.20185

Re:goto文は是か非か
投稿者---あかま(2005/02/28 20:55:02)


>#うーん、これも前途多難だ。。。自分でやってはいけないgoto文を書いてみたらいいのかなぁ。
文句つけられたらその人に書かせてみればok
実際に構造が綺麗になってれば次回からそうすればいいし、
if,else,whileが無駄に増えてダメになってたらニヤニヤする。



この投稿にコメントする

削除パスワード

No.20194

Re:goto文は是か非か
投稿者---スタイル(2005/03/01 12:10:02)


>>#うーん、これも前途多難だ。。。自分でやってはいけないgoto文を書いてみたらいいのかなぁ。
>文句つけられたらその人に書かせてみればok
>実際に構造が綺麗になってれば次回からそうすればいいし、
>if,else,whileが無駄に増えてダメになってたらニヤニヤする。

アドバイスありがとうございます。
以前、ループやif文が多重になって(ネストが深いって言うんですか?)構造が分かりにくくなったことがあるのですが、
そのときは「こんなに複雑な処理をよくぞ書ききった。えらいぞオレ!」
とか思ったのですが、後で見直したら何の処理か分からなくなってしまいました。
(友人にも「見にくい(醜い)。読む気しねぇ!」とか言われたし。。。)
やっぱりプログラムを誰かに見てもらうっていうのは大事なんですねぇ。

これからも「よりうつくしく健康なプログラム」を目指して頑張っていこうと思います。
皆さん、たくさんのアドバイスありがとうございました。


この投稿にコメントする

削除パスワード

No.20176

Re:goto文は是か非か
投稿者---shu(2005/02/28 15:18:54)


基本的」には、「gotoはダメ」で良いと思います。

「gotoはダメ」という意見があるから、gotoは余り使われません。
「gotoはダメ」という意見があるということは、gotoではなく、goto以外での解決法があります。

break exit() return

「gotoはダメ」という意見があるから、gotoの使いどころも限られてきます。
「gotoはダメ」という意見があるから、ダメじゃないgotoの使い方が考えられます。
結果的に、gotoでの解決法は制限されてしまうので、goto以外での解決法の方が重要。

#include <stdio.h>

int main( void )
{
    FILE *fp1 = NULL, *fp2 = NULL;
    char buf[100 + 1] = {0};
    int i;
    
    if (!(fp1 = fopen( "data1.txt", "r" ))) return 1;
    if (!(fp2 = fopen( "data2.txt", "w" ))) return 1;
    
    for (i = 0; i < 10 && fgets( buf, 100, fp1 ); i++)
        fputs( buf, fp2 );
    puts( "処理を終了しました" );
    
    fclose( fp1 );
    fclose( fp2 );
    puts( "ファイルクローズしました" );
    
    return 0;
}



この投稿にコメントする

削除パスワード

No.20178

Re:goto文は是か非か
投稿者---Ban(2005/02/28 16:06:54)


     -- snip --
    if (!(fp1 = fopen( "data1.txt", "r" ))) return 1;
    if (!(fp2 = fopen( "data2.txt", "w" ))) return 1;
     -- snip --


私見ですが、明示的にクローズせずに環境の後始末に任せる用法の方が、
個人的にはお勧めできません。(考慮不足でリークさせているようにしか見えない)
関数にして、関数から抜けてくるならありだとは思いますが、main でやるのは.....。

# C++ 使いなので、資源解放はデストラクタ使うからこんな状況にはならないんですけど。
# だから、C は面倒くさい。


この投稿にコメントする

削除パスワード

No.20179

Re:goto文は是か非か
投稿者---shu(2005/02/28 16:51:08)


>私見ですが、明示的にクローズせずに環境の後始末に任せる用法の方が、
>個人的にはお勧めできません。(考慮不足でリークさせているようにしか見えない)
>関数にして、関数から抜けてくるならありだとは思いますが、main でやるのは.....。

とりあえず正常にファイルが開いてくれなければ、
後々の処理をする必要が無いと思い、return 1;としました。

私には、Banさんの意見がはっきりと理解できません。
なんとなく良くないことをしているんだろうとは感じるのですが、
表現が曖昧なので、反論も納得も出来ません。


この投稿にコメントする

削除パスワード

No.20180

Re:goto文は是か非か
投稿者---とおりすがり(2005/02/28 17:00:11)


fp1 はfopenできたけどfp2 のfopen失敗したときは...



この投稿にコメントする

削除パスワード

No.20181

Re:goto文は是か非か
投稿者---Ban(2005/02/28 17:19:11)


>とおりすがり さん
フォローありがとうございます。

>fp1 はfopenできたけどfp2 のfopen失敗したときは...

ご指摘の通り、fp1 を開いた後、fp2 に失敗すると、
fp1 のクローズが行われないままプロセスが終了することになりますので、
提示のコードは好ましくないと思った次第です。

昨今の主要な環境なら、プロセス終了前にファイルはとりあえずクローズされて
リソースがリークすることはそうそうないと思いますが、
常にそうである保証はありませんし、フラッシュ無しに閉じられて最後の出力が
消えても文句は言えないと思います。

# main でなければ〜という話は、goto の替わりに関数からの return を使う
# というイディオムのことを指したつもりです。
# 関数内から抜けるたびに個別の終了処理をいれなくていい書き方の一つで、
# goto の替わりに return を使う。関数に入る前に資源を確保しておいて、
# 関数内では何があっても単に復帰し、関数から抜けたところで常に資源を解放する、
# という構成のことです。



この投稿にコメントする

削除パスワード

No.20182

Re:goto文は是か非か
投稿者---かずま(2005/02/28 18:13:19)


> 昨今の主要な環境なら、プロセス終了前にファイルはとりあえずクローズされて
> リソースがリークすることはそうそうないと思いますが、
> 常にそうである保証はありませんし、フラッシュ無しに閉じられて最後の出力が
> 消えても文句は言えないと思います。

規格書には昔から次のことが明記されています。

・main からの return は exit関数を実行する。
・exit は、書込みしていないバッファリングされたデータを持つすべての
 オープンしているストリームをフラッシュし、すべてのオープンしている
 ストリームをクローズする。


この投稿にコメントする

削除パスワード

No.20183

Re:goto文は是か非か
投稿者---shu(2005/02/28 19:58:40)


Banさん、とおりすがりさん、かずまさん、ありがとうございます。

自分ではちゃんと書いたつもりになっていたのが、良くわかりました。
開いたファイルを閉じるなんて当たり前のことなのに、
その当たり前のことを見落としていました。


この投稿にコメントする

削除パスワード

No.20186

Re:goto文は是か非か
投稿者---かずま(2005/02/28 21:18:21)


> 自分ではちゃんと書いたつもりになっていたのが、良くわかりました。
> 開いたファイルを閉じるなんて当たり前のことなのに、
> その当たり前のことを見落としていました。

誤解されていませんか?

shu さんのプログラムは規格どおりに、ちゃんと書かれています。

規格に準拠した処理系なら、main からの return により、exit で確実にすべ
てのストリームがフラッシュされ、クローズされることが保証されています。

fclose しなくて、最後の出力が消えるなんてことはありえないのです。
もし消えたら、処理系を作ったところに文句は言えます。


この投稿にコメントする

削除パスワード

No.20188

Re:goto文は是か非か
投稿者---RAPT(2005/02/28 21:46:26)


ファイルのクローズだけならまだしも、malloc()-free()とか、後始末の順番などが
問題となる場合、下記のようにdo〜while(0)で対処する場合が多いですね。

# 下記では後始末の順番については関わっていませんが、
# struct hoge{
#     char *str;
# };
# hoge *ptr;
# とかした場合に問題が発生する場合があるかもしれません。

#include <stdio.h>
#include <string.h>

int main( void )
{
    FILE *fp1 = NULL, *fp2 = NULL;
    char *buf = NULL;
    int i;

    do{
        if (!(fp1 = fopen( "data1.txt", "r" ))) break;
        if (!(fp2 = fopen( "data2.txt", "w" ))) break;
        if (!(buf = malloc(100))) break;
        for (i = 0; i < 10 && fgets( buf, 100, fp1 ); i++)
            fputs( buf, fp2 );
        puts( "処理を終了しました" );
    }while(0);

    free(buf);
    puts( "バッファを開放しました" );

    fclose( fp1 );
    fclose( fp2 );
    puts( "ファイルクローズしました" );

    return 0;
}



この投稿にコメントする

削除パスワード

No.20192

Re:goto文は是か非か
投稿者---かずま(2005/03/01 09:38:41)


>    free(buf);
>    puts( "バッファを開放しました" );
>
>    fclose( fp1 );
>    fclose( fp2 );

規格書に、free(NULL) についての記述はありますが、
int fclose(FILE *stream) については、stream がストリームを指している
ことを前提に機能が記述されています。NULL は認められていません。

実際、VC++ では、fclose(NULL) は異常終了しました。


> # 下記では後始末の順番については関わっていませんが、
> # struct hoge{
> #     char *str;
> # };
> # hoge *ptr;
> # とかした場合に問題が発生する場合があるかもしれません。

これはどういうことを言いたいのでしょうか?
C では、タグ名は型名ではないということ?



この投稿にコメントする

削除パスワード

No.20193

Re:goto文は是か非か
投稿者---Ban(2005/03/01 10:04:09)


do while のこの用法自体も、
「do を読んでいる時点で break を使うためだけの構造であることがわからない」
と言う点で、賛否両論があると思います。
# 好きな人は多用するし嫌いな人はまず使わない。
使うにしても、最低限 do にコメントつけるなり、do を define して分かりやすい
名前を付けるなりの配慮が欲しい書き方だと、個人的には思います。




この投稿にコメントする

削除パスワード

No.20201

Re:goto文は是か非か
投稿者---RAPT(2005/03/02 00:43:55)


> 規格書に、free(NULL) についての記述はありますが、
> int fclose(FILE *stream) については、stream がストリームを指している
> ことを前提に機能が記述されています。NULL は認められていません。
すみません、手抜きです。fcloseについては既に記載があったので、
省略してしまいましたが、実際には、
if (stream != NULL){
    fclose(stream);
}
などとする、と訂正致します。


> > # 下記では後始末の順番については関わっていませんが、
> > # struct hoge{
> > #     char *str;
> > # };
> > # hoge *ptr;
> > # とかした場合に問題が発生する場合があるかもしれません。
> これはどういうことを言いたいのでしょうか?
> C では、タグ名は型名ではないということ?
失礼。struct hoge *ptr; とするつもりでした。

イマイチうまい例が思いつかなかったのですが、要は、initialize, 
terminateの順番が問題になる場合、その順序を考慮するケースが
ありうるのではないか、と言いたかっただけです。



この投稿にコメントする

削除パスワード

No.20191

Re:goto文は是か非か
投稿者---Ban(2005/03/01 02:54:23)


>規格に準拠した処理系なら、main からの return により、exit で確実にすべ
>てのストリームがフラッシュされ、クローズされることが保証されています。

exit ならフラッシュされることは知ってましたが、main からの
return でも暗黙でそうなることが保証されているとは認識していません
でした。(exit でも return でも開いたものは事前に必ず閉じてましたし..)
で、規格書を読んできましたが、確かに等価だと書いてありましたので、
少なくとも言語規格的には問題はないようです。
その点、訂正してお詫びします。申し訳ありませんでした>shu さん
また、ご指摘ありがとうございました>かずま さん

とはいえ、goto も言語規格的にはまったく問題ないわけで、
書き方としては明示的にクローズしておく方が好ましいと思う、
私の感想に変わりはありません。

main からすぐに抜けるケースなどは例外的にアリだとわかりましたが、
それ以外は基本的にやめておいた方がいいと思いますし、勿論自分は常に閉じるようにしてます。
まぁ goto と一緒で、絶対的に禁止されるような類ではないわけですが。



この投稿にコメントする

削除パスワード

No.20200

Re:goto文は是か非か
投稿者---とおりすがり(2005/03/01 23:27:41)


たぶんかずまさんは私よりずっと詳しいんだろうなあ。と、思いつつ...

>ログラムは規格どおりに、ちゃんと書かれています。
"規格沿えばそれはあり?"という事がスレ主さんの疑問のような。

私はこう思います。
規格に沿っていても(例えばgotoとか、ループ内のreturnとか)、
それを使う必然性があり、"なぜこういう書き方したの?"っていう疑問に
きちんと答えられるのならば、使ってもいいと思います。

"ただなんとなく"とか"気分次第"とかで使うと、読む人は"なぜ?"という
感覚に陥り、無駄な検証をされられるだけでなく、それを書いた人のソース
は全部疑って読まなければならないという労力を払わされます。
たとえ規格通りだとしても、みんなが"?"というソースをあえて書く事は
生産性を下げるだけです。

"じゃあみんなってなんだよ?"とか"何が普通?"とかの説明はできませんが、
まあそこは日々努力という事で。(^^)




この投稿にコメントする

削除パスワード

No.20202

Re:goto文は是か非か
投稿者---zero(2005/03/02 01:47:03)


gotoを使うと自然に書けるのなら使えばいいと思う


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧