2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

久しぶりに goto をゴリゴリ使ってみた

Last updated at Posted at 2022-01-20

N88-BASIC からプログラムを始めた自分にとっては goto が使えないのは手足をもがれたようだ!と思ったものです。
C言語の初学で goto はヤバいから使うなと教わり、今ではコーディングスタイルも安定してきただろうところですが、小さなマイコン組み込みにおいては UART によるコンソールを作るだけでも速度と容量の戦いで goto を使いたいという衝動を抑えられませんでした。
一回使ってしまったらもう何個使っても同じですよね。ということで、「性能のためには goto が必要」という考えのもとに実装してみました。

why goto?

いわゆる構造化プログラミングには邪魔とされる goto ですがそもそも構造化プログラミングってなんぞや、と今更ながらに疑問になりました。軽く調べると「制御構造を抽象化していくこと」のようです。確かに、goto を使いまくるプログラム(の範囲)は非常に具体的ですね。しかし快適にプログラムを組むことを考えたとき、goto の何が悪だったのでしょうか。
この問題を考えるときに制約条件を整えておく必要があるでしょう。今回の私の例では、

  • C言語
  • 小さなマイコンなのでリソースが少ない
  • 標準ライブラリは使わない
  • 速度が絶対だがメンテナンス性も必要

というところでしょうか。実のところ、いちばん重要なのは「C言語」ではないでしょうか。C言語で goto を書いてもそれほど恐ろしいことにならないんですよね。C言語では関数間の goto はできません。N88-BASIC ではサブルーチン間も飛べた(と記憶している)のでC言語の goto なんてザコも同然!意図的に読みにくくしない限り大丈夫じゃないでしょうか。

how goto?

ではどのように使ったかという話ですが、概要を示しましょう。

	switch( param )
	{
	case A:	step = 0;	goto CASE_A;
	case B:	step = 0;	goto CASE_B;
	case C:	step = 0;	goto SKIP;
	case D:	step = 1;	goto END;
	default:break;
	}

	if( step != 0 )
	{
		switch( param )
		{
			case E:	if( step == 1 )	{ step = 2; }
						else		{ step = 0; }
						goto END;

			case F:
			case G:
				if( step == 2 )
				{
					evaluate = param;
					step = 0;
					goto PROCESS_C;
				}
				step = 0;
				goto END;

			default:
				step = 0;
				goto END;
		}
	}
	else
	{
		goto PROCESS_DEFAULT;
	}

これが飛び元のコードですね。もう少し改善できそうですが。
そして下が飛び先のコードです。ちょっと怒られそうです。

	if(0)
CASE_A:
	{
		...
	}
	else if(0)
CASE_B:
	{
		...
	}
	else if(0)
PROCESS_C:
	{
		...
	}
	else if(0)
PROCESS_DEFAULT:
	{
		...
	}
	else
SKIP:
	{
	}

END:
	...

goto を使用したコードとしては非常に一般的な「下へ下へといく」コードです。...にはそれぞれ複数行入ります。
あえて一般的でないところを挙げるとしたら if(0) でしょうか。
この部分、もともとはフラグを使用してコーディングしていたのですが、goto に置き換え中に気づいたんです。
「if文の中(then節)に飛んだらelse節どうなるんだ!?」
単純に考えれば then 節の最後には else 節を回避するためのコードが埋め込まれなければならないため、狙った部分だけ実行できます。そのため離脱のための goto を書く必要もなく、あえて if文の形式を残しました。(もし怒られたときはフラグに戻しやすいし...)

if(0) は goto が無ければ絶対に実行されない箇所です。コンパイルするとマシンコードからは消えてなくなるのが一般的ですが、goto によって使われる可能性があるとちゃんと残りますね。素晴らしい。

これで「ラベルへ飛んで、{} の中だけ実行する」という悪さしにくい goto 文ができたわけです。やったね。

wow, switch...

いざ goto を使ってコーディングし、その高速化を確認すべく逆アセンブルのコードを確認してみたところ...

switch の case ラベルに全部詰め込んでた!! (というのと等価だった)

そりゃそうかーそうだなーはははー
しかし switch の case はインデントが深くなりやすい問題があります。また case が俯瞰できた方が良いし breakし忘れない等の言い訳から 今回の件は「効果あり」と判定しました。

いろいろと学びのあった goto ですが、便利なのでみなさんも是非使いましょう!

2
1
12

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?