用 c 扩展 python 时,如何处理任意大小的整数?

when extending python with c, how do one cope with arbitrary size integers?

Python/C API 手册提到从⁽¹⁾ 到⁽²⁾ void 指针的转换函数,这似乎是在中使用任意长度 python 整数的唯一方法C.
(1) : PyLong_FromVoidPtr() 格式 0&Py_BuildValue()
(2) : PyLong_AsVoidPtr() 和格式 00&0!PyArg_…Parse…()

但是,我没有在手册中找到⁽³⁾,任何关于如何使用这些空指针在 C 中用那些任意长整数做任何事情的指示。
(3) : 我尝试搜索 «voidptr»、«void *» 和 «0&» 但还没有完全阅读。

我在哪里可以找到有关它们的内部结构或要对其进行计算的原语的信息?

实际上,这些函数不会有 "a pointer to an arbitrarily large integer",而是字面上只是整数值作为 void * 指针,如转换为类型 void *。请参阅 PyLong_FromVoidPtr and PyLong_AsVoidPtr 的实现。它的存在只是为了让您在 Python 内保存任意指针,确保转换正确完成。

据我所知,从 Python 中获取任意长整数的最实用方法是使用 int.to_bytes and int.from_bytes. There is actually a internal-ish API _PyLong_FromByteArray / _PyLong_AsByteArray for that which you can probably use. See the related question Python extension - construct and inspect large integers efficiently.

注:有趣的是,似乎没有任何 C API,官方或其他方式,告诉 Python 整数值的位或字节长度。在 Python 中有 int.bit_length,但它似乎没有映射到任何公开可用的函数。

Include/longintrepr.h中有文档:

/* Parameters of the integer representation.  There are two different
   sets of parameters: one set for 30-bit digits, stored in an unsigned 32-bit
   integer type, and one set for 15-bit digits with each digit stored in an
   unsigned short.  The value of PYLONG_BITS_IN_DIGIT, defined either at
   configure time or in pyport.h, is used to decide which digit size to use.

   Type 'digit' should be able to hold 2*PyLong_BASE-1, and type 'twodigits'
   should be an unsigned integer type able to hold all integers up to
   PyLong_BASE*PyLong_BASE-1.  x_sub assumes that 'digit' is an unsigned type,
   and that overflow is handled by taking the result modulo 2**N for some N >
   PyLong_SHIFT.  The majority of the code doesn't care about the precise
   value of PyLong_SHIFT, but there are some notable exceptions:

   - long_pow() requires that PyLong_SHIFT be divisible by 5

   - PyLong_{As,From}ByteArray require that PyLong_SHIFT be at least 8

   - long_hash() requires that PyLong_SHIFT is *strictly* less than the number
     of bits in an unsigned long, as do the PyLong <-> long (or unsigned long)
     conversion functions

   - the Python int <-> size_t/Py_ssize_t conversion functions expect that
     PyLong_SHIFT is strictly less than the number of bits in a size_t

   - the marshal code currently expects that PyLong_SHIFT is a multiple of 15

   - NSMALLNEGINTS and NSMALLPOSINTS should be small enough to fit in a single
     digit; with the current values this forces PyLong_SHIFT >= 9

  The values 15 and 30 should fit all of the above requirements, on any
  platform.
*/

int 的长度是可变长度部分的长度乘以 15/16 位 - 数字是 30 bits in uint32_t, #if PYLONG_BITS_IN_DIGIT == 30, else 15 bits in uint16_t;长对象的结构是

struct _longobject {
    PyObject_VAR_HEAD
    digit ob_digit[1];
};

有一个成员 ob_size 会以字节为单位告知大小 - 所以如果 PYLONG_BITS_IN_DIGIT 是 30,那么 ob_digitob_size / sizeof(uint32_t) [=14] 的数组=]s,每个30位有效;否则 ob_digitob_size / sizeof(uint16_t) uint16_t 的数组,每个数字存储 15 个有效位。

这是 Include/longintrepr.h 的全部内容,但只公开了 #ifndef Py_LIMITED_API!