えっと情報系の学部に入るとなにやらC言語というプログラム言語をいじらされることがあるかと思います。
そんで、なんかccなりgccなりとかをコマンドとしてうたされて、「a.outってやるといいよ。」みたいなことをいろんな人から言われ、そんなもんなのかーって思ってよくわかんねーなって、感じになってしまい、プログラム怖いみたいなことになってしまうこともあるように思います。
なので、映画とかなんとなくの情報で、コンピュータって0と1だけで動くんだぜ、みたいな知識はあるのに、
C言語は0と1じゃないし、よくわからんって人のためにコンピュータって0と1だけなんだなぁということをわかってもらうための記事を書きます。
たとえば、C言語でこんなプログラムを書いたとします。
#include<stdio.h>
void main(){
int b;
b=10
printf("%d",b);
}
これは「人間が理解できるように」「人間がプログラムを書きやすいように。」ということを目的に作られたプログラム言語ってやつなんですが、
これを「コンパイル」(ぷよぷよの会社じゃないよ)という動作で「機械がよめる01のデータ」に直します。
人間が読めなくなってしまったデータはバイナリといいます。
コロスケがコロッケが2個から4個に増えて喜んだときにも「バイナリ~♪」といいますが
それとは関係なく「2進数の」という意味です。
これを16進数に置き換えて中身をみてみると
00 B8 CC CC CC CC F3 AB C7 45 FC 0A 00 00
00 C7 45 F8 14 00 00 00 8B 45 FC 03 45 F8・・・・・・
こんなかんじになっています。
まぁ、なんだかわかりませんよねw
なんだかわからないけど、もしこの中身を適当に書き換えちゃうと機械が読めなくなるので動作しなくなっちゃうわけです。
「機械が読めるように書き換えたら?」
そうしたら、C言語という人間がわかる言語を介さなくてもコンピュータに計算を動作させることができるようになるわけです。
というわけでやってみましょう。
void main(){
int a;
int b;
a=10;
b=20;
b=a+b;
b=a-b;//ココを
printf("%d",b);
}
このようなプログラムを、
void main(){
int a;
int b;
a=10;
b=20;
b=a+b;
b=a+b;//こうする
printf("%d",b);
}
このようにしたいと思ったときに、バイナリを書き換えて変更することを考えて見ましょう。
自力で、main関数があるところを探してもいいのですが面倒なので、
目印を入れておきましょう。
void main(){
int a;
int b;
a=10;
b=20;
b=a+b;
b=a-b;
//ここに目印を入れておく。
__asm{
nop;
nop;
nop;
nop;
nop;
nop;
}
//何もしない命令をいっぱい入れておく
printf("%d",b);
}
__asmから始まるなんかわからない部分が追加されてますが、
気にしないでください・・・・
とはいえ、気になるので説明します。
僕は、プログラムを書くときに「これはおまじない」といわれるのが嫌いなんです。
学校の先生が「おまじないだよ。」というのを耳にしたら、それは半分は職務怠慢だと思ってください。
CPUというコンピュータの内部で計算をしている一番えらい(熱い)機械は、01の塊になったプログラムを渡されるとそれを数個ずつ塊で持ってきて、塊ごとにきまった動作をしてくれる熱いやつです。
この決まった動作ごとに割り当てられた命令を機械語といいます。
機械語にはニーモニックという名前が付いていて、それを「アセンブリ」といいます。(ちょっと違うかもしれないけどこんなかんじ。)
C言語はそれよりももっと複雑に人間にわかるような書き方に変えているので「高級言語」といいます。
__asm{}というブロックの中にはこのアセンブリ言語をそのままかいていいですよ。というふうにコンパイラにお願いする部分です。
そのなかにnopという「何もしないでね」という命令を書き込んでおいたわけです。
nopというニーモニックは、機械語としては16進数で90というのに書き換えられます。これはCPUごとに違うのですが今回の場合はそうでした。
ということは__asmの中身はコンパイルすると
nop :90
nop :90
nop :90
nop :90
nop :90
nop :90
と展開されるということです。
と言うことは・・・・コンパイルされた実行ファイルの中にも
90 90 90 90 90 90という列が存在するはず。
そしてそのあたりがよくわからんけど、
実行ファイルの中のmain関数の最後あたりなんだろーなぁー。
というあたりがつきます。
なので、
バイナリエディタから検索してその位置を確認します。
そして、この90 90 90 90 90 90の直前に
b=b-aが
機械語として展開されているはず!!
そしてその動作がどのようになるかというと、
1.レジスタにbを入れる
2.レジスタとaを引き算する
3.bにレジスタを写す。
こんな感じになります。
いきなり出てきたけど、レジスタってなんだ!!バカ野郎!いい加減にしやがれ!このFXXXやろうが!
と思われるかもしれないので、説明すると
普通プログラムと言うのは、最初作られたときには
ハードディスクという機械の中に入っています。
これは大容量ですが、ちょっとよく使うデータにとっては遅いです。
これを実行するときにメモリという機械にこのプログラムを入れておきます。
メモリは比較的早く読み書きできるので便利です。
ところがCPUっていう熱い機械は、メモリのデータを直接計算することができないのです。
そのかわりレジスタというメモリよりももっと早く読み書きできてしかも計算できるところをちょっとだけ持ってます。
なので、計算するときは一度レジスタに入れてから計算します。
そしてそれぞれが、16進数で2桁(この単位をバイトといいます)×3ずつの命令の長さを持っていて、
1.(レジスタに入れろ)[Bを]
2.(レジスタと引き算しろ)[Aを]
3.(レジスタをメモリに戻せ)[Bに]。
という感じになっていて、
(**)[** **](sub)[ ** **]
(**)[** **]90 90 90 90 90 90
こんな風に分けられているので、
このsubという引き算の部分をadd(03)に書き換えて足し算にかえてしまえば
いいということがわかります。
%assembly1 -20
%assembly2 40
わお。
ちゃんと書き換えられてますね。
やっぱりコンピュータって01で動いているんだなぁということが
わかりました。
それじゃ、また。
« Javascriptでカプセル化を実現する! – FizzBuzzはもう古い!世界のナベアツ問題。 »
No comments yet.