もうすっかり暖かくなりまして、近所の公園でもさくらが満開になっておりまする。
明解C言語中級が(とりあえず)終わって、今は前橋和弥さんのC言語ポインタ完全制覇という本を読んでいます。日記のネタ勉強になることがたくさん書いてあって、2章まで読み終わったので、とりまとめと言いますか、自分メモ用に残しておこうかなと。
問題集ではないので、自分でまとめないと意味がないっすからね。
かといって、ノートとかにまとめると後から自分で読んでも訳が分からんものになっちゃうんで、それよりも人の目に触れるインターネット上に残す方が自分にとってもいいっていう不思議な世の中になったもんだ。
コンパイルのオプション
いきなりタイトルコールに入ってぶっとんでしまいそうだが、ここから本題です。
以前かぼちゃさんがプログラムをコンパイルしているときに-Wallというオプションをつけていたのを覚えていたのだが、最適化っていうのをやってくれるってものだと思っていたのですが、-Wallってのはいろいろあるオプションをすべて有効にしてコンパイルするっていうオプションのようで、デバッグ中にはつけるような感じのもんらしい。
試しに以前作っていたプログラムに-Wallをつけてみると、使ってない変数を教えてくれた。
なんという世話焼き女房ですこと!いままで全く気にしてなかったけど、覚えておこう。
本書ではコンパイルの警告レベルは上げようという事でした。
変数のアドレス、関数のアドレス
ポインタは変数のアドレスの最初のやつってことですが(あいまいだな(笑))、関数でもなんでもメモリはアドレスで管理しているのだ。ってことで、いろんなアドレスを比べてみるというもの。
仮想アドレス空間の説明を見たときに、今までの何かが色々と紐づいてほお~っと思った瞬間でもあります。
KVMでVMを作ったときとか、いろいろなアプリケーションを動かしているときとか、しっかりとした理論が分かった訳ではないけど、仮想アドレス空間に物理メモリを割り当てるという言葉はなかなか重要なキーワードではないかと思います。
ちなみにアドレスの並びは書籍と同じようになりました。
&func1関数 0x400564 &func2関数 0x40059e &printf関数 0x400438 string literal.. 0x40082e &global_variable.. 0x600ba0 &file_static_variable.. 0x600b98 &func1_static_variable.. 0x600b9c malloc address.. 0x126c010 calloc address.. 0x126c030 &func1_variable.. 0x7fff2e475bfc &func2_variable.. 0x7fff2e475bfc
callocやprintfは自分で付け足したもんですが、これも当然アドレスがあります。
この並びがあとあと重要なものになってまいるのです。
仕組みはちゃんとは分からないけど、このまま深く突っ込んでいくと、言葉でしか出てこなかったシステムコールとかDLLとかを利用する側の事が出てくるんだと思う。お~。
んで、表の中のアドレスをよく見ると0x7fff2e475bfcを使っているのが2個あるぞ!となりますが、これに関しては、「へぇ、あんたも0x7fff2e475bfcっていうんだ」ってな感じで某マンガのように二人が出会う事はありません。
二つはfunc1内とfunc2内で宣言されているもので、そこだけの変数なのです。うまく説明できてない気がするので、分かりづらい人はスコープと変数とかのキーワードでぐぐってください。
malloc()はシステムコール??
そう思ってる訳ではなくて、本の中でのタイトルがこうでした。でも意味は分かるよ。
ではLinuxにはstraceというコマンドがあるので、実際に見てみようのコーナー。
mallocを使った簡単なプログラムを用意する。
#include <stdio.h> #include <stdlib.h> int main(void) { int i; int *num; if((num = malloc(1024)) == NULL){ fprintf(stderr, "miss!\n"); } for(i = 0; i < 10; i++){ num[i] = i; printf("%d", num[i]); } putchar('\n'); free(num); return 0; }
straceする。
$ strace ./a.out execve("./a.out", ["./a.out"], [/* 21 vars */]) = 0 brk(0) = 0x257b000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f19e6e88000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=23484, ...}) = 0 mmap(NULL, 23484, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f19e6e82000 close(3) = 0 open("/lib64/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\356\1\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1921216, ...}) = 0 mmap(NULL, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f19e68d6000 mprotect(0x7f19e6a61000, 2093056, PROT_NONE) = 0 mmap(0x7f19e6c60000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x7f19e6c60000 mmap(0x7f19e6c65000, 18696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f19e6c65000 close(3) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f19e6e81000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f19e6e80000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f19e6e7f000 arch_prctl(ARCH_SET_FS, 0x7f19e6e80700) = 0 mprotect(0x7f19e6c60000, 16384, PROT_READ) = 0 mprotect(0x7f19e6e89000, 4096, PROT_READ) = 0 munmap(0x7f19e6e82000, 23484) = 0 brk(0) = 0x257b000 brk(0x259c000) = 0x259c000 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f19e6e87000 write(1, "0123456789\n", 110123456789 ) = 11 exit_group(0) = ?
本書に書いてるbrkも出てきていますね。何してるんだかよく分かりませんけど。
ところどころにでくるmmapってのが、さっきも言ってた仮想アドレスに関するシステムコールらしいです。mprotectはメモリ保護かな?
今まではstraceなんて身近じゃなかったけど、Cをやると今までよりかは身近に感じた。
Cはインフラーを育てるプログラミング言語なのだ。
インフラーって何だって?昔はそうなりたいという人や物の最後にラーをつけて表現していたので、それをパクってみました。
と、ちょっとネタに走ってしまいましたが、まあいいでしょ。
まあPHPでもJAVAでもいいと思いますけどね。何を使おうが、実際にやるのが一番なんです。
いまでしょって流行ってますし。
毎日徹夜してるエンジニアの人は、どこで寝るの?居間でしょ!ってネタを持っているらしい。
知らないけど(笑)
ちょっと話が逸れてしまったけど、さっきstraceをしたときにシステムコールがずらずらと出てきましたが、それを直接実行することはできるのかなぁなんて思ったけど、できるのかしらね。
ではまた。
http://foxism.net/?p=717
システムコール(write)を使ってprintfの代用をしてみました(Linux用)
writeの第一引数を1にしていますが,これをsocket()で得られるファイルディスクリプタに置き換えると,通信も可能です.
要するに,引数の少ないsend()として使えますが,sockopt()あたりであとづけ設定出来たような気がします.
mmap系は使えますが,実用的ではないかと思います….
man mmap参照
コメントありがとう(^^)
http://foxism.net/?p=717 拝見させていただきました。