電子書籍の厳選無料作品が豊富!

私は組み込みでソフト開発を行っているものです。
(アセンブラでの経験は長いが、Cは短い)
基本的な質問になってしまいますがご了承ください。
C言語で"goto文は使うな、スパゲティプログラムになりやすい。
使うなら多重ループからの脱出のみ" ということを良く聞きます。
本当ににそうでしょうか?・・・

例えば、下記サイトの図6をC言語で書いてみました。
もちろんgotoを使いました。
gotoを使わないで、誰でもわかりやすく書くことなどできるのでしょうか?
よろしくお願いします。


http://techon.nikkeibp.co.jp/article/NEWS/200711 …



/* 開始 */

KAISHI:
p74 = High;

if(p70 == High){
register = 1;
}
if(p71 == High){
register = register + 1;
if(register != 1){
goto KAISHI;
}
if(p72 == High){

A 回答 (16件中1~10件)

>初めの10秒間でsw1が押されなかったらエラー処理1を実施して終了。


>無事にsw1が押されると次の10秒がスタートする。
>次の10秒間でsw2が押されなかったら""エラー処理2を行った後エラー処理1を""実施して終了。
>無事にsw2が押されると次の次の10秒がスタートする。
>次の次の10秒間でsw3が押されなかったら""エラー処理3を行った後エラー処理2を行いエラー処理1""を実施して終了。
>です。

それなら、こうすればお望み通りになります。
switch( timerCheck() ) {
case ERR_SYORI3:
____エラー処理3
case ERR_SYORI2:
____エラー処理2
case ERR_SYORI1:
____エラー処理1
____break;
}

・ERR_SYORI3→ERR_SYORI2→ERR_SYORI1
・ERR_SYORI2→ERR_SYORI1
・ERR_SYORI1
ですので、これでOKですか?
    • good
    • 1
この回答へのお礼

OKです。

お礼日時:2013/02/20 21:57

なんか熱くなってる方もおられますが



基本は、最近の言語は goto を使う必要が無い
逆を言えば 昔のC言語は goto を使った方が良い部分があった
簡単に言えば、 java や C# にある finally 的な処理

今、java や C# に慣れているのであれば、
昔のC言語でプログラムを組む必要が出てきたなら
finally 的な処理に goto を使うのは全然問題ないでしょう
飛び先のラベルを cFINALLY: と cCATCH: の二つだけ用意して
goto はこの二つのみ許可するのであれば、十分可読性があると言えますし
C# しか知らない人とかでも、理解できるでしょう
    • good
    • 0

私は,およそ半世紀前に起こった goto 文を避けようという運動の目的について,そもそも「わかりやすく書くこと」ではなかっただろうと思います.



ダイクストラさん(故人)のEWD566の邦訳より
「当時やられていたことは,同じ問題について四つの異なるプログラムを作り,それらのうちでどれが一番好きかを何時間も,何日も考える,といったことでありました.」

これは(goto論争の頃を指すのか判然としませんが)今を彷彿とさせます.しかしこの回想の次の展開は工芸から科学を目指しています.

「『わかりやすい』とか『明瞭な』とか『読みやすい』とかいうような,ぼんやりとした,情緒的な用語から,『形式的な正しさの証明』という妥協のない,残忍な概念への移行は...(中略)...一里塚でありました.」

だからといって,いつも証明しろと言われたら私も一目散に逃げるのですが,時折プログラミング入門書などに,連接(;)・選択(if)・反復(while)の三つの構文だけでプログラムを分かりやすく作ることが当時は画期的なことでした,のような解説を見かけるたびに少し悲しくなるのです.

すみません.あまり回答になっていなくて.

参考URL:http://ci.nii.ac.jp/naid/110002753409
    • good
    • 0

書き忘れました。



>gotoを使った方が一つの関数で済むのでスッキリすのではと主張したかったからです。

それは悪い設計だと思います。
止む負えない場合を除いて関数は単機能が望ましく複数の機能を有するのは悪い設計となります。

理由は単体テストで関数ごとのテスト項目がシンプルになるのでバグが減りやすく、安定した関数を積み重ねることで安定したシステムを構築できると言う点と そもそも目視でのコードの把握もやりやすくなるためバグが入りづらくなると言う点です。

この回答への補足

>それは悪い設計だと思います。
>止む負えない場合を除いて関数は単機能が望ましく複数の機能を>有するのは悪い設計となります。
悪い設計ではありません。
このような簡単なフローで関数を2つも用いるのが正直解りにくいです。今回の場合は、むしろGOTOを使って出口を一箇所に集めた方が見やすいです。
素直にGOTO使えば良いのにと思います。
関数化して別けるのは、複雑で行数が長くなりそうな物ほど、
効果があると思います。
元々が簡単なものを関数化して別けるはどうかと思います。
GOTOを使って行き先が不明になるのを防止するため、
関数化するなら理解できます。
行き先が一箇所に集約されているものをあえて関数で別けるのは
おかしいと思います。
正直、個人の考え方によると思いますが・・・。
どんな場合でもGOTOなしで記述できるのでしょうか?
また、GOTOを使った方がスッキリする場合もあると思います。
最後に、いろいろな模範例(組み込み系の)があると助かります。
そういう書籍が無いんですよ。

補足日時:2013/02/20 21:54
    • good
    • 0

> do{


> ____if(timer == 0){
> ________エラー処理3
> ERR_SYORI2:
> ________エラー処理2
> ERR_SYORI1:
> ________エラー処理1
> ________break;
> ____}
> }while(sw3 == off);

・ループは先頭から開始する
・ループ中にifがあったら、そのループ中で発生する現象を判定し、処理する
・if文で、then部はifの条件が真のとき、else部には偽のときだけ実行される
と考えるのが自然ではないでしょうか?

sw3と関係無いエラー処理1,2が、どうしてこんな箇所にあるのか?
今回はたまたまtimer==0という条件が一致していますが、それは、プログラム全体を調べて、そこからしか飛んでこないのが明らかになったから言えることで、ここだけ見たらtimer==0とエラー処理1,2との関係もわかりません。

「gotoを使うのがわかりやすい」例を作ったつもりが、スパゲッティの見本になっています。

最初のフォローチャートも、追加されたプログラムも、まずやるべきなのは、そのままgoto文を使うことではなく、設計を見直すこと、ではないでしょうか?


構造化プログラミング、オブジェクト指向プログラミング、関数型プログラミングといったものは、間違ったプログラムに苦労した研究者達が、どうしたら正しいプログラムを楽に書けるか、考えた結果生れてきた手法です。一度、これらのプログラミング方法について、概論だけでも読んでみてはいかがでしょうか?


以下、gotoとは関係無い話
> return (register == 1) ;
> はどういう意味でしょうか?

関数からのreturnには戻り値を指定できる、というのはよろしいですね?
C言語では、==や<,>といった比較は、「比較演算子による演算」になります。
== 演算子は、「左と右が等しかったら1,異っていたら0」という「計算」をしたものが、値になります。
なので
a=(b<c) ; /* b<cならa=1, b>=cなら a=0 */
といった使い方もできます。

> 「音を鳴らしてはならない」なら0,「音を鳴らす」なら1を返す checkButton() 関数
を作りました。「音を鳴らす」のは「register==1」の時なので、register==1なら1、そうでないなら0を返さなければなりません。
そこで return (register == 1) です。
    • good
    • 0

>説明:


>初めの10秒間でsw1が押されなかったらエラー処理1を実施して終了。
>無事にsw1が押されると次の10秒がスタートする。
>次の10秒間でsw2が押されなかったらエラー処理2を実施して終了。
>無事にsw2が押されると次の次の10秒がスタートする。
>次の次の10秒間でsw3が押されなかったらエラー処理3を実施して終了。

No.4回答者さんの方法なら別に問題なく書けますよ。

------------- ここから
/*開始*/

int timer //型宣言 timerは1秒割込みで0になるまで1秒置きに-1されるものとする

enum {
ERR_NASHI,
ERR_SYORI1,
ERR_SYORI2,
ERR_SYORI2,
};

switch( timerCheck() ) {
case ERR_SYORI1:
____エラー処理1
____break;
case ERR_SYORI2:
____エラー処理2
____break;
case ERR_SYORI3:
____エラー処理3
____break;
}

int timerCheck()
{
____timer = 10; // 10秒をセット
____while( sw1 == off ) {
________if(timer == 0) return ERR_SYORI1;
____}
____timer = 10; // 10秒を再セット
____while( sw2 == off ) {
________if(timer == 0) return ERR_SYORI2;
____}
____timer = 10; // 10秒を再々セット
____while( sw3 == off ) {
________if(timer == 0) return ERR_SYORI3;
____}
____return ERR_NASHI;
}
------------- ここまで

とにかく、いろんな手法を心得た上でgotoが一番簡潔だと言う結論なら分かるのですが、gotoまず有りきに見えるのが気になるところです。
私も場合によっては使いますよ。


この処理はどうかと思います。
>do{
>____if(timer == 0){
>________エラー処理3
>ERR_SYORI2:
>________エラー処理2
>ERR_SYORI1:
>________エラー処理1
>________break;
>____}
>}while(sw3 == off)

この回答への補足

例題の説明ですが、私の説明が悪かったです。すいません。
正しくは、
初めの10秒間でsw1が押されなかったらエラー処理1を実施して終了。
無事にsw1が押されると次の10秒がスタートする。
次の10秒間でsw2が押されなかったら""エラー処理2を行った後エラー処理1を""実施して終了。
無事にsw2が押されると次の次の10秒がスタートする。
次の次の10秒間でsw3が押されなかったら""エラー処理3を行った後エラー処理2を行いエラー処理1""を実施して終了。
です。

gotoを使った方が一つの関数で済むのでスッキリすのではと
主張したかったからです。
とある書籍では「スパゲティになるのでgotoは使わないように」と目の敵のように書かれており、納得がいかなかった次第です。
アセンブラでは、ブランチ命令のようにラベルに飛ばすことは、
当たり前のように行っていたのにC言語になると「goto使うな」です。どの書籍も説明不足なんです。
今回のように、gotoで書いた例文をgotoのない文に変更して、
比較するなどの説明をしてほしいのものです。

補足日時:2013/02/19 22:55
    • good
    • 0

基本は会社の方針に従え、だと思います。



例えば、

while(1)
{
if( 条件 ) break;
処理
if( 条件 ) break;
処理
if( 条件 ) break;
処理
処理
処理
break;
}
処理


と書く人がいました。先頭の while や最後の break の意味、やってる事を要約すると
goto を使うと可読性が悪くなるという理由で goto の処理をを可読性悪く書いてるだけという
意味不明な状態です

C言語は、いわば昔の言語です。 最近の C# や JAVA 等 goto を使うよりもベストな方法が
別に用意されている言語とは違うのですから
goto にこだわらず、臨機応変に作れる事が理想でしょう

この回答への補足

>goto を使うと可読性が悪くなるという理由で
>goto の処理を可読性悪く書いてるだけという
>意味不明な状態です

確かにそうですね。

補足日時:2013/02/18 21:52
    • good
    • 0

付け足しですが「GO TO論争」(bit誌 1975年5月)という記事を読まれたことがなければ、一読することをお勧めします。

このような議論は過去にも相当に行われています(主に米CACM誌で)。これらの雑誌は大きな図書館に保存されています。

http://memo.ptie.org/bit/1975

内容は平行線です。goto文の支持派と反対派(形式手法よりな人々)は話がかみあわないので。それはそれで面白いですが。

IPAルールなど既存の成果や整理された経験則を参考にコーディング規約を考える方が有意義だと私は思います。

http://sec.ipa.go.jp/publish/tn06-004.html
    • good
    • 0

今回の例であれば、do文を使うのが妥当だと思います。


使用するマイコンμPD78F0547Dは決して高速ではなく、メモリサイズも小さいので、遅く大きくなるとわかっている、またはその可能性が高いコードを書くのは時期尚早な不最適化にもつながります。
その意味で、while文や関数の呼び出しは避けるべきです。ましてや、gotoを回避するためだけの新たな変数を導入するのは論外です。
(1KバイトしかないRAMを、本来不要な関数呼び出しのためのスタックや、変数で浪費すべきではありません)

> C言語で"goto文は使うな、スパゲティプログラムになりやすい。

必ずしもgoto文を使ったからスパゲッティになりやすいというわけではありません。
gotoを使わなくてもスパゲッティになるときはなりますし、gotoを使ってもならないときはなりません。
結局はプログラマ次第なので、本当に使ってはならないのは、特定の構文ではなく下手糞なプログラマの方です。

> 使うなら多重ループからの脱出のみ" ということを良く聞きます。
> 本当ににそうでしょうか?・・・

私の場合、終了処理やエラー処理にも普通にgoto文を使います。
たとえば、割り込みハンドラでは特別な出口処理が必要になることが多いと思います。
その場合、出口を一か所に集中させるにはgoto文を使う方がむしろ可読性が高いはずです。

ただ、今回のような単なるループを作るためにgoto文を使うことは、よほど特別な事情がない限りありません。
異常時にリトライを行う場合などは、結果的にループであってもgoto文を使うことはあります(エラー処理なので)。

職場のコーディング規約等でgoto文が禁止されているならそれに従わざるを得ませんが、質問者さんの開発経験はある程度長いようなので、お仕着せのコーディング規約に盲従するだけではなく、定期的に見直しをかけて適切な内容に改善していくべき立場ではないでしょうか?

この回答への補足

>出口を一か所に集中させるにはgoto文を使う方がむしろ可読性>が高いはずです。

私も同じです。
出口を一か所に集中させるにはgoto文を使います。
今回は例題になるフローチャートを無理やり探してきたんです。
すいませんでした。

補足日時:2013/02/18 21:57
    • good
    • 0

> gotoを使わないで、誰でもわかりやすく書くことなどできるのでしょうか?



goto文を使わないことは、大半のプログラマが可能でしょう。

しかしプログラムの正しさの証明や、それが容易なプログラムの作成は、そのための訓練を積まないと不可能でしょう。

1970年頃から始まった goto 文を使うなと教えは、証明を念頭においた(初学者に対する)プログラミング教育の準備段階です。状況に応じて goto 文をどう避ければよいかの判断は、その先にある技術を習得してからでないと難しいと思います。誰でも論理や集合をプログラミングに応用できるわけではありません。とりあえず何も考えず goto 文を排除するというのは一つの手でしょう。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!