四时宝库

程序员的知识宝库

CPython源码阅读8-变长对象的柔性数组

Int对象在CPython中的位置

//cpython-master\Include\longobject.h

typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */

//cpython-master\Include\longintrepr.h

struct _longobject {

PyObject_VAR_HEAD//变长对象头

digit ob_digit[1]; //维护具体的整数值

};


在这个声明中我们看到一个声明为:digit ob_digit[1]; //维护具体的整数值,数组的大小声明为1。我们凭感觉可知道,这个数组的大小一定不是1,它是随着整数的范围扩展的,实现变长对象。这种方法在linux内核中大量使用,被称作柔性数组。

动态结构体的需求催生了这种代码结构。比如这个int对象,我们可能需要一个ob_digit,或者2个是不确定的,我们期望根据需要动态地调整这种结构体的大小。这种代码结构已经被C99收入标准。

C99使用不完整类型实现柔性数组成员,在C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组(flexible array)成员(也叫伸缩性数组成员),但结构中的柔性数组成员前面必须至少一个其他成员。柔性数组成员允许结构中包含一个大小可变的数组。柔性数组成员只作为一个符号地址存在,而且必须是结构体的最后一个成员,sizeof 返回的这种结构大小不包括柔性数组的内存。柔性数组成员不仅可以用于字符数组,还可以是元素为其它类型的数组。包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

未知大小的数组和数组大小为1还是有区别的。经查证,这种定义为数组大小为1的“柔性数组”,是为了兼容所有编译器。没有c99但又要用柔性数组时的妥协,gcc对c语言的一种扩展。


从这个编译器来看,我的电脑为64位,PYLONG_BITS_IN_DIGIT == 30,typedef uint32_t digit;实际上digit类型就是一个uint32_t类型,就是一个unsigned long类型。

所以Int这个变长对象就与类型无关,永远不会溢出。因为CPython底层使用一个类型为32位unsigned long的柔性数组存储的,而数组的长度有时可变的。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接