もうかなり寒さも和らいできたかなと。
あと少しで、もう上着はいらないかと思ってますけどね。
だけどまだ朝は寒いですね。
これが和らいだら本当に春だと思いますよ。
そういえば2月4日は立春だったんですね。そのときやっと春が見えてきたというところですか。
学校の友人が、どうも担任が面倒を見ているらしいCの勉強をやっていた。
Cですか。まぁ妥当ですね。
それでこんな問題だった。
#include
int dec2bin(int n,char *bin);
int main(){
int n;
char *bin;
fputs("Input number >",stdout);
scanf("%d",&n);
dec2bin(n,bin);
printf("decimal : %d binary: %s\n",n,bin);
}
このプログラムを実行したとき、入力した数字を2進数に変換できるようにdec2bin関数を定義せよ。
まぁ実際はちょっと変数名などが下品に見えたのでおしゃれに書き換えてあります。
楽勝と思った人もいるかもしれない。
Perlなら楽勝ですよ。printfに%bとすれば簡単に2進数で表示できるんだから。
しかしCのprintfには残念ながらそんなフォーマットない。
だから自作しろと。
しかしCの文字列の処理というのはどうも慣れない。
引数で受け取ったchar型配列binの各要素に値をセットしていくと。
まぁとりあえず書いてみましょうか。
int dec2bin(int n,char *bin){
int i=0,end;
while(1){
bin[i]=(n%2)+'0';
n/=2;
if(n==0) break;
i++;
}
end=i;
bin[end+1]='\0';
}
原理は簡単でnを2で割った余りをとると0か1がでる。
0か1を0の文字コードに足すと0か1という文字の文字コードを指してくれると。
これで2で割ってを繰り返して、0になったら終了。最後に終端文字を入れて終わり。
さて、これを動かすとエラー出る。
実はこれはmain関数の作者のミスです。binの宣言をこうすればOKです。
char bin[1000];
何のことかわかりますかね?
binという名前のポインタ変数は用意してたんですけど、
ポインタの指す先を用意してなかったんですよ。
普通はこういうのはcalloc関数(stdlib.h)とかで動的にメモリを確保するときとかに、
ポインタ変数だけ用意するけど、そんなことはしていない。
随分マヌケなミスです。しかしありがちなのかな?
だから配列を作成して、実体も一緒に作っていただくと。
要素数1000は無駄に多いですが、これだけあればバッファオーバーランの心配はない?
本当はちゃんと対策しないとダメなんですけどね。
それで動かすと動きました。10を入力したら結果0101…
おかしいだろ、1010だろ、というのはごもっともな話。
2で割ったあまりを取ると、まず出てくるのが、もっとも下の位なんですよね。
それをすっかり忘れてた。
下の位から上の位に数字を並べたら読めないわな。
上の位から下の位にさっきの結果を並べ直すと1010だから正しいと。
というわけで文字列を反転させないと。
Rubyな人はString#reverse!をすれば終わりだと言うけど、そうも易しくはない。
for(i=0;i<(end+1)/2;i++){
char tmp;
tmp=bin[i]; bin[i]=bin[end-i]; bin[end-i]=tmp;
}
これをさっきの関数の最後に入れれば反転できると。
頭から1文字目と後ろから1文字目を入れ替え、頭から2文字目と後ろから2文字目を入れ替え…
とやっていって中央に着いたら終わり。まぁ有名な方法ですね。
めでたしめでたし。
まぁ.NET Frameworkではあんまりこういう風に一文字一文字いじることはないですよね。
文字列の反転すらchar[]を介さないとできないわけですからね。
まぁCには.NET Frameworkの世界のchar[]相当のものしかないと。
だからあらかじめ配列を確保しておかないといけない。
これがめんどくさい。これを忘れてはいけませんねということです。
ポインタ変数作っただけで実体作った気になってはいけないなぁと思いましたね。
ポインタ作っても実体ないからcallocかmallocで実体用意しろと。
それはC#とかでも一緒ですけどね。クラスはnewで実体作らないとだめだしね。
しかしこっちはインスタンス作らんとなぁと思うけどね。
この違いはcallocと仲良くなれてない証拠なのかな?