Programming組み込みC開発者

C言語におけるコールバック関数の実装と動作を説明してください。ライブラリ開発やAPIとの相互作用において、これらの関数を正しく宣言し使用する方法は何ですか?

Hintsage AIアシスタントで面接を突破

回答

コールバック関数は、別の関数に引数として渡される関数のアドレスです。これにより、イベントハンドラー、ユーザーアルゴリズム、プラグインを実装できます。

コールバック関数の宣言:

  1. 関数ポインタの適切な型を定義します:
typedef void (*callback_func_t)(int);
  1. ハンドラ関数を渡す:
void process(callback_func_t cb) { // ... cb(42); // コールバックの呼び出し } void handler(int n) { printf("処理された数: %d ", n); } int main() { process(handler); return 0; }

アドバイス:

  • ポインタ型には明示的なtypedefを使用して、コードを読みやすくします。
  • コールバック関数のシグネチャが期待されるものと一致していることを確認します。
  • 初期化されていないローカル関数や解放された関数を渡すことは避けます。

ひねりのある質問

シグネチャが一致しない関数をコールバックとして渡すことはできますか?

よくある誤った回答: 「はい、明示的な型変換を宣言すればCは許可します。」

正しい回答: 形式的な型変換は可能ですが、そのような関数を呼び出すことは未定義の動作につながります — パラメータに無効な値が渡され、スタックが壊れる可能性があります。

危険の例:

typedef void (*cb_t)(int); void wrong_cb(double d) { printf("%f ", d); } void call(cb_t f) { f(123); } int main() { call((cb_t)wrong_cb); } // 危険: シグネチャが異なります

このテーマの詳細を知らないことによる実際のミスの例


物語

ある組み込みプロジェクトにおいて、ソート関数はシグネチャが狭すぎる関数ポインタを比較関数として受け取っていました。これにより、パラメータの不正な渡しによるソートエラーとメモリ破損が発生しました。

物語

あるグラフィックスエンジンライブラリではコールバックベースのイベントメカニズムが実装されていましたが、一部のコールバックがすでに解放された動的ライブラリのローカル関数を偶然参照していたため、不正なアドレスに遭遇した際にクラッシュが発生しました。

物語

クロスプラットフォームシステムを開発する際、著者はコールバック関数の呼び出し規約を誤って定義しました。これにより、あるOSでは問題が発生しませんでしたが、別のOSでCライブラリからコールバックを呼び出すとプログラムがクラッシュしました。