関数へのポインタ(関数を指すポインタ)
Copyright(C) 14May2003 coskx
関数を指すポインタを紹介する。これは,関数の先頭アドレスを保存する事のできるポインタ変数である。
1.配列の先頭アドレスと,関数の先頭アドレス | |
配列名は,そのまま配列の先頭番地を意味していた。これと同じように,関数名は機械語に変換された関数の先頭番地を表している。
ゆえに,どちらもprintfでは%pで表示することが出来る。
List.1 配列の先頭アドレスと,関数の先頭アドレス |
#include <stdio.h> void printarray(int ary[], int num) { int i; for (i=0; i<num; i++) { printf("%d : %d\n",i+1,ary[i]); } } int main() { int array[]={3,5,7,9}; printarray(array,4); printf("address of array = %p\n",array); printf("address of printarray = %p\n",printarray); return 0; } |
実行結果 |
1 : 3 2 : 5 3 : 7 4 : 9 address of array = 01B3F768 address of printarray = 00A61260 |
2.関数へのポインタ(関数の先頭アドレスを指すポインタ変数) | |
関数名は,(機械語に変換された)関数の先頭アドレスを意味していることが確認できた。
関数の先頭アドレスを保存できる関数へのポインタ変数がある。
int型変数を表すポインタ変数pnthensuの宣言は int *pnthensu,あるいは int* pnthensuであった。
関数の場合は宣言表現が少々複雑になる。
int型の値ataiを与え,int型の値を返す関数kansuなら int kansu(int atai) であるが,
int型の値ataiを与え,int型の値を返す関数を指すポインタ変数pntkansuなら
関数名のところが(*pntkansu)になって,引数名は定まっていないので(int)だけになるので
int (*pntkansu)(int) になる。
(かっこを付けずに int *pntkansu(int) では演算子の優先順位のため (int *)pntkansu(int) の用に解釈されてしまうため,コンパイルエラーになってしまう。)
関数へのポインタ変数に値(関数の先頭アドレス)が保存されてしまえば,このポインタ変数は関数と同じように使うことが出来る。
List.2 関数へのポインタの利用 |
#include <stdio.h> void printarray(int ary[], int num) { int i; for (i=0; i<num; i++) { printf("%d : %d\n",i+1,ary[i]); } } int main() { int array[]={3,5,7,9}; void (*ptrtofunc)(int[],int); printarray(array,4); printf("address of array = %p\n",array); printf("address of printarray = %p\n",printarray); ptrtofunc=printarray; ptrtofunc(array,4); return 0; } |
実行結果 |
1 : 3 2 : 5 3 : 7 4 : 9 address of array = 0216FDE8 address of printarray = 01061260 1 : 3 2 : 5 3 : 7 4 : 9 |
3.関数へのポインタ(関数の先頭アドレスを指すポインタ)の配列 | |
Lint.2のような例では,関数へのポインタのありがたみがわからない。
関数へのポインタは,「関数へのポインタの配列」として,場合分けをスマートに記述するのに使われることがある。
ある変数の値に応じて呼び出す関数を変更するには,if-elseif構文,あるいはswtch-case構文で記述する場合が多いが,
これをスマートに表現するプログラムを書いてみよう。
通常使われる例
if (i==1) func1();
else if (i==2) func2();
else func3();
List.3 関数へのポインタの配列 |
#include <stdio.h> void printarray1(int ary[], int num) { int i; printf("printarray1\n"); for (i=0; i<num; i++) { printf("%d : %d\n",i+1,ary[i]); } printf("\n"); } void printarray2(int ary[], int num) { int i; printf("printarray2\n"); for (i=num-1; 0<=i; i--) { printf("%d : %d\n",i+1,ary[i]); } printf("\n"); } void printarray3(int ary[], int num) { int i; printf("printarray3\n"); for (i=0; i<num; i++) { printf("%d : %c\n",i+1,(char)(ary[i]+0x40)); } printf("\n"); } int main() { int array[]={3,5,7,9}; void (*ptrtofunc[])(int[],int)={ printarray1,printarray2,printarray3 }; int select; printf("select 1,2,3 0:quit ==> "); scanf("%d",&select); while(0<select && select<4) { ptrtofunc[select-1](array,4); printf("select 1,2,3 0:quit ==> "); scanf("%d",&select); } printf("quit\n"); return 0; } |
実行結果 |
select 1,2,3 0:quit ==> 1 printarray1 1 : 3 2 : 5 3 : 7 4 : 9 select 1,2,3 0:quit ==> 2 printarray2 4 : 9 3 : 7 2 : 5 1 : 3 select 1,2,3 0:quit ==> 3 printarray3 1 : C 2 : E 3 : G 4 : I select 1,2,3 0:quit ==> 0 quit |
次の4,5の項目「ソートアルゴリズム」はクイックソートのところで再掲される。
4.C標準ライブラリのクイックソート(1) | |
ソートアルゴリズムの1つクイックソート使えるようにするため,C言語では標準ライブラリとしてクイックソートが提供されている。
昇順にソートしようとする時も降順にソートしようとする時も同じクイックソート関数を使うことができるように,2つのデータの比較方法を与える関数を別に定義し,標準ライブラリのクイックソート関数に,この比較関数を使ってほしいとお願いするプログラミングとなる。
このとき,比較関数を与えるのに,関数へのポインタが使われる。
クイックソート関数を使う場合は,「stdlib.h」をインクルードする。
qsort()はstdlib.h中で次のようにプロトタイプ宣言されている
void qsort(void *base, size_t num, size_t size, int (*compare)(const void*, const void*)) |
ここで「int (*compare)(const void*, const void*)」は「2つのデータの比較方法を与える」関数へのポインタであり,その返す値はint型である。2つのデータの大きさの状態によって,正の値・0・負の値を返すように作っている。
引数は2つあり,比較すべき2つのデータへのポインタとなっている。
なおこの比較関数の名前はcompareである必要はなく,なんでもよい。
List.4 標準ライブラリのクイックソート関数を使うプログラム例 |
#include "stdio.h" #define SIZE 20 void printdata(int x[], int num) /*小さい順ソート済みデータのチェック /*比較関数 昇順ソートの場合 */ |
実行例 |
41 67 34 0 69 24 78 58 62 64 5 45 81 27 61 91 95 42 27 36 0 5 24 27 27 34 36 41 42 45 58 61 62 64 67 69 78 81 91 95 good |
5.C標準ライブラリのクイックソート(2) | |
C標準ライブラリのクイックソートは比較関数仕様が柔軟にできているため(扱いにくいが),構造体のソートも比較関数をスイッチするだけでできる。
List.5 標準ライブラリのクイックソート関数で構造体を扱うプログラム例 |
#include <stdio.h> typedef struct { island_t Island[]={ void printData(island_t data[], int num, char *title) int compfuncName(const void *a, const void *b) int compfuncPopulation(const void *a, const void *b) int compfuncArea(const void *a, const void *b) int main() |
実行例 |
初期状態 名前順 人口順 面積順 |