C プログラミングの関数ポインターと例
⚡ スマートサマリー
C言語の関数ポインタは実行可能コードのアドレスを格納し、プログラムが関数を間接的に呼び出したり、引数として渡したり、配列からディスパッチテーブルを作成したり、qsort比較ルーチンなどのコールバックを実装したりすることを可能にし、柔軟で再利用可能な設計を実現します。

ポインタは、XNUMX つの値を返すように制限されている 'C' 関数に大きな可能性を与えます。 ポインター パラメーターを使用すると、関数はデータのコピーではなく実際のデータを処理できるようになりました。
変数の実際の値を変更するには、呼び出しステートメントで関数内のポインター パラメーターにアドレスを渡します。
関数ポインタの例
たとえば、次のプログラムは XNUMX の XNUMX つの値を交換します。
void swap (int *a, int *b); int main() { int m = 25; int n = 100; printf("m is %d, n is %d\n", m, n); swap(&m, &n); printf("m is %d, n is %d\n", m, n); return 0;} void swap (int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp;} }
出力:
m is 25, n is 100 m is 100, n is 25
関数はアドレスによって変数にアクセスするため、プログラムは実際の変数値を交換します。 ポインタ。 ここではプログラムのプロセスについて説明します。
- スワップを担当する関数を宣言しますping 2 つの変数値。これは、2 つの整数ポインタをパラメータとして受け取り、呼び出されたときに任意の値を返します。
- main 関数では、XNUMX つの整数変数 ('m' と 'n') を宣言して初期化し、それぞれの値を出力します。
- アンパサンド記号を使用して XNUMX つの変数のアドレスを引数として渡し、swap() 関数を呼び出します。 その後、変数の新しく交換された値を出力します。
- ここでは、2 つの整数変数アドレスをパラメーターとして受け取る swap() 関数の内容を定義し、2 番目の変数に配置される値変数の 1 つを保存するための 3 番目のストレージ ボックスとして使用される一時的な整数変数を宣言します。
- 「a」が指す最初の変数の内容を一時変数に保存します。
- b が指す XNUMX 番目の変数を、a が指す最初の変数に格納します。
- 一時変数に保存された最初の変数の値で XNUMX 番目の変数 (b で指定) を更新します。
配列パラメータを持つ関数
C では、配列を値で関数に渡すことはできません。 一方、配列名はポインタ (アドレス) であるため、配列名を関数に渡すだけで、これは配列へのポインタを渡すことを意味します。
たとえば、次のプログラムを考えます。
int add_array (int *a, int num_elements); int main() { int Tab[5] = {100, 220, 37, 16, 98}; printf("Total summation is %d\n", add_array(Tab, 5)); return 0;} int add_array (int *p, int size) { int total = 0; int k; for (k = 0; k < size; k++) { total += p[k]; /* it is equivalent to total +=*p ;p++; */} return (total);}
出力:
Total summation is 471
ここではプログラムコードの詳細を説明します。
- add_array() 関数を宣言して定義します。この関数は、配列アドレス (ポインタ) とその要素番号をパラメータとして受け取り、これらの要素の累積合計を返します。 ポインタは配列要素を反復するために使用され (p[k] 表記を使用)、要素配列全体を反復した後に返されるローカル変数に合計を蓄積します。
- XNUMX つの整数要素を含む整数配列を宣言して初期化します。 配列名 (アドレスとして機能します) と配列サイズを add_array()関数を引数として呼び出します。
配列を返す関数
C では、次のプログラムのように、配列へのポインターを返すことができます。
#include <stdio.h> int * build_array(); int main() { int *a; a = build_array(); /* get first 5 even numbers */ for (k = 0; k < 5; k++) printf("%d\n", a[k]); return 0;} int * build_array() { static int Tab[5]={1,2,3,4,5}; return (Tab);}
出力:
1 2 3 4 5
ここではプログラムの詳細について議論します
- 整数値を含む配列アドレスを返し、引数をとらない関数を定義して宣言します。
- 関数の呼び出し後に構築された完全な配列を受け取る整数ポインターを宣言し、XNUMX つの要素の配列全体を反復してその内容を出力します。
関数によって返される配列アドレスを格納するために、配列ではなくポインターが定義されていることに注意してください。 また、ローカル変数が関数から返されるときは、関数内でそれを static として宣言する必要があることに注意してください。
関数ポインタ
定義により、ポインタはメモリ内の任意の場所のアドレスを指すことがわかっていますが、メモリ内の関数として実行可能コードの先頭を指すこともできます。
関数へのポインターは * で宣言されます。その宣言の一般的なステートメントは次のとおりです。
return_type (*function_name)(arguments)
(*function_name) を囲む括弧が重要であることに注意してください。括弧がないと、コンパイラーは function_name が return_type のポインターを返しているとみなしてしまうからです。
関数ポインターを定義したら、それを関数に割り当てる必要があります。たとえば、次のプログラムでは、通常の関数を宣言し、関数ポインターを定義し、関数ポインターを通常の関数に割り当て、その後、ポインターを通じて関数を呼び出します。
#include <stdio.h> void Hi_function (int times); /* function */ int main() { void (*function_ptr)(int); /* function pointer Declaration */ function_ptr = Hi_function; /* pointer assignment */ function_ptr (3); /* function call */ return 0;} void Hi_function (int times) { int k; for (k = 0; k < times; k++) printf("Hi\n");}
出力:
Hi Hi Hi
- 関数が呼び出されたときに、パラメーターで示される Hi テキストを k 回出力する標準関数を定義して宣言します。
- 整数パラメーターを受け取り、何も返さないポインター関数を (特別な宣言を使用して) 定義します。
- ポインター関数を Hi_function で初期化します。これは、ポインターが Hi_function() を指すことを意味します。
- 標準関数呼び出しではなく、ping 引数付きの関数名ですが、引数として数値3を渡してポインタ関数を呼び出すだけで、それで完了です!
関数名は、最初の要素を指す配列名と同様に、実行可能コードの先頭アドレスを指すことに注意してください。 したがって、function_ptr = &Hi_function や (*funptr)(3) のような命令は正しいです。
注意: 関数の割り当てと関数の呼び出し中に、アドレス演算子 & と間接演算子 * を挿入することは重要ではありません。
関数ポインタの配列
次のプログラムのように、関数ポインターの配列は、意思決定を行うためのスイッチまたは if ステートメントの役割を果たすことができます。
#include <stdio.h> int sum(int num1, int num2); int sub(int num1, int num2); int mult(int num1, int num2); int div(int num1, int num2); int main() { int x, y, choice, result; int (*ope[4])(int, int); ope[0] = sum; ope[1] = sub; ope[2] = mult; ope[3] = div; printf("Enter two integer numbers: "); scanf("%d%d", &x, &y); printf("Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: "); scanf("%d", &choice); result = ope[choice](x, y); printf("%d", result); return 0;} int sum(int x, int y) {return(x + y);} int sub(int x, int y) {return(x - y);} int mult(int x, int y) {return(x * y);} int div(int x, int y) {if (y != 0) return (x / y); else return 0;}
Enter two integer numbers: 13 48
Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: 2
624
ここでは、プログラムの詳細について説明します。
- XNUMX つを宣言し、定義します。 機能 これらの関数は2つの整数引数を受け取り、整数値を返します。tract は、ユーザーがどの関数を呼び出しているかに関する 2 つの引数を乗算および除算します。
- オペランド、演算タイプ、結果をそれぞれ処理するために 4 つの整数を宣言します。また、XNUMX つの関数ポインターの配列を宣言します。配列要素の各関数ポインターは、XNUMX つの整数パラメーターを受け取り、整数値を返します。
- すでに宣言されている関数を使用して、各配列要素を割り当てて初期化します。たとえば、3 番目の関数ポインターである 3 番目の要素は、乗算演算関数を指します。
- ユーザーがキーボードで入力したオペランドと演算の種類を取得します。
- 引数を指定して適切な配列要素 (関数ポインタ) を呼び出し、適切な関数によって生成された結果を保存します。
命令 int (*ope[4])(int, int); 関数ポインタの配列を定義します。 各配列要素は同じパラメータと戻り値の型を持つ必要があります。
ステートメント result = ope[choice](x, y); ユーザーの選択に従って、適切な関数を実行します。入力された XNUMX つの整数は、関数に渡される引数です。
void ポインターを使用する関数
Void ポインターは関数宣言中に使用されます。 void * 戻り型を使用すると、任意の型を返すことができます。 関数に渡すときにパラメーターが変更されないと仮定する場合は、それを const として宣言します。
具体的な例を挙げますと、以下の通りです。
void * cube (const void *);
次のプログラムを検討してください。
#include <stdio.h> void* cube (const void* num); int main() { int x, cube_int; x = 4; cube_int = cube (&x); printf("%d cubed is %d\n", x, cube_int); return 0;} void* cube (const void *num) { int result; result = (*(int *)num) * (*(int *)num) * (*(int *)num); return result;}
結果:
4 cubed is 64
ここでは、プログラムの詳細について説明します。
- 整数値を返し、特定のデータ型を持たない変更不可能な変数のアドレスを取得する関数を定義して宣言します。 num ポインターが指すコンテンツ変数 (x) の cube 値を計算します。これは void ポインターであるため、特定の表記法 (* datatype) ポインターを使用して整数データ型に型キャストする必要があり、次の値を返します。立方体の値。
- オペランドと結果変数を宣言します。また、オペランドを値「4」で初期化します。
- オペランドアドレスを渡してキューブ関数を呼び出し、戻り値を結果変数で処理します。
引数としての関数ポインタ
関数ポインタを別の関数に引数として渡すことで関数ポインタを悪用するもう XNUMX つの方法は、受信関数がそれを「コールバックする」ため、「コールバック関数」と呼ばれることもあります。
stdlib.h ヘッダー ファイルでは、Quicksort の「qsort()」関数が、配列のソート専用のアルゴリズムであるこの手法を使用します。
void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
- void *base : 配列への void ポインタ。
- size_t num : 配列の要素番号。
- size_t width 要素のサイズ。
- int (*compare (const void *, const void *) : 0 つの引数で構成される関数ポインター。引数の値が同じ場合は 0 を返し、arg1 が arg2 の前に来る場合は <0 を返し、arg1 が arg2 の後に来る場合は >XNUMX を返します。
次のプログラムは、qsort() 関数を使用して整数配列を小さい数から大きい数の順にソートします。
#include <stdio.h> #include <stdlib.h> int compare (const void *, const void *); int main() { int arr[5] = {52, 14, 50, 48, 13}; int num, width, i; num = sizeof(arr)/sizeof(arr[0]); width = sizeof(arr[0]); qsort((void *)arr, num, width, compare); for (i = 0; i < 5; i++) printf("%d ", arr[ i ]); return 0;} int compare (const void *elem1, const void *elem2) { if ((*(int *)elem1) == (*(int *)elem2)) return 0; else if ((*(int *)elem1) < (*(int *)elem2)) return -1; else return 1;}
結果:
13 14 48 50 52
ここでは、プログラムの詳細について説明します。
- 0 つの引数で構成される比較関数を定義し、引数が同じ値を持つ場合は 0 を返し、arg1 が arg2 の前に来る場合は <0、arg1 が arg2 の後に来る場合は >XNUMX を返します。パラメータは、適切な配列データ型にキャストされた void ポインター型です。 (整数)
- 整数配列を定義して初期化します。配列サイズは NUM 変数と各配列要素のサイズは、事前定義された sizeof() を使用して width 変数に格納されます。 C演算子.
- qsort関数を呼び出し、配列名、サイズ、幅、およびユーザーが事前に定義した比較関数を渡すことで、配列を昇順にソートします。比較は、配列全体がソートされるまで、各イテレーションで2つの配列要素を取り込んで実行されます。
- 配列要素を出力して、配列全体を反復処理して配列が適切にソートされていることを確認します。 forループ.







