日記帳だ! with Tux on Libserver

二度目の大改造!! 日記帳…か?を継承し、より柔軟でパワフルなBlogに変身しました。

RSSに対応しています。リンク・コメント・トラックバックは自由にしていただいてほぼ問題ありません。
RSS購読方法、僕のリンク・コメント・トラックバックについての考えを読むことをおすすめします。

ゾンビが怖いから、マクロで戦うことにした

Cの問題は、メモリ管理を自分でやらなくてはいけないこと。
C++の有名なライブラリ、STLでは、関数を出るとき、vectorなどの実体は破棄される。
これはありがたいことですね。
Cにもそんな便利な機能が欲しいねぇ。C++のつまみ食いをするのも1つありだけど…
まぁしかし、メモリ管理をするといっても、確保した空間は確かに破棄しろと。それだけです。
これを忘れると、ゾンビこと永遠に参照されない空間が発生すると。
何度も呼び出される関数でこれができるとえらいことです。
これを考えなくてもいいように、.NET FrameworkにはGCが付いていて、これで自動的に解放してくれると。
ああありがたい。というわけでC#を使えば問題はないですが、それでは話がずれてしまうので、Cを使うことだけ考えましょう。
Cの便利な機能に#defineがあります。
というけど、正直言うと、不気味な考え方ですけどね。
普通の使い方。
#define SIZE 100
こう書くと、プログラム中の文字列としてのSIZE以外のSIZEが全部100に置換されます。
まぁリテラルを何回も書かなくても、コンパイラが書いてくれると言うことです。
この考え方を拡張したマクロというのがある。
#define SQU(x) ((x)*(x))
SQU( foo() )と書くと、((foo()*(foo()))に置換される。
この無駄に多い括弧は演算子の結合順序のこととか考えると、引数は(x)のように書いて、答えも全部()で囲うと。
この程度のことが必要であると。
しかもこの欠点は、foo()が2回実行されること。もし結果が変わったらどうする。
だからこういうのは本当は作ってはいけません。
ただ関数を呼び出すより軽いという話もあると。けどもはや時代遅れの意見のような…
まぁあくまでもコンパイラが置換してくれるだけ、そう考えましょう。
今回はこのマクロを利用したお話。
さて、今回参考にしたのはC#のこんな構文。
using(System.IO.StreamReader mysr=new System.IO.StreamReader("test.txt")){
//mysrが使える
}
//mysrは使えない。
これはGC対象外のファイル関係のクラスのインスタンスを確実に破棄するのに使われる。
これによって非常に使いやすい。なのでusingを使うことを僕は推奨しています。
このusingをまねてみようとおもった。
#define USINGARRAY(array,size,BLOCK) \
do{\
void* __ref = calloc(sizeof(*(array)),size);\
if( __ref == NULL) abort();\
(array)=__ref; \
do{\
BLOCK\
}while(0);\
free(__ref);\
}while(0)
#defineは本来一行で終わりますが、\を付ければ次の行に続けられます。
全体をdo{~}while(0)で囲んでいますが、これはそういうものです。さっきの()と同じような感じです。
最後に;はありませんが、マクロを使う方が書いてくれると思うのでいりません。
BLOCKの中に実際に実行する文が入るのですが、なんか知らんけどうまいこと行きます。
けど、int a,b;のようなのをブロック内に書くとだめですけどね。カンマは嫌いらしい。
同様にファイルのやつも作ってみた。
#define USINGFILE(fp,fname,fmode,BLOCK) \
do{\
FILE* __fp=fopen((fname),(fmode));\
if(__fp==NULL) abort();\
(fp)=__fp;\
do{\
BLOCK\
}while(0);\
fclose(fp);\
}while(0)
まぁこんな風に、始まりと終わりをしっかり対応させる。
そういうことを強制させるわけです。
ただし、非常に扱いが悪い。まともにデバッグできないよ。
さて、これを使ったプログラム。
main(){
int* myarr;
int size;
FILE* fp;
int i;
double average=0.0,variance=0.0;
USINGFILE(fp,"test.txt","r",
if( fscanf(fp,"%d",&size)!=1 ) abort();
USINGARRAY(myarr,size,
for(i=0;i<size;i++){
if(fscanf(fp,"%d",&myarr[i])!=1) abort();
}
for(i=0;i<size;i++) average+=myarr[i];
average/=size;
for(i=0;i<size;i++) variance+=SQU(myarr[i]-average);
variance/=(size-1);
);
);
printf("%f / %f",average,sqrt(variance));
return 0;
}
1行ずつ読み込みたいのですが、下品なのでこういうfscanfの使い方はやめましょう。
とはいうけど、ちゃんと読み込めてるか、読み込んだ個数をチェックしてるから、
fscanfのバッファにゾンビが残ったりしないのでまだ安心です。優・良・可・不可で言えば良ぐらいのもんです。
fgetsで読み込んで、sscanfする方がいいです。これだと1行に何個値が書いてあってもいいことになるので。
さて、最後の引数がすごく長いので気持ち悪いですが、きちんと始まり、終わりの関係が見えますね。
これも万能ではないですよ。配列の配列なんかはこれでは処理できない。
まぁ何が言いたかったかというと、確保と破棄は一対一対応しなければいけない。
それができないならば、そのプログラムはちょっと危険だと。そう思いますよ。
Author : Hidemaro
Date : 2008/09/05(Fri) 23:13
コンピュータ・インターネット | Comment | trackback (0)

Tools