C言語難しい

意外と反響があったので期待通りに動くようになったバージョンを掲載しておきます。

#include "Python.h"
#include <stdio.h>

int main(){
  Py_Initialize();
  unsigned long buf[10];

  PyObject* i = Py_BuildValue("i", 0x1234567);
  printf("i: %p\n", i);
  memcpy(buf, i, sizeof(long) * 3);
  printf("  %016lx\n", buf[0]); /* refcount */
  printf("  %016lx\n", buf[1]); /* type */
  printf("  %016lx\n", buf[2]); /* value */

  PyObject* cls = PyObject_GetAttrString(i, "__class__");
  printf("i.__class__: %p\n", cls); /* type */

  PyObject* x = Py_BuildValue("f", (float)0x123456 / (float)0x100000);
  printf("x: %p\n", x);
  memcpy(buf, x, sizeof(long) * 3);
  printf("  %016lx\n", buf[0]); /* refcount */
  printf("  %016lx\n", buf[1]); /* type */
  printf("  %016lx\n", buf[2]); /* value */

  cls = PyObject_GetAttrString(x, "__class__");
  printf("x.__class__: %p\n", cls); /* type */

  Py_Finalize();
  return 0;
}

出力

i: 0x10101c590
  0000000000000001
  0000000100e7f180
  0000000001234567
i.__class__: 0x100e7f180

x: 0x10100c240
  0000000000000001
  0000000100e7d690
  3ff2345600000000
x.__class__: 0x100e7d690




(以下試行錯誤の過程)

期待通りに動かないコード

#include "Python.h"
#include <stdio.h>

int main(){
  Py_Initialize();
  PyObject* i = Py_BuildValue("i", 0xDEADBEEF);
  printf("i: %016x\n", i);
  printf("%016x: %032x\n", i, *(unsigned long*)i); /* refcount */
  printf("%016x: %032x\n", i + 1, *(unsigned long*)(i + 1)); /* type */
  printf("%016x: %032x\n", i + 2, *(unsigned long*)(i + 2)); /* value */

  PyObject* cls = PyObject_GetAttrString(i, "__class__");
  printf("cls: %016x\n", cls); /* type */

  Py_Finalize();
  return 0;
}

gdb

(gdb) n
7	  printf("i: %016x\n", i);
(gdb) n
i: 000000000021c580
8	  printf("%016x: %032x\n", i, *(unsigned long long*)i); /* refcount */
(gdb) n
000000000021c580: 00000000000000000000000000000001
9	  printf("%016x: %032x\n", i + 1, *(unsigned long long*)(i + 1)); /* type */
(gdb) n
000000000021c590: 000000000000000000000000deadbeef
10	  printf("%016x: %032x\n", i + 2, *(unsigned long long*)(i + 2)); /* value */
(gdb) n
000000000021c5a0: 0000000000000000000000000012f180
12	  PyObject* cls = PyObject_GetAttrString(i, "__class__");
(gdb) n
13	  printf("cls: %016x\n", cls); /* type */
(gdb) n
cls: 000000000012f180
16	  Py_Finalize();
(gdb) print i
$1 = (PyObject *) 0x10021c580
(gdb) print x
No symbol "x" in current context.
(gdb) print cls
$2 = (PyObject *) 0x10012f180
(gdb) x/xg i
0x10021c580:	0x0000000000000001
(gdb) x
0x10021c588:	0x000000010012f180 ←ちゃんとclsを指してる
(gdb) x
0x10021c590:	0xffffffffdeadbeef

うーん、%xが下8文字しか表示してないってこと?

全部%pに変えたらとりあえず8文字を超える部分も出るようになったが

i: 0x10b71c590
0x10b71c590: 0x1
0x10b71c5a0: 0xffffffffdeadbeef
0x10b71c5b0: 0x10b71c530
cls: 0x10b5d4180

えーと。それでも本当は2番めに表示されるものがclsと同じになってほしいのだが。

(gdb) print sizeof(Py_ssize_t)
$1 = 8
(gdb) print sizeof(double)
$2 = 8
(gdb) print sizeof(void*)
$3 = 8
(gdb) print sizeof(long)
$4 = 8
(gdb) print sizeof(long long)
$5 = 8
(gdb) print sizeof(char)
$6 = 1

memcpyしてみる

int main(){
  Py_Initialize();
  unsigned long buf[10];

  PyObject* i = Py_BuildValue("i", 0xDEADBEEF);
  printf("i: %p\n", i);
  memcpy(buf, i, sizeof(i) * 3);
  printf("%p: %p\n", i, buf[0]); /* refcount */
  printf("%p: %p\n", i + 1, buf[1]); /* type */
  printf("%p: %p\n", i + 2, buf[2]); /* value */

  PyObject* cls = PyObject_GetAttrString(i, "__class__");
  printf("cls: %p\n", cls); /* type */

  //PyObject* x = Py_BuildValue("f", 0xCAFEBABE + 0x100000000 + 0.0);
  Py_Finalize();
  return 0;
}

おお、できた。

i: 0x10811c590
0x10811c590: 0x1
0x10811c5a0: 0x10800c180
0x10811c5b0: 0xffffffffdeadbeef
cls: 0x10800c180

本当は0でパディングしたいんだけど%pにはパディングを指定できないし、%xにすると4バイトしか出ないし。あ、そうか%lxか。

%016lxにしたら期待通りの出力になった。

C言語難しい。

あ、全部終わってからいまわかった。

(gdb) print sizeof(PyObject)
$2 = 16

これが原因だな、そりゃそうだ。

以下メモ

typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;
/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD                   \
    _PyObject_HEAD_EXTRA                \
    Py_ssize_t ob_refcnt;               \
    struct _typeobject *ob_type;
typedef struct {
    PyObject_HEAD
    double ob_fval;
} PyFloatObject;