本の虫

著者:江添亮
GitHub pages: https://ezoeryou.github.io/blog/
Blogger: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

C99のVLAは関数の本体が空でもコードが書ける

https://lemon.rip/w/c99-vla-tricks/

C99のVLAはだいぶ狂っている。

C99のVLA(Varriable Array Length)は実行時にサイズが決定される配列だ。VLAは関数の引数にも使える。

void f( int n, int a[n] )
{
    int b[n] ;
    // sizeof(int *)
    int A = sizeof(a) ;
    // sizeof(int) * n
    int B = sizeof
}

配列型の引数は通常のルールにしたがってポインター型になるのだが、VLAの配列内の要素数については、実行時に計算される。ところで、VLAの配列の要素数には任意の式を書ける。

void f( int a, int b, int c[a+b] ) {}
void g( int x, int b[abs(x)] ) {}
void h( int a[getchar()] ) { }

こうなると次にどういうコードが書かれるかはお察しの通りだ。

int main( int argc, char * argv[ printf("Hello,World")] ) { }

ところでC言語にはカンマ演算子がある。

int main( int argc, char * argv[(
    printf("Enter a character:"),
    getchar(),
    printf("bye"),
    1
)]){}

条件分岐は条件式で実現できる。

void f(int n) {
    if (n < 0)
        printf("negative!");
    else if (n > 0)
        printf("positive!");
    else
        printf("zero!");
}

というコードは、

void f(int n, char _[(
    (n < 0) ?
        printf("negative!")
    : (n > 0) ?
        printf("positive!")
    :
        printf("zero!")
    , 1
)]) {}

と書ける。

戻り値を返すことはポインター型の引数で表現できる。

void fswapf(float *a, float *b) {
    float tmp = *a;
    *a = *b;
    *b = tmp;
}

配列の要素数のなかに書けるのは式なのでfor文やwhile文を書くことはできない。しかし関数の再帰呼出しはできるのでループも表現できる。

/* the forward declaration is necessary */
static void sum_aux(float *out, float *v, size_t n, char *);
static void sum_aux(float *out, float *v, size_t n, char _[(
    (n > 0) ? (
        *out += *v,
        sum_aux(out, v + 1, n - 1, ""),
        1
    ) : 1
)]) {}

void sum(float *out, float *v, size_t n, char _[(
    *out = 0.0f,
    sum_aux(out, v, n, ""),
    1
)]) {}

VLAは狂っている。