🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

コンパイラ

コンパイラは、プログラミング言語で記述したソースコードを、コンピュータの実行形式であるオブジェクトコードに変換するプログラムです。

Q&A

解決済

6回答

1529閲覧

C#で動的なgotoラベルを実装したい

Aty

総合スコア7

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

コンパイラ

コンパイラは、プログラミング言語で記述したソースコードを、コンピュータの実行形式であるオブジェクトコードに変換するプログラムです。

0グッド

0クリップ

投稿2019/11/23 17:47

編集2019/11/24 00:05

コンパイラ最適化について勉強していました。

まず、C言語では、gccで以下のような記述ができます。

c

1void main() 2{ 3 int i = 1; 4 void *labels[2] = { &&LABEL0, &&LABEL1 }; 5 goto *labels[i]; 6LABEL0: 7 printf("LABEL0\n"); 8 goto END; 9LABEL1: 10 printf("LABEL1\n"); 11 goto END; 12END: 13 printf("END\n"); 14}

&&LABEL0というのは、gccの拡張文法です。
これは、labelが何十個と増えた場合に、if else文やswitch文より高速に動作するように思えます。

ここで、C#で上記と同様の内容を書くことはできないでしょうか?というのが質問です。すなわち、ラベルを値として扱えないか、ということです。

なお、以下は試してみましたがダメでした。

cs

1void main() 2{ 3 int i = 1; 4 switch (0) 5 { 6 case 0: goto case i+1; //定数値が必要です。 7 case 1: Console.WriteLine("i=0"); break; 8 case 2: Console.WriteLine("i=1"); break; 9 } 10}

また、そもそもこの質問がちんぷんかんぷんなことを言っていた場合、それも指摘くださると嬉しいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/11/23 22:17

昔、避けるべきとよく言われていたスパゲッティプログラムを作るような気がします。保守性を考えて、普通に switch - case や if - else を使った方が良いのではないでしょうか?
Zuishin

2019/11/23 23:23 編集

本当にそれが高速動作するのかどうか計測してみてください。C 言語の switch を最適化を有効にしてコンパイルすると、ジャンプテーブルを作って同様のコードになるか、もしくは更に高速なものになるかもしれません。 C# では System.Reflection.Emit 名前空間のクラスやまたは System.Linq.Expressions 名前空間のクラスを使えばできなくはないのではないかと思いますが、デメリットの方が多すぎて無意味です。
Aty

2019/11/23 23:28 編集

今回の質問はアセンブリの勉強から出た発想であり、普段使いでgoto文を使用したことはないです。
Zuishin

2019/11/23 23:29

ならば System.Linq.Expressions 名前空間のクラス群を学習されたらどうでしょうか。 自分で式木を組み立てるなら、ラベルを配列に入れることも、そこにジャンプすることもできるはずです。 組み上げた式木を Expression<Action>.Lambda に入れて Compile メソッドでコンパイルすると、通常のメソッドのようにデリゲートに入れて使うことができます。
Zuishin

2019/11/23 23:31

できるはずと書きましたが、ちゃんと調べてないので本当にできるかどうかは知りません。これで無理なら System.Reflection.Emit 空間のクラス群を使って動的メソッドを生成すればできそうです。これもやったことはないので自分で確かめてください。
退会済みユーザー

退会済みユーザー

2019/11/23 23:41 編集

上はオブラートに被せてレスしましたが、はっきり言って、デメリットしかなくて、質問者さんが言われるように「ちんぷんかんぷん」な話と思います。 スパゲッティプログラムでググってみてください。
退会済みユーザー

退会済みユーザー

2019/11/23 23:48

高速動作するのかどうか分かりませんが、もしそうだとしても、そこがアプリのボトルネックになっていて、絶対改善が必要という実務的な話ではないですよね?
Zuishin

2019/11/24 01:14 編集

> ここで、C#で上記と同様の内容を書くことはできないでしょうか?というのが質問です。 について、各回答者に見解の相違があるようです。 > すなわち、ラベルを値として扱えないか ということですが、raccy さんが根拠を明確にされた通り、また質問者さんの確かめられた通り、C# の文法の範囲内ではラベルを値として使うことはできません。ここまでは大前提で、答えは No になります。 そこで私は、「C# で」を「C# の文法として」ではなく「C# を使用して」と読むなら「ラベルを値として扱うことができる」と示唆しています。これは「アセンブリの学習目的」という方針を根拠にしたものです。 y_waiwai さんの回答は、それは GCC 独自の拡張であって、C 言語でもできないし、可読性が落ちて危険性が増すのですべきではない、よって C# も同様であるというものです。SurferOnWww さんの意見も同主旨と感じています。 asm さんと catsforepaw さんの回答は、コンパイル後は同じものになるので switch を使うのが良いというものです。これは、 > これは、labelが何十個と増えた場合に、if else文やswitch文より高速に動作するように思えます。 に答えたものだと思います。 そして maisumakun さんの回答は、配列にデリゲートを入れるという、実務でもよく使われるテクニックについての言及です。これは主に分岐後の処理が動的なものである時、または非常に多く switch では可読性が落ちる時に使われる実用的なもので、質問の背景を想像して書かれたものではないかと思います。 質問に対する回答は全員「No」です。まだ解決しないのであれば、質問を編集し、どの部分をもっと深く知りたいのかを明確にしてください。
guest

回答6

0

これは、labelが何十個と増えた場合に、if else文やswitch文より高速に動作するように思えます。

コンパイラの最適化によってswitch文がテーブルジャンプに最適化されることは割とあります。
C#もSWITCH命令によるジャンプテーブルになる場合があります。

適当に調べたところ3分岐以上ではジャンプテーブルを使うようです。


追記
ついでにgccによるC言語の場合さらに簡潔になりジャンプがなくなります。
基本的にこのレベルのコンパイラができる最適化は人間がやる理由がありません。
古い・もしくは最適化しないコンパイラを使わざるを得ない時、計測の結果、性能が足りない時に行う最後の手段です。

C#が動く時点で、そんな曲芸が必要な環境はほぼ皆無だと思います。

投稿2019/11/24 00:06

編集2019/11/24 00:35
asm

総合スコア15149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

これは、labelが何十個と増えた場合に、if else文やswitch文より高速に動作するように思えます。

それは気のせいです。if文はともかく(ご質問のようなコードだとif文は不適当ですね)、switch文に関してはほぼ差はないでしょう。

コンパイル時にswitch文をテーブルジャンプに置き換えるというのは古くからある最適化の手法です。それはCもC#も同様です。

「そんな気がする」というだけで無意味な判りづらいコードを書くよりも、素直に書いてコンパイラーの最適化に任せる方が、大抵の場合は良い結果をもたらします。速さを追求するのなら最適化について学ぶと良いでしょう(アセンブラーの知識が要求されますが)。

投稿2019/11/24 00:53

catsforepaw

総合スコア5944

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

現在のC# 8.0では仕様上は無理です。

Statements - C# language specification | Microsoft Docs

※ 上はC #6.0のドラフト仕様ですが、C# 8.0までに変更されたというのは見つけられないため、このままと思われます。

text

1goto_statement 2 : 'goto' identifier ';' 3 | 'goto' 'case' constant_expression ';' 4 | 'goto' 'default' ';' 5 ;

普通のgotoに続くのは識別子(identifier)のみ、goto caseに続くのは定数式(constant expression)のみ、そして最後はgote default;で完結している文だけがgoto文として使えます。全てコンパイル時に確定され、動的に値が変わるような式を入れることはできません。

投稿2019/11/23 23:24

raccy

総合スコア21737

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

あくまでそれはGCC独自の拡張なだけで、他のコンパイラで使用できるとは限らないので注意しましょう。

早い話がアドレスのテーブルを作って、そこにジャンプしているだけって話ですが、
これである程度大規模なコードを組んでみるとわかりますが、こういうので一歩間違えると、一発暴走するようになります

そのテーブルに設定される値が果たして安全なアドレス値なのか、がチェックできない時点で、こんな危険な構文は全く意味がないと断じることができるかと思います

投稿2019/11/23 23:05

y_waiwai

総合スコア88038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

自己解決

ご回答ありがとうございました。

質問修正欄でZuiShinさんがわかりやすくまとめてくださっていますが、ベストアンサーにできないのでこちらでまとめました。

まず、最適化オプションを有効にしてビルドしたところ、switch文で意図した結果が得られたので、その点で問題は解決しました。asmさん、catsforepawさん、ありがとうございました。

次に、デリゲート配列はたまに使うのですが、今回はメソッドを使いたくなかったのでパスしました。maisumakunさん、ありがとうございました。

さて、ラベルを値として扱う方法ですが、こちらに記載された System.Reflection.Emit.Label の例が動作しました。System.Linq.Expressions.LabelExpression でもできそうですが、正直私にはまだ難しいと感じました。ZuiShinさん、raccyさん、ありがとうございました。

投稿2019/11/24 02:39

編集2019/11/24 02:43
Aty

総合スコア7

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

配列にラムダ式を入れておいてそれを呼び出す、みたいな手段で代替できないでしょうか。

投稿2019/11/23 23:12

maisumakun

総合スコア145975

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問