メモリ内容出力+stringのメモリ表現
SBCLでマシンコードを直接実行で作成したstring-to-alien-functionマクロを使って少し遊んでみる。
SBCL: sbcl-1.0.28-x86-linux-binary.tar.bz2
まずは、指定されたメモリの内容を出力する関数を定義。
※ もともとは、文字列を出力する関数のつもりだったが、機能的には上の方が適切なので変更。
; 元となるマシンコード ; 左から順に、オフセット、機械語、アセンブリ言語 ;0000 55 pushl %ebp ;0001 89E5 movl %esp, %ebp ;0003 8B4D08 movl 8(%ebp), %ecx ;0006 53 pushl %ebx ;0007 8B550C movl 12(%ebp), %edx ;000a B8040000 movl $4,%eax # 4=出力用のシステムコール(?) ; 00 ;000f BB010000 movl $1,%ebx # 1=標準出力 ; 00 ;0014 CD80 int $0x80 ;0016 5B popl %ebx ;0017 5D popl %ebp ;0018 C3 ret ;; 関数定義 (let* ((code "5589E58B4D08538B550CB804000000BB01000000CD805B5DC3") (fn (string-to-alien-function code (function int int int)))) ;(function 返り値 アドレス 表示サイズ) (defun print-memory (#1=address-or-object size &optional (offset 0)) (unless (typep #1# 'fixnum) ; fixnumならメモリアドレスと仮定 (setf #1# (sb-kernel:get-lisp-obj-address #1#))) (sb-alien:alien-funcall fn (+ #1# offset) size)))
この関数に文字列(simple-string)を渡して、それがメモリ上でどのように表現されているのかを見てみる。
※ 下の出力はemacsによるもの。^@は、Cで云うNULL文字、lispなら(code-char 0)の文字に該当する。
> (print-memory "simple-string" 55) 出力: ^@s^@^@^@i^@^@^@m^@^@^@p^@^@^@l^@^@^@e^@^@^@-^@^@^@s^@^@^@t^@^@^@r^@^@^@i^@^@^@n^@^@^@g^@^@^@^@^@ ;; 少し変えてみる > (print-memory (string-downcase "SIMPLE-STRING") 55) ;; 結果は同じ 出力: ^@s^@^@^@i^@^@^@m^@^@^@p^@^@^@l^@^@^@e^@^@^@-^@^@^@s^@^@^@t^@^@^@r^@^@^@i^@^@^@n^@^@^@g^@^@^@^@^@
これを見ると、simple-stringは、1文字ごとに4byte割り当てているのが分かる。
sbclのchar-code-limitは#x11000なので、(各4byteの)2byte目以降の3つは文字の表現用に割り当てているのだろうが、先頭の1byteを何に使っているのかは不明。(cf. (print-memory (coerce "simple-string" 'simple-vector) 55))
それにしても、一つの文字に一律に4byteを割り当てるのは、少しもったいないような気がする。
また下のように、文字列の長さが文字列のアドレスの直前に格納されていた。
;; 4byte分、前のアドレスも表示 > (print-memory "simple-string" 55 -4) 出力: ^@4^@^@^@s^@^@^@i^@^@^@m^@^@^@p^@^@^@l^@^@^@e^@^@^@-^@^@^@s^@^@^@t^@^@^@r^@^@^@i^@^@^@n^@^@^@g^@^@^@^@^@ ;; (/ (char-code #\4) 4) == (length "simple-string") == 13
そして何故か、adjustableやfill-pointerが設定されている場合は、文字列のアドレスよりも前に文字列の中身が格納されていた。
>(defparameter *str* (make-array 13 :fill-pointer 0 :element-type 'character)) >(dotimes (i 13) (vector-push (aref "simple-string" i) *str*)) > (print-memory *str* 64 -64) ;; 「^@4^@^@」の位置は変わらないが、「^@s^@^@^@i^@^@ ...」の位置が前方に移動している 出力: ^@s^@^@^@i^@^@^@m^@^@^@p^@^@^@l^@^@^@e^@^@^@-^@^@^@s^@^@^@t^@^@^@r^@^@^@i^@^@^@n^@^@^@g^@^@^@^@^@^@^@\356^G^@^@4^@^@
symbolの場合は(も?)少し特殊で、(おそらく)組み込みのsymbolかユーザがinternしたsymbolかでメモリへの配置の仕方が変わっている。
;; 組み込みの場合 > (print-memory (symbol-name 'simple-string) 20 -4) ;; 1文字1byteで表現されている 出力: ^@4^@^@^@SIMPLE-STRING^@^@ ;; ユーザが(暗黙に)internした場合 > (print-memory (symbol-name 'my-simple-string) 68 -4) ;; 通常のsimple-stringと同じように、1文字に4byteが使われている 出力: ^@@^@^@^@M^@^@^@Y^@^@^@-^@^@^@S^@^@^@I^@^@^@M^@^@^@P^@^@^@L^@^@^@E^@^@^@-^@^@^@S^@^@^@T^@^@^@R^@^@^@I^@^@^@N^@^@^@G^@^@
以上。
その他に、構造体などでも試してみたが、文字列ほど直截的ではなかった。
最近は(私的には)lispばかり使っているので、こういったことを気にしなくなっていたが、久しぶりにメモリの中身とかを見てみると、やはりsbclで各種オブジェクトがどのように実装(メモリ表現)されているのかが、気になってくる。
すぐには無理だろうが、いつか時間のある時にでもソースコードをちゃんと調べてみたい。