next up previous contents index
: 32 bit 整数の内部表現 : 数の内部表現 : 数の内部表現   目次   索引

peek と poke

関数 peek(D) を用いると, アドレス D に格納された 1 バイト (16 進 2 桁, 2 進 8 桁) のデータを覗く(peek)ことができる. 関数 poke(D,N) を用いると, アドレス D に データ N を直接書き込む事が可能である. ここで, N$0$ から $2^8-1 = 254 = {\tt0xff}$ の範囲のデータ である.

通常のプログラムでこのような関数を利用する必要はないが, 計算機プログラミングの達人になるには これらの関数を援用して, メモリの様子が手にとるように理解できるようになるまで 頑張る必要があろう.

なお, poke であやまった場所にデータを書き込んでしまうと, システム全体が異常な動作におちいるので注意. どうしてそのような事態になるか説明できるだろうか?

例 10.1   文字列はその文字のアスキーコードがそのままメモリーに 格納されている. このことを実例で確かめてみよう.
[469] X="abc";
abc
[470] D=get_addr(X);
139968704
[471] hex_dump(D,10);
0857c0c0: 07000000 98aa5308 0000
0
[472] type(X);
7
[473] hex_dump(0x0853aa98,10);
0853aa98: 61626300 00000000 656e
0
    
  1. 変数 X に 文字列 abc を代入.
  2. 変数 X の実体が存在するアドレスを get_addr で取り出し, 変数 D にいれる.
  3. D から 10 バイト分の領域に何があるか hex_dump で調べて みる.
  4. 07 00 00 00 07 は文字列を表すタグ(印).
  5. 98 aa 53 08 は アドレス 08 53 aa 98 (後ろから読む) を意味する. ここにデータの実体が存在. Asir では0x ではじまる数字および a から f の列は 16 進数を 表す.
  6. アドレス 08 53 aa 98 から 10 バイト分の領域をしらべると, 61 62 63 00 なるデータが存在しており, 順番に a, b, c のアスキーコードである.

上の例では, hex_dump の出力 98 aa 53 08 より アドレス 08 53 aa 98 (後ろから読む) を読みとり, 手動でアドレスを入力する必要があった. 次の関数 get_body_addr はこのアドレスを一気に求める.

def get_body_addr(X) {
  A = get_addr(X);
  A3=peek(A+7);
  A2=peek(A+6);
  A1=peek(A+5);
  A0=peek(A+4);
  return(ishift(A3,-24)+ishift(A2,-16)+ishift(A1,-8)+A0);
}

最後の ishift(A3,-24)+ishift(A2,-16)+ishift(A1,-8)+A0
A3*0x1000000+A2*0x10000+A1*0x100+A0 としてもよい. これはたとえば 2進数を $-8$ bit 左にシフトすることと, その数を $2^8$ 倍 ( $2^8 = 0x100 = 256$) するのは同じことなので 明らかであろう.

例 10.2   get_body_addr を用いて, メモリに格納された文字列を直接書き換えよう.
[469] X="abc";
abc
[470] D=get_addr(X);
139968704
[471] hex_dump(D,10);
0857c0c0: 07000000 98aa5308 0000
0
[473] get_body_addr(X);
139700888
[474] poke(get_body_addr(X),0x41);
0
[475] X;
Abc
    
  1. 変数 X に 文字列 abc を代入.
  2. 変数 X の実体が存在するアドレスを get_addr で取り出し, 変数 D にいれる.
  3. D から 10 バイト分の領域に何があるか hex_dump で調べて みる.
  4. アドレス 08 53 aa 98 を 10 進数になおすと, 139700888.
  5. poke コマンドで, このアドレスに直接 0x41 (A のアスキーコード) を書き込む.
  6. X の値は Abc にかわった.

アドレス get_addr(X) より 始まるメモリ領域の最初のバイトは X に格納されているデータの 型番号である. この数は, 関数 type(X) の戻す値と一致している. たとえば次の例をみてみよう.

[496] X=10;
10
[497] type(X);
1
[498] hex_dump(get_addr(X),4)$
085843b0: 01000001 
[499] X=x^2-1; 
x^2-1
[500] type(X);
2
[501] hex_dump(get_addr(X),4)$
085840c0: 02000000

C 等の一般的な言語での整数は, ``32 bit 整数'' とよばれ, サイズが 32 bit である. 一番上位の bit を符号としてもちいるので, 扱うことのできる最大の正の整数は $2^{31}-1=2147483647$ となる. C でこの数に $1$ を加えると負の数となる.

Asir が標準的に用いている整数は , ``32 bit 整数'' でない. いわゆる bignum である. 数が大きくなっていったら動的に数のデータを格納するメモリの領域を広げて 計算をおこなう. したがって, 数の大きさの上限は, 計算機のメモリのサイズに依存するのみである. この様子を, 次のプログラムで眺めてみよう.

def naibu() {
  X = 2^16;
  for (I=0; I<4; I++) {
    print("X=",0); print(X);
    A = get_body_addr(X);
    print("address=",0); print(A);
    hex_dump(A,32);
    X = X*X;
  }
}

    
  1. X には, 順に $2^{16}$, $2^{32}$, $2^{64}$, $2^{128}$ がはいる.
  2. アドレス A より 32 バイトのメモリを見る.

[532] naibu()$
X=65536
address=140075328
08596140: 01000000 00000100 
          00000000 00000000
08596150: 01000000 00010000 
          00000000 00000000

X=4294967296
address=140075040
08596020: 02000000 00000000 
          01000000 00000000
08596030: 02000000 40806302 
          01000000 00000000
    
  1. 00 00 01 00 を逆順にならべると 00 01 00 00 となり, これを 16 進数とみなすと, $16^4 = 2^{16} = 65536$ となる.
  2. 00 00 00 00 01 00 00 00 を逆順にならべると 00 00 00 01 00 00 00 00 となり, これを 16 進数とみなすと, $16^{8} = 2^{32} = 4294967296$ となる.
以下同様である. address の値も時時刻刻と変化しており, 計算の結果が新しいメモリ領域に 格納されている様子もわかるであろう.

X=18446744073709551616
address=139038512
08498f30: 03000000 00000000 
          00000000 01000000
08498f40: 00000000 00000000 
          03000000 00bf9100

X=340282366920938463463374607431768211456
address=140029728
0858af20: 05000000 00000000 
          00000000 00000000
0858af30: 00000000 01000000 
          00000000 00000000
[533]
    
    

問題 10.1 (10)   peek および poke を用いて, あたえられた文字列の中のアルファベット 小文字をすべて大文字に変換するプログラムを書きなさい.

問題 10.2 (20)   整数がどのようにメモリに格納されているか解析して, 変数に格納された整数を 1 増やすプログラムを peek, poke を用いて書きなさい.


next up previous contents index
: 32 bit 整数の内部表現 : 数の内部表現 : 数の内部表現   目次   索引
Nobuki Takayama 平成15年9月12日