C 语言基础
结构体
//
struct(关键字) 名称 {结构体成员};// 定义结构体
struct Student { char name[50]; int age; float score;
};// 初始化 结构体变量
struct Student stu1;
strcpy(stu1.name, "张三");
stu1.age = 20;
stu1.score = 90.5;// 初始化 结构体数组
struct Student students[2]; strcpy(students[0].name, "张三");
students[0].age = 20;
students[0].score = 90.5; strcpy(students[1].name, "李四");
students[1].age = 21;
students[1].score = 85.0; // 结构体指针,访问结构体成员
struct Student *ptr = &stu1;
printf("学生名字: %s\n", ptr->name);
printf("学生年龄: %d\n", ptr->age);
printf("学生分数: %.1f\n", ptr->score);// cpython中的结构体,长整型的结构体
struct _longobject {PyObject_VAR_HEADdigit ob_digit[1];
};
typedef
// 用于给数据类型定义别名,常用于给结构体定义别名// 将int别名设置成integer
typedef int integer;
// 将int的指针设置pinteger
typedef int* pinteger; // 定义变量
integer a = 100;
pinteger pa = &a;struct Student { char name[50]; int age; float score;
};// 为struct Student 设置一个别名为stu
typedef struct Student stu;
// 为struct Student* 设置一个别名为pstu
typedef struct Student* pstu;
stu stu1;
stu1.name = "张三";
stu1.age = 20;
stu1.score = 89.5;pstu stu2 = (pstu)malloc(sizeof(Student)); // 申请内存
stu2->name = "张三";
stu2->age = 30;
stu2->score = 95.6;if (stu2 != NULL)
{free(stu2); // 释放内存
}// cpython中的使用
typedef struct _longobject PyLongObject;
宏定义
// 用于定义常量和表达式,预编译后会将对应的字符串替换成定义的值// 定义常量PyLong_SHIFT为30
#define PyLong_SHIFT 30// 源码
(digit)1 << PyLong_SHIFT// 预编译后
(digit)1 << 30
预编译指令
#if // 基础判断
#ifdef // 判断释放有宏定义
#ifndef //
#else
#elif
#endif
#define // 宏定义
#undef // 取消之前定义的宏
#defined// cpython
#if PYLONG_BITS_IN_DIGIT == 30
typedef uint32_t digit;
#define PyLong_SHIFT 30
#elif PYLONG_BITS_IN_DIGIT == 15
typedef unsigned short digit;
#define PyLong_SHIFT 15
#else
#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
#endif
assert断言
// 用于在调试期间捕获程序错误的机制
#include <assert.h>
int x = 5;
assert(x != 0); // 断言 x 不等于 0
goto 语法
// 用于无条件跳转到程序中的指定标签int i = 0;
for (i = 0; i < 10; i++) { if (i == 5) { goto end_loop; // 当 i 等于 5 时,跳转到 end_loop 标签处 } printf("%d\n", i);
}
end_loop: // 这是 end_loop 标签
printf("Loop ended early.\n");
一切皆对象
整数对象
有无符号
整数分为无符号整数和有符号整数,无符号整数只表示正数和零,而有符号整数则通过特定的编码方式(如补码)来表示正数、负数和零,在补码表示法中,最高位(符号位)为0表示正数,为1表示负数。其余位则用于表示数值的大小。
整数和操作系统
- 位数:
- 在32位操作系统中,整数通常使用32位来表示,即4个字节(32个比特)。
- 在64位操作系统中,整数通常使用64位来表示,即8个字节(64个比特)。
- 范围:
- 在32位操作系统中,有符号整数的范围通常是从 -2^31 到 2^31-1,即从 -2147483648 到 2147483647;无符号整数的范围通常是从 0 到 2^32-1,即从 0 到 4294967295。
- 在64位操作系统中,有符号整数的范围通常是从 -2^63 到 2^63-1,即从 -9223372036854775808 到 9223372036854775807;无符号整数的范围通常是从 0 到 2^64-1,即从 0 到 18446744073709551615。
整数结构体
// Include/object.h // 基础对象,定长的
typedef struct _object {_PyObject_HEAD_EXTRA/*引用计数,用于垃圾回收*/Py_ssize_t ob_refcnt; /*指向对象类型的指针,用于标识对象的类型,运行时类型检查和类型特定的操作,每个对象有一个类型对象,定义了该对象的属性、行为、方法等。PyObject 对象到底是什么类型的,只有再调用的时候,通过ob_type来判断,即多态机制*/struct _typeobject *ob_type;
} PyObject;// 可变长对象
typedef struct {PyObject ob_base;Py_ssize_t ob_size; /* 可变部分的项目数 */
} PyVarObject; // 定义所有可变大小容器对象的初始段。
#define PyObject_VAR_HEAD PyVarObject ob_base;// Include/longintrepr.h
typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */struct _longobject {PyObject_VAR_HEAD// 定义了一个数组 ob_digit,其类型为 digit(即uint32_t),该数组只有一个元素digit ob_digit[1];
};// 结合上面的结构体
typedef struct {_PyObject_HEAD_EXTRAPy_ssize_t ob_refcnt; // 引用计数 8字节struct _typeobject *ob_type; // 类型 8字节Py_ssize_t ob_size; // 元素个数 8字节digit ob_digit[1]; // digit类型的数组,默认长度为1
} PyLongObject;
PyLongObject 对象中数组ob_digit 是 digit 类型的,默认长度是 1,python 中的整数就是存在这个数组中的,看下 digit 的类型
// Include/longintrepr.h
// 值为30表示64位系统,值为15表示32位系统
#if PYLONG_BITS_IN_DIGIT == 30
// uint32_t 是一个无符号32位整数类型
typedef uint32_t digit;
#define PyLong_SHIFT 30
......#elif PYLONG_BITS_IN_DIGIT == 15
// unsigned short 一个16位的无符号整数类型
typedef unsigned short digit;
#define PyLong_SHIFT 15
当操作系统 64 位时,digit 的类型是无符号的 32 位整数类型,并且ob_digit 数组中每一位存储的最大数字为 (2^30)-1 即1073741823,此处 30 是 PyLong_SHIFT 的值。如果一个数值大于1073741823,则数组长度通过PyLong_SHIFT 进行计算。
操作系统是 32 位,digit 的类型是一个16位的无符号整数类型,PyLong_SHIFT 值为 15。
看下longintrepr.h 中一段注释
/* Long integer representation.The absolute value of a number is equal toSUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i)Negative numbers are represented with ob_size < 0;zero is represented by ob_size == 0.In a normalized number, ob_digit[abs(ob_size)-1] (the most significantdigit) is never zero. Also, in all cases, for all valid i,0 <= ob_digit[i] <= MASK.The allocation function takes care of allocating extra memoryso that ob_digit[0] ... ob_digit[abs(ob_size)-1] are actually available.CAUTION: Generic code manipulating subtypes of PyVarObject has toaware that ints abuse ob_size's sign bit.
*/
PyLongObject 对象中 ob_size 即表示数组ob_digit 的长度,又表示整数的符号。
ob_size如果小于零,则表示一个负数,ob_size 如果等于零,表示 0。而整个整数的值则通过表达式来计算:
**SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i)**
比如数字:1234567890987654321
在ob_digit 中的存储:
此时 ob_size => 3; ob_digit => {829168817, 76039122, 1}
根据公式反推一下ob_digit 数组的值:
第一步:
temp = 1234567890987654321
ob_digit[0] = 829168817 => temp % (2^30)
temp = 1149780946 => temp // (2^30)
ob_size++
第二步:
temp = 1149780946
ob_digit[1] = 76039122 => temp % (2^30)
temp = 1 => temp // (2^30)
ob_size++
第三步:
temp = 1
ob_digit[2] = 1 => temp % (2^30)
temp = 0 => temp // (2^30)
ob_size++
根据公式反算一下 829168817*2**(30*0) + 76039122*2**(30*1) + 1*2**(30*2)
用 python 来模拟查看下PyLongObject 对象
# cpython长整数底层存储算法
import math
import ctypesclass PyLong:SHIFT = 30MASK = (2 ** SHIFT)def parse_ob_size(self, longint):"""解析数组长度:param longint::return:"""ob_size = int(math.log(10) / math.log(self.MASK) * len(str(longint)) + 1)print(ob_size)return ob_sizedef parse_ob_digit(self, longint):n = abs(longint)ob_digit = []while n != 0:digit = n % self.MASKob_digit.append(digit)n //= self.MASKprint(ob_digit) # [829168817, 76039122, 1]def parse_ob_digit_by_struct(self, longint):"""通过访问底层地址查看ob_digit数组:param longint::return:"""_ob_size = self.parse_ob_size(longint)class _PyLongObject(ctypes.Structure):# c_ssize_t 是一个表示 C 语言中 ssize_t 类型的外包装类。ssize_t 是一个有符号整数类型,即 Py_ssize_t# c_void_p 是一个表示通用指针类型的外包装类,它对应于 C 语言中的 void* 类型。void* 是一个泛型指针# c_uint32 是一个外包装类,用于表示无符号的 32 位整数,对应于 C 语言中的 uint32_t 类型_fields_ = [("ob_refcnt", ctypes.c_ssize_t),("ob_type", ctypes.c_void_p),("ob_size", ctypes.c_ssize_t),("ob_digit", ctypes.c_uint32 * _ob_size)]long_object = _PyLongObject.from_address(id(longint))ob_size = abs(long_object.ob_size)ob_digit = long_object.ob_digit[:ob_size]print(ob_digit, ob_size) # [829168817, 76039122, 1], 3if __name__ == '__main__':pylong = PyLong()data = 1234567890987654321pylong.parse_ob_size(data)pylong.parse_ob_digit(data)pylong.parse_ob_digit_by_struct(data)
来看下几个特殊的数是怎么存的:
0 ob_size 如果等于零,表示 0,ob_size => 0
1 ob_size => 1; ob_digit=>{1}
-1 ob_size => -1; ob_digit=>{1}
(2 ^ 30) -1 ob_size => 1; ob_digit=>{1073741823}
-(2 ^ 30) -1 ob_size => -1; ob_digit=>{1073741823}
(2 ^ 30) ob_size => 2; ob_digit=>{0, 1}
-(2 ^ 30) ob_size => -2; ob_digit=>{0, 1}
整数占内存大小
ob_refcnt 是 8 字节,ob_type 指针类型占 8 字节,ob_size 占 8 字节,ob_digit 是 4 字节。所以整数的大小是,83+ob_size 绝对值*4
import sys# 1的ob_size是1,占内存大小为24+4*1=28
sys.getsizeof(1) # 28# 0的ob_size是0,说明ob_digit长度是0,24+4*0=24sys.getsizeof(1) # 24#(2**30)-1的ob_size是1,内存大小为24+4*1=28
sys.getsizeof((2**30)-1) # 28#2**30的ob_size是2,内存大小为24+4*2=32
sys.getsizeof(2**30) # 32
创建整数的方法
PyLong_FromLong 使用 C 的 long 类型创建 python 整数
PyLong_FromUnsignedLong 使用 C 的无符号 long 类型创建
PyLong_FromDouble 使用 C 的 longlong 类型创建
PyLong_FromVoidPtr 使用 C 的 指针 类型创建
PyLong_FromLongLong 使用 C 的 longlong 类型创建
PyLong_FromUnsignedLongLong 使用 C 的无符号 longlong 类型创建
PyLong_FromSsize_t 使用 C 的Py_ssize_t 类型创建
PyLong_FromSize_t 使用 C 的size_t 类型创建
创建整数对象
_PyLong_New
PyLongObject *
_PyLong_New(Py_ssize_t size)
{PyLongObject *result; // result 是一个PyLongObject类型的指针/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +sizeof(digit)*size. Previous incarnations of this code usedsizeof(PyVarObject) instead of the offsetof, but this risks beingincorrect in the presence of padding between the PyVarObject headerand the digits. 所需字节数为offsetof(PyLongObject, ob_digit) + sizeof(digit)*size此代码的先前版本使用sizeof(PyVarObject)而不是offsetof,但在PyVarObject头文件和数字之间存在填充时,这有可能是不正确的。*/if (size > (Py_ssize_t)MAX_LONG_DIGITS) {PyErr_SetString(PyExc_OverflowError,"too many digits in integer");return NULL;}/* PyObject_MALLOC 通常用于分配小块内存offsetof(PyLongObject, ob_digit) 表示获取 PyLongObject 结构体中 ob_digit 成员相对于结构体起始地址的偏移量申请内存存储PyLongObject结构体和长度为size 数组 ob_digit*/result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) +size*sizeof(digit));if (!result) {PyErr_NoMemory();return NULL;}// 初始化ob_type、ob_size、ob_refcnt等值return (PyLongObject*)PyObject_INIT_VAR(result, &PyLong_Type, size);
}
整数类型
// 整数对象的类型
PyTypeObject PyLong_Type = {PyVarObject_HEAD_INIT(&PyType_Type, 0)"int", /* tp_name */offsetof(PyLongObject, ob_digit), /* tp_basicsize */sizeof(digit), /* tp_itemsize */long_dealloc, /* tp_dealloc 析构操作,计数器为0时,清除对象*/0, /* tp_print */0, /* tp_getattr */0, /* tp_setattr */0, /* tp_reserved */long_to_decimal_string, /* tp_repr */&long_as_number, /* tp_as_number 数值相关的操作*/0, /* tp_as_sequence */0, /* tp_as_mapping */(hashfunc)long_hash, /* tp_hash 哈希函数,是可哈希的*/0, /* tp_call */long_to_decimal_string, /* tp_str */PyObject_GenericGetAttr, /* tp_getattro */0, /* tp_setattro */0, /* tp_as_buffer */Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |Py_TPFLAGS_LONG_SUBCLASS, /* tp_flags */long_doc, /* tp_doc */0, /* tp_traverse */0, /* tp_clear */long_richcompare, /* tp_richcompare 比较操作*/0, /* tp_weaklistoffset */0, /* tp_iter */0, /* tp_iternext */long_methods, /* tp_methods 相关的函数*/0, /* tp_members */long_getset, /* tp_getset */0, /* tp_base */0, /* tp_dict */0, /* tp_descr_get */0, /* tp_descr_set */0, /* tp_dictoffset */0, /* tp_init */0, /* tp_alloc */long_new, /* tp_new */PyObject_Del, /* tp_free */
};
比较操作
/* 复杂的比较操作
self 本身
other 比较对象
op 比较操作
*/
static PyObject *
long_richcompare(PyObject *self, PyObject *other, int op)
{int result;CHECK_BINOP(self, other); // 检测 self other 是不是长整数// 两个对象地址相同时,则表明是同一个对象,不需要比较if (self == other)result = 0;elseresult = long_compare((PyLongObject*)self, (PyLongObject*)other);Py_RETURN_RICHCOMPARE(result, 0, op);
}// 长整数比较
static int
long_compare(PyLongObject *a, PyLongObject *b)
{Py_ssize_t sign;/*对于长整数对象,Py_SIZE 返回的是数字中绝对值的位数(二进制位)。注意,这个大小是包括符号位的,所以一个正数和它的负数值会有相同的大小。对于列表、元组或其他序列类型的对象,Py_SIZE 通常返回序列中元素的数量。*/ if (Py_SIZE(a) != Py_SIZE(b)) {// a和b的ob_size不相等时,两个ob_size相减,然后根据符号判断哪个数大sign = Py_SIZE(a) - Py_SIZE(b);}else {// 如果a和b的ob_size相同,需要逐个比较ob_digit中的值Py_ssize_t i = Py_ABS(Py_SIZE(a));// 从后往前(因为高位的数放在后面),循环比较ob_digit中的值while (--i >= 0 && a->ob_digit[i] == b->ob_digit[i]);// a和b数组ob_digit值都一样,执行--i后i就小于零if (i < 0)sign = 0;else {// 如果a、b的ob_digit中有1位不相同,则只需要比较当前位上的数字,就能分出大小sign = (sdigit)a->ob_digit[i] - (sdigit)b->ob_digit[i];// 如果a是负数,则比较结果就要加上负号if (Py_SIZE(a) < 0)sign = -sign;}}// 最终检查sign的值// sign < 0, a < b// sign > 0, a > b// sign = 0, a = breturn sign < 0 ? -1 : sign > 0 ? 1 : 0;
}
整数类型的函数集
static PyNumberMethods long_as_number = {(binaryfunc)long_add, /*nb_add 加法*/ (binaryfunc)long_sub, /*nb_subtract 减法*/(binaryfunc)long_mul, /*nb_multiply 乘法*/long_mod, /*nb_remainder 除法*/long_divmod, /*nb_divmod 取余*/long_pow, /*nb_power 乘方*/(unaryfunc)long_neg, /*nb_negative*/(unaryfunc)long_long, /*tp_positive*/(unaryfunc)long_abs, /*tp_absolute*/(inquiry)long_bool, /*tp_bool*/(unaryfunc)long_invert, /*nb_invert*/long_lshift, /*nb_lshift*/(binaryfunc)long_rshift, /*nb_rshift*/long_and, /*nb_and*/long_xor, /*nb_xor*/long_or, /*nb_or*/long_long, /*nb_int*/0, /*nb_reserved*/long_float, /*nb_float*/0, /* nb_inplace_add */0, /* nb_inplace_subtract */0, /* nb_inplace_multiply */0, /* nb_inplace_remainder */0, /* nb_inplace_power */0, /* nb_inplace_lshift */0, /* nb_inplace_rshift */0, /* nb_inplace_and */0, /* nb_inplace_xor */0, /* nb_inplace_or */long_div, /* nb_floor_divide */long_true_divide, /* nb_true_divide */0, /* nb_inplace_floor_divide */0, /* nb_inplace_true_divide */long_long, /* nb_index */
};