gcc 拡張を使うと、関数の中に関数を定義することができる。これを利用すると C でもクロージャが使えることを少し前に知ったが、ようやく理解できた…気がする。

クロージャとは

C 言語のコールバック関数に近いが、引数以外の変数を自身が定義された静的スコープで解決することが最大の特徴。

…だと理解しています。^^;

コーディング例

#include <stdio.h>

/* 任意の関数 func を n 回実行するだけのライブラリ関数 */
void
dolist(void (*func)(void), int n)
{
    int i;

    for(i = 0; i < n; i++) {
	func();
    }

    return;
}

int
main(void)
{
    int level = 0;

    void lambda(void)
    {
	level++;
    }

    printf("%d\n", level);

    dolist(lambda, 1);
    printf("%d\n", level);

    dolist(lambda, 2);
    printf("%d\n", level);

    dolist(lambda, 3);
    printf("%d\n", level);

    return 0;
}

結果

0
1
3
6

Solaris9 の /usr/local/bin/gcc で確認した。

dolist は渡された処理(lambda)を、指定された回数分実行するだけのライブラリ関数。

ユーザ(main)は処理内容を、自分の静的スコープの中で自由に定義することができる。level を外部変数にしなくて良い点が素晴らしい。狭いスコープ大事。

lambda() を nm で確認したところ、t マークが付いていた。lambda() はスタックに展開されているわけではなく、static な関数として定義されているということ。

lambda() と level の関係はどうなっているんだろう?