バリケンのRuby日記 RSSフィード

2007-10-09

[] バイナリアンC言語入門(2) - 整数の配列で関数を作ってみる  バイナリアン的C言語入門(2) - 整数の配列で関数を作ってみる - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  バイナリアン的C言語入門(2) - 整数の配列で関数を作ってみる - バリケンのRuby日記  バイナリアン的C言語入門(2) - 整数の配列で関数を作ってみる - バリケンのRuby日記 のブックマークコメント

まずはふつうに「与えられたメモリアドレスの先にある整数値を1増やす」succ関数を作ってみるよ。

int succ(int *a) {
  *a += 1;
  return 0;
}

これを使ったプログラムテキストエディタで書いて、succ.cとして保存するよ。

#include <stdio.h>

int succ(int *a) {
  *a += 1;
  return 0;
}

int main(void) {
  int num = 1;
  printf("%d\n", num);
  succ(&num);
  printf("%d\n", num);
  return 0;
}

コンパイルして実行するよ。

$ vi succ.c
$ cat succ.c
#include <stdio.h>

int succ(int *a) {
  *a += 1;
  return 0;
}

int main(void) {
  int num = 1;
  printf("%d\n", num);
  succ(&num);
  printf("%d\n", num);
  return 0;
}
$ gcc succ.c
$ ./a.out
1
2
$

ちゃんとnumが1増えているのがわかるよね。

次は、この実行ファイルをobjdumpコマンドを使ってsucc関数の部分が機械語にどのように変換されているかを見てみるよ。

$ objdump -D a.out | grep '<succ>' -A 11 -m 1
08048384 <succ>:
 8048384:       55                      push   %ebp
 8048385:       89 e5                   mov    %esp,%ebp
 8048387:       8b 55 08                mov    0x8(%ebp),%edx
 804838a:       8b 45 08                mov    0x8(%ebp),%eax
 804838d:       8b 00                   mov    (%eax),%eax
 804838f:       40                      inc    %eax
 8048390:       89 02                   mov    %eax,(%edx)
 8048392:       b8 00 00 00 00          mov    $0x0,%eax
 8048397:       5d                      pop    %ebp
 8048398:       c3                      ret

$

じゃあ、この機械語整数表現に変換してみるよ。このあいだのようにirbを使って調べればいいよね。

$ irb
irb(main):001:0> 0x8be58955 - 0xffffffff - 1
=> -1947891371
irb(main):002:0> 0x458b0855
=> 1166739541
irb(main):003:0> 0x40008b08
=> 1073777416
irb(main):004:0> 0x00b80289
=> 12059273
irb(main):005:0> 0x5d000000
=> 1560281088
irb(main):006:0> 0x000000c3
=> 195
irb(main):007:0> exit
$

これを応用して作った次のプログラムテキストエディタで書いて、succ2.cとして保存するよ。

#include <stdio.h>

int main(void) {
  int num = 1;
  int succ_bin[] = {
    -1947891371, 1166739541, 1073777416, 12059273, 1560281088, 195
  };
  int (*succ)();

  printf("%d\n", num);
  succ = &succ_bin;
  (*succ)(&num);
  printf("%d\n", num);

  return 0;
}

実行結果だよ。

$ vi succ2.c
$ cat succ2.c
#include <stdio.h>

int main(void) {
  int num = 1;
  int succ_bin[] = {
    -1947891371, 1166739541, 1073777416, 12059273, 1560281088, 195
  };
  int (*succ)();

  printf("%d\n", num);
  succ = &succ_bin;
  (*succ)(&num);
  printf("%d\n", num);

  return 0;
}
$ gcc succ2.c
succ2.c: 関数 `main' 内:
succ2.c:11: 警告: 互換性のないポインタ型からの代入です
$ ./a.out
1
2
$

うーん、C言語ならこんなこともできちゃうんだねえ。

追記:関数へのポインタ「int (*succ)();」を使って関数を呼び出すときは、わざわざ「(*succ)(&num);」と書かなくても本当の関数呼び出し同様に「succ(&num);」と書いてもいいんだね。

$ vi succ2.c
$ cat succ2.c
#include <stdio.h>

int main(void) {
  int num = 1;
  int succ_bin[] = {
    -1947891371, 1166739541, 1073777416, 12059273, 1560281088, 195
  };
  int (*succ)();

  printf("%d\n", num);
  succ = &succ_bin;
  succ(&num);
  printf("%d\n", num);

  return 0;
}
$ gcc succ2.c
succ2.c: 関数 `main' 内:
succ2.c:11: 警告: 互換性のないポインタ型からの代入です
$ ./a.out
1
2
$
トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20071009