C99

以前コメントでもらったけど、現在の標準の C というのは、ANSI C ではなく C99(ISO/IEC 9899:1999)のことのようだ。入門書(「独習 C」)もひと通り読み終わったことだし、ここで C99 の新機能について主なところをまとめておこう。
参考にしたのはこのページ。

 cf. C99の仕様 – C言語最新事情

bool型

真偽値を表すブール型が追加された。標準では型名が _Bool となっているけど、stdbool.h を読み込むことで bool という型名と、値として true、false が使えるようになる。

#include
#include

bool isoctal(char c)
{
    return '0' <= c && c < '8';
}

int main(void)
{
    char c = '3';
    if (isoctal(c)) {
        printf("%c is octal.\n", c);
    }
    return 0;
}
takatoh@nightschool $ ./bool
3 is octal.

可変長配列

可変長といっても自由に長さを変えられるのではなくて、実行時に配列の長さを指定できるようになった。

#include

size_t fsize3(int n)
{
    char b[n + 3];

    return sizeof b;
}

int main(void)
{
    printf("%ld\n", fsize3(10));

    return 0;
}
takatoh@nightschool $ ./array
13

C++コメント

// 以降行末までがコメントとして扱われるようになった。

関数名マクロ

__func__ というマクロが追加された。これは現在の関数の名前を表す。

#include

void myfunc(void)
{
    printf("%s\n", __func__);
}

int main(void)
{
    myfunc();

    return 0;
}
takatoh@nightschool $ ./mfunc
myfunc

指示初期化子

指示初期化子を使うと、配列の特定の添字、構造体の特定のメンバを初期化できる。
まずは配列の場合:

#include

void PrintArray(int ary[]);

int main(void)
{
    int a[] = {
        [9] = 1
    };

    PrintArray(a);

    return 0;
}

void PrintArray(int *ary)
{
    int i;

    for (i = 0; i < 10; i++) {
        printf("%d ", ary[i]);
    }
    printf("\n");
}
takatoh@nightschool $ ./init1
0 0 0 0 0 0 0 0 0 1

上の例では、配列の添字9の要素を 1 に初期化している。この場合配列は次のように初期化される。

  • 配列のサイズが未知の場合、指示初期化子で指定された最大のものをもとにサイズが決定される。
  • 指示初期化子で指定されなかった添字の領域は、int型では 0 に初期化され、ポインタではヌルポインタに初期化される。

指示初期化子は複数指定することも可能。また、通常の初期化子と混ぜて使うことも可能で、通常の初期化子は指示初期化子の添字に続くものとして解釈される。

#include

void PrintArray(int ary[]);

int main(void)
{
    int a[] = {
        [3] = 1,
        2,
        [8] = 3,
        4
    };

    PrintArray(a);

    return 0;
}

void PrintArray(int *ary)
{
    int i;

    for (i = 0; i < 10; i++) {
        printf("%d ", ary[i]);
    }
    printf("\n");
}
takatoh@nightschool $ ./init2
0 0 0 1 2 0 0 0 3 4

構造体の指示初期化子は「.メンバー」。

#include

typedef struct {
    int x;
    int y;
    int width;
    int height;
} Rectangle;

int main(void)
{
    Rectangle r = { .x = 1 };

    printf("x = %d\n", r.x);
    printf("y = %d\n", r.y);
    printf("widht = %d\n", r.width);
    printf("height = %d\n", r.height);

    return 0;
}
takatoh@nightschool $ ./init3
x = 1
y = 0
widht = 0
height = 0

変数宣言

ANSI C では変数の宣言はブロックの先頭で行う必要があったけど、C99 ではブロックの途中でも宣言できるようになった。また、for 文に限ってループ変数の宣言が可能になった。このループ変数は for ブロックの中だけで有効。

#include
#include

int sum(int count, int a[])
{
    assert(count >= 0);

    int sum = 0;
    // Error in ANSI-C
    for (int i = 0; i < count; i++) {
        // Error in ANSI-C
        sum += a[i];
    }
    return sum;
}

int main(void)
{
    int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    printf("sum = %d\n", sum(sizeof(data) / sizeof(data[0]), data));

    return 0;
}
takatoh@nightschool $ ./localvar
sum = 45

複合リテラル

構造体や配列(ただし可変長配列を除く)をリテラルとして生成できるようになった。

#include

typedef struct {
    int x;
    int y;
} Point;

Point Center(const Point *p1, const Point *p2)
{
    return (Point) {
        .x = (p1->x + p2->x) / 2,
        .y = (p1->y + p2->y) / 2
    };
}

int main(void)
{
    Point p = Center(&(Point){1, 1}, &(Point){3, 3});
    printf("(%d, %d)\n", p.x, p.y);

    return 0;
}
takatoh@nightschool $ ./compound_literal
(2, 2)

上の例では、Center 関数の呼び出し時と、Center 関数が値を返すときに構造体の複合リテラルを使っている。
……ところで複合ってどういう意味だろう?

さて、こんなところだろうか。