Sim's blog

電子工作はじめてみました

Microsoft Extension

2007-09-18 23:25:29 | その他
ICFPの方もいよいよ画像の修復に取りかかっていますが、なかなか進みません。

C言語の標準はISOとかJISで決まっています。標準ではないのですが使ってて便利だと思った方言というか、Microsoft独自仕様をいくつか紹介します。

(1) 名前なしのunion(struct)メンバー

正式にはなんというのか知りません。普通unionのメンバーには必ず名前をつけます。
union {
    struct {
        char a, b;
    } x;
    int y;
} u;

例えば、これだとメンバーはu.x.a、u.x.b、u.yの3つです。名前なしの場合はstructのxを省略できます。
union {
    struct {
        char a, b;
    };
    int y;
} u;

この場合、u.a、u.b、u.yのようにアクセスできます。.xを省略できるわけです。structでも、あいまいにならない限りは同じことができます。

あくまで方言なので、あまり使っていいものではないと思いますが、ついつい使ってしまいます。どういう時に使うかというと、最初の3つはメンバーが同じだけど、残りが違うみたいなときに使います。
struct header { ... };

struct type1 {
    struct header;
    残り1
};

struct type2 {
    struct header;
    残り2
};

みたいな感じです。

(2) 長さ0の配列

Cでは長さ0の配列は許されないことになっています。int a[0]みたいなのです。
これは、可変長データを作るときに使っています。
typedef struct {
    int size;
    char s[0];
} header;

例えば、長さの後にcharが可変長続くみたいなデータのヘッダです。データを格納する領域自体はmallocで取りますが、ポインタを一回経由しなくていいので便利です。
メモリ確保はheader *p = (header *)malloc(sizeof(header) + サイズ);みたいなかんじです。データへのアクセスはp->sizeとp->sみたいな感じになります。
javaだと最初からあります。byte a = new byte[サイズ]とすれば、a.lengthでサイズが取れるサイズ可変配列になります。
長さ0配列を使わないとすると
typedef struct {
    int size;
    char *s;
} header;

のように配列側をポインタにします。この場合、ヘッダとデータはメモリとして連続しないことになります。

長さ0にこだわらなくても、とりあえず1ということにしておくのもありですが、実際に長さが0のときに無駄なメモリが必要なことになります。

(3) SEH

これはwindowsの構造化例外ハンドラという仕組みです。__try節の後に必ず__finally節が実行されることが保証されています(たぶん)。リソースの確実な解放を行うために使っています。
FILE *fp = NULL;
char *p = NULL;

__try {
    fp = fopen(ほにゃ);
    if(fp == NULL) __leave;

    p = malloc(うにゃ);
    if(p == NULL) __leave;
}
__finally {
    if(fp != NULL) fclose(fp);
    if(p != NULL) free(p);
}

上の例だと__try節の中でファイルとメモリを確保して、__finally節で解放しています。__try節から脱出するときは必ず__finally節を実行します。例えばreturnしても__finally節は実行されます。
また、普通のCコードだとmallocに失敗したときはfcloseしてからreturnするとかしないといけませんが、エラー処理というかif ~ returnが出てくるたびにリソース解放するのは面倒です。
    if(p != NULL){
        fclose(fp);
        return;
    }

一応、gotoを使えば同じようなことができます。

というわけで、悪の道は甘い蜜の味ということで、ついつい使ってしまう方言を紹介してみました。可変配列については、C99だとmallocを使わなくても作れるということみたいですが、手持ちのコンパイラ(VC++6.0)はもちろん対応していません。