ポインタ
C言語
More than 1 year has passed since last update.


この記事は、かなり初心者 Advent Calendar 2017に登録させていただいてます。


c言語には関数ポインタとやらがあると、つい最近知りました。

それがあまりにも便利だったので、ここで紹介させてください。

ちなみに今日(12/19)は僕の誕生日です。

ジョジョリオンの最新刊の発売日でもあります。

                   /::::::::::: i::::::::: |:::::::: ヽ::::::::::::::::::::::::::::::::::ゝ

                   イ:::::i:::::::ハ::::::::: ヾ:::: |:::\:::ハ::::| :::::::::::::::::::::::\

                   |::|::::l:::!:::/ \:::::: ヾ:::|::::::::| ::i::::1::リ:::: |: i::::::::::::::ゝ

 __                |::|::::|:::l::ハ| ヾ ::::::| ヽィ::::j/=、|::::::|::::7: /:::i::::| ____

     \ :'´⌒ヽ          |::ハ:::V:::| イ⌒゙`\:i リ  \|ノ 弋
フノ /:::/: /

      |i   " )_,,, _     l:ゝ::.\::i〃⌒゙ヽ  〃⌒゙ヾ //::) | 'ハ::::: |

 た や |i         ヽ   | ト/人7} 〃〃     〃〃´ ∠イr 'ちノ::::: |  ふ 年

 え っ  |i      / ・ i     イ:リ::::|    '、     |:::::rイ:::::::: |  え 齢

 ち た  |i          t    / i:::::ハ    r‐--ー、     /ハi!:::::::::::::::: |  る が

 ゃ ね  |i           〃 ●   ハ::::::: \   .イ_ ,,ツ  イ/'/:::::::::::::. <  よ

 ん    |i   r一 ヽ      )  /i::ハi::::i:::::>,, 
__ ,, ´ /,,ハ/|/:::ii::::::::  |  !!

 !    |i   |   i   ∀"    "  ̄ ̄     ト、  //ヽ  ̄" ̄   |

        |i   |    i   ノi         ノ:r j      :ア` …‐:  |

       |i ニ|   |二二◎        
,..'| /       /   :::: |

       |i  i    i   ヽ     _
,,:'´  t/      /    :: |

       li           }  ,:'´    {    ,,__ /     ,,/i \____

       |i           |  /j\    :ヘ:ニヽ,,,/,,    , /:::j      j

  __ /          / ⌒`)⌒) i:::::ヽ::`r‐'___ `   ヽ ,,:_,,_,,/:::::ノ"ノシ    〃

      ,ノ フr フ   メ   / ノ  ゝ:::::: ゝ- 、 ヽ     |::::::::::::::::::::ソ /     ./

変数名は適当です。


基本的な使い方


宣言編

宣言は、次のようにします。

void (*funcp)();

ここで、funcpは、void型関数のポインタです。

他のポインタ型と同じように、void型関数のポインタとしてのみ、使えます。

int型関数のポインタとして使おうものなら、コンパイラに全力で怒られます。

voidをintならint、charならcharにすればおkです。


代入編

代入は、普通にやればいいのです。

では、例をば。

funcp = func;

まあ、普通です。

ちなみに、funcは関数ですよ。

func()ではないですよ。

宣言と同時でもおkです。


実行編

funcp()

普通です。紹介に一行使うのも、もったいないぐらい普通です。


応用的な使い方


配列編

関数ポインタは、あくまで変数です。なので、配列にすることもできます。

void (*funcp[7])() = {func0,func1,func2,func3,func4,func5,func6};

for (size_t i = 0; i < 7; i++) {
funcp[i]();
}

for (size_t i = 0; i < 7; funcp[i++]());
//for文の増減式は1ループ終了ごと、条件式は1ループ開始ごとに実行・判断されます。
//また、インクリメントは返り値として、インクリメント前の値を返します。
//なので、この式の実行結果は、2つ目の式と同じになります。

これだけなら別に必要ないかもしれませんが、実は擬似switch文として使えます。

switch(x){

case 1:
//処理1
break;
case 2:
...
case n:
//処理n
break;
}

//これの代わりに、

hogehoge (*funcp[PIYOPIYO])();

funcp[0] = func0;
funcp[1] = func1;
...

funcp[x]();

こうすることで、場合によっては非常にソースコードの可読性が上がります。

また、何度も同じswitch文が登場場合に、冗長なコードになるのを防げます。

まあ、使えるのは限られた場合ですけどね。


typedef編

関数ポインタ型に対して、typedefで簡潔に表すことができます。

typedef int (*type_funcp)(int, int);

int add(int a, int b){return a + b;}

--------------

type_funcp funcp = add;

printf("2 + 3 = %d\n", funcp(2, 3));

//2 + 3 = 5

この場合、type_funcpは2つのintを引数に持つint型関数ポインタ型になります。


おまけ

関数ポインタをグローバル変数にすることもできます。

void (*funcp_global)();

void func100() {
printf( "100\n" );
}

void func11() {
printf("11\n" );
funcp_global = func100;
}

void func10() {
printf("10\n" );
funcp_global = func11;
}

--------------

funcp_global = func10;

for (size_t i = 0; i < 3; i++)funcp_global();

//10
//11
//100

多分需要ないと思います。(もしかしたらあるかも。僕には使い方がわかりません。)