Cコーディング設計-関数ポインター?


24

私が持っているPIC18F46K22をしてXC8コンパイラでそれをプログラムします。最後に、私は、搭載したPCのようなシステムがあるでしょうstdinし、をstdout。したがって、メインループには、新しい入力があるかどうかをチェックする関数があります。入力がある場合、それに応じて関数が呼び出されます。たとえば、Aを入力するstdinと、PICはBを入力したときに呼び出される関数のfunction_A代わりに、次のような関数を実行しますfunction_B

関数でPICが完了したら、新しい入力が関数に送信されるようにします。したがって、Aを押すとRS232トランスミッタが開き、その瞬間からすべての入力がRS232経由で送信されます。最終的に、プロジェクトはスタンドアロンのテキストエディターです。そのため、Aを押すとファイルシステムが開き、その瞬間からテキストの編集は行われず、ファイルのリストを確認します。つまり、上と下を押すことは、テキスト編集環境とは異なることを意味します。

私はこれをCでプログラムする方法について多くのことを考えてきました。昨晩、これを考えて、可能かどうか、可能であればどのようにしたらよいかを知りたいと思います。私がやりたいことは:

  • main機能のような関数を呼び出しますfunction_A
  • function_Aグローバル変数function_addrを関数のアドレスポインターに変更しますin_function_A
  • その瞬間から、新しい入力があるときにmain関数を呼び出しfunction_addrます。

したがって、必要なのは、ゼロmainかどうかをチェックする関数function_addrです。もしそうなら、「通常の」関数を呼び出す必要がありますfunction_A。そうでない場合、関数at function_addrを呼び出す必要があります。またfunction_Afunction_addrへのポインタに変更するが必要in_function_Aです。

注:ファイルシステム関数を閉じる必要がある場合は、0にis_function_A変更function_addrするだけです。

だから基本的に私の質問は

  • 関数のアドレスを取得(および変数に格納)
  • 指定されたアドレスで関数を呼び出す

6
オフトピック。stackoverflow.comに属します。
user207421

私にとって、ステートマシンのアプローチは、関数ポインターを扱うよりもはるかにリスクが低いです。入力バイトは、1つまたは複数の状態変数の関数としてさまざまなコードにジャンプする状態マシン構造体(スイッチケースのように単純な場合もあります)に渡されます。また、あなたはあなたのstdinがどこから来ているのか完全に明確ではありません(それは本当に重要なことではありませんが、私は興味があります)。
アダムローレンス

9
@EJP同意しない。重複があるからといって、あるサイトまたは別のサイトにある必要があるわけではありません。低レベルの組み込みシステムの設計とプログラミングに関連する質問をしているが、それはどちらのトピックにも当てはまるようだ。
Kortuk

状態マシンは、関数の間接指定に基づくことができます。状態遷移関数の呼び出しは、新しい状態遷移関数を返します。
カズ

1
ここでこの議論をしましょう。

回答:


21

機能

int f(int x){ .. }

関数のアドレスを取得(および変数に格納)

int (*fp)(int) = f;

指定されたアドレスで関数を呼び出す

int x = (*fp)( 12 );

3
+ 1、CおよびASMの世界では明らかですが、OO言語で始めた人にはそれほど明白ではありません。
アニンドゴーシュ

4
fp呼び出しは、「int x = fp(12);」と書くこともできます。読みやすくします。
-Rev1.0

1
現在主にC#とJavaで作業していることを認めざるを得ませんが、Cからの古き良き関数ポインタが恋しいときがあります。それらは正確に正しく行われていないと危険であり、ほとんどのタスクは他の方法で達成できますが、実際に役立つときには数回しか役に立ちません。
CVn

@MichaelKjörling:本当です。私は常に組み込みCとC#プログラミングを切り替えています(デバイスとPC間の通信に同様のコードを実装している場合でも)。
-Rev1.0

2
fp(12)読みやすさは向上しません(*fp)(12)。読みやすさが異なります。ポインタである(*fp)(12)ことを読者に思い出させ、fpの宣言にも似ていfpます。私の考えでは、このスタイルはX11内部やK&R Cなどの古いソースに関連付けられています。K&R Cに関連付けられる可能性のある理由の1つは、ANSI Cでは、パラメーターのfp場合に関数構文を使用して宣言できるためfpです: int func(int fp(double)) {}。K&R Cではそうint func(fp) int (*fp)(double); { ... }でした。
カズ

17

しばらくWOUTERSの答えは絶対にこの多分より多くの初心者に優しく、あなたの質問については、より良い例を修正しています。

int (*fp)(int) = NULL;

int FunctionA(int x){ 
    return x + x;
}

int FunctionB(int x){ 
    return x * x;
}

void Example(void) {
    int x = 0;

    fp = FunctionA;
    x = fp(3);  /* after this, x == 6 */

    fp = FunctionB;
    x = fp(3);  /* after this, x == 9 */
}

関数ポインタに実際にターゲット関数アドレスが割り当てられていることが明らかでない場合は、NULLチェックを実行するのが賢明です。

if (fp != NULL)
    fp(); /* with parameters, if applicable */

8
ヌルチェックの場合は+1ですが、RAM内のアドレスのアドレスが最初に実際にNULLであることをコンパイラが保証しない場合があることに注意してくださいfp。私はどうなるint (*fp)(int) = NULL;特定する組み合わせの宣言と初期化など-あなたは理由だけでそこに起こったことをいくつかの間抜け値のいくつかのランダムなメモリの場所にジャンプする必要はありません。次にfpExample()関数のように割り当てます。
CVn

1
コメントをありがとう、NULL初期化を追加しました。
Rev1.0

4
@MichaelKjörlingの良い点ですfpが、「グローバル変数」の場合(上記のコードのように)、初期化されることが保証されますNULL(明示的に行う必要はありません)。
アロク
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.