Python 中的“指针”思想:从 C 语言指针到对象引用、内存地址与数据结构

发布时间:2026/10/14 3:26:05
Python 中的“指针”思想:从 C 语言指针到对象引用、内存地址与数据结构
在 C 语言中指针是一个非常重要的概念。指针变量可以保存内存地址通过*解引用可以访问地址中存储的值通过取地址可以获得变量的内存位置。指针与数组、字符串配合使用时可以直接操作内存中的数据因此效率很高但也容易出现越界访问、野指针、内存泄漏等问题。Python 并没有传统意义上的“指针”语法。Python 程序员不会写*p来解引用也不会写a来取地址。但 Python 的变量、对象、引用和内存管理机制本质上仍然涉及“某个名字指向某个对象”“多个名字共享同一个对象”“通过引用间接访问数据”等思想。理解这些内容有助于把 C 语言中的指针概念迁移到 Python 中也能更好地理解可变对象、不可变对象、函数传参、列表与字符串操作等问题。一、Python 变量不是指针而是对象引用在 C 语言中变量通常对应一块固定大小的内存空间。例如inta10;int*pa;这里a存储整数10p存储a的地址*p可以访问a的值。在 Python 中变量更像是一个“标签”或“引用”。执行下面代码时a10baa和b并不是各自保存一份整数10而是都指向同一个整数对象。可以使用id()查看对象的内存地址a10baprint(id(a))print(id(b))print(aisb)运行结果中id(a)和id(b)通常相同a is b为True说明它们指向同一个对象。这里需要注意id()并不是 C 语言中的指针值也不能像指针一样进行算术运算。它只是 Python 提供的一个用于查看对象身份的工具。二、用id()和is理解“引用”行为Python 中没有取地址运算符但可以用id()观察对象是否相同没有*解引用运算符但变量名本身就可以访问对象。x[1,2,3]yxprint(id(x))print(id(y))print(xisy)y.append(4)print(x)输出类似[1, 2, 3, 4]这里y x并没有复制列表只是让y也指向同一个列表对象。修改y时x也会受到影响。这个现象与 C 语言中通过指针修改同一块内存有相似之处。但如果重新赋值y[5,6,7]print(x)print(y)此时x仍然是[1, 2, 3, 4]因为y只是换了一个新的对象并没有改变原来列表的内容。这说明 Python 中的赋值通常改变的是“引用指向”而不是直接复制数据。三、可变对象与不可变对象的区别理解 Python 引用机制时必须区分可变对象和不可变对象。整数、浮点数、字符串、元组属于不可变对象。列表、字典、集合属于可变对象。ahelloba bb worldprint(a)print(b)输出hello hello world字符串不可变b world会生成一个新字符串b指向新对象而a仍然指向原来的hello。列表则不同a[1,2,3]ba b.append(4)print(a)print(b)输出[1, 2, 3, 4] [1, 2, 3, 4]因为列表可变append是在原对象上修改所以a和b都会看到变化。这与 C 语言指针的类比是不可变对象更像“不能通过指针修改的内容”可变对象更像“可以通过指针修改的数组或结构体”。但 Python 仍然没有暴露真正的指针操作。四、函数传参传的是引用不是简单复制Python 函数传参既不是纯粹的“值传递”也不是 C 语言中显式的“指针传递”更准确地说是“对象引用传递”。defchange_list(lst):lst.append(99)nums[1,2,3]change_list(nums)print(nums)输出[1, 2, 3, 99]函数内部通过参数lst访问了外部列表对象并修改了它。但如果函数内部重新赋值defreset_list(lst):lst[0,0,0]nums[1,2,3]reset_list(nums)print(nums)输出仍然是[1, 2, 3]因为lst [0, 0, 0]只是让局部变量lst指向新列表并没有修改外部nums指向的对象。如果想让函数影响外部不可变对象通常需要通过返回值defadd_one(x):returnx1a10aadd_one(a)print(a)输出11五、用ctypes模拟 C 语言指针行为如果需要真正接触 C 语言风格的指针可以使用 Python 标准库ctypes。它允许 Python 调用 C 语言动态库也可以创建 C 类型变量。importctypes actypes.c_int(10)pctypes.pointer(a)print(a 的值:,a.value)print(p 指向的值:,p.contents.value)p.contents.value99print(修改后 a 的值:,a.value)输出a 的值: 10 p 指向的值: 10 修改后 a 的值: 99这里ctypes.c_int(10)创建了一个 C 整数ctypes.pointer(a)获得指向它的指针p.contents.value相当于 C 语言中的*p。但这种方式主要用于与 C 语言库交互日常 Python 开发很少这样写。大多数情况下Python 的引用机制已经足够使用。六、指针与数组Python 中对应列表与内存视图C 语言中数组名常常可以退化为指针通过指针偏移访问数组元素。Python 中没有指针算术但列表本身支持索引访问。arr[10,20,30,40,50]foriinrange(len(arr)):print(farr[{i}] {arr[i]})如果要模拟“通过指针遍历数组”可以用索引或迭代器实现deftraverse_by_index(data):result[]foriinrange(len(data)):result.append(data[i])returnresultprint(traverse_by_index([1,2,3,4,5]))对于更底层的数组操作可以使用array模块或memoryviewimportarray bufarray.array(i,[1,2,3,4,5])mvmemoryview(buf)print(mv[0])print(list(mv[1:4]))memoryview可以在不复制数据的情况下访问缓冲区内容这在处理二进制数据、网络协议、图像数据时比较有用。它仍然不是 C 指针但比普通列表更接近底层内存访问。七、指针与字符串Python 字符串不可变C 语言中字符串常以字符数组或字符指针表示可以通过指针移动逐个访问字符。Python 字符串也是序列可以通过索引访问sPointerforiinrange(len(s)):print(fs[{i}] {s[i]})但 Python 字符串不可变不能像 C 语言那样通过指针修改某个字符shello# s[0] H # 这会报错如果需要修改字符串内容通常转换为列表shellocharslist(s)chars[0]Hnew_s.join(chars)print(new_s)输出Hello这种写法类似于先把字符串复制到可修改的字符数组中修改后再重新组合。八、综合示例用 Python 模拟指针式操作下面代码把前面多个概念整合在一起包括对象引用、可变对象、函数修改、字符串处理、类封装以及ctypes指针模拟。importctypesdefshow_identity(obj,name):显示对象的 id 和类型帮助理解引用关系print(f{name}: id{id(obj)}, type{type(obj).__name__}, value{obj!r})defmodify_container(container):演示函数通过引用修改可变对象ifisinstance(container,list):container.append(added)elifisinstance(container,dict):container[updated]Truedefimmutable_update(value):不可变对象只能通过返回值产生新对象returnvalue1classPointerLike:用类封装一个可变值模拟简单指针行为def__init__(self,value):self.valuevaluedefderef(self):returnself.valuedefassign(self,value):self.valuevaluedef__repr__(self):returnfPointerLike({self.value!r})defswap_pointer_like(a,b):交换两个 PointerLike 对象的值a.value,b.valueb.value,a.valuedefmain():# 1. 对象引用print( 对象引用 )x[1,2,3]yx show_identity(x,x)show_identity(y,y)y.append(4)print(x ,x)# 2. 不可变对象print(\n 不可变对象 )a10ba show_identity(a,a)show_identity(b,b)aa1show_identity(a,a 更新后)show_identity(b,b 仍指向旧对象)# 3. 函数修改可变对象print(\n 函数修改可变对象 )data[1,2,3]modify_container(data)print(data ,data)info{name:Python}modify_container(info)print(info ,info)# 4. 不可变对象更新print(\n 不可变对象更新 )num5numimmutable_update(num)print(num ,num)# 5. 字符串处理print(\n 字符串处理 )spythoncharslist(s)chars[0]chars[0].upper()new_s.join(chars)print(new_s ,new_s)# 6. 类模拟指针print(\n 类模拟指针 )p1PointerLike(10)p2PointerLike(20)print(交换前:,p1,p2)swap_pointer_like(p1,p2)print(交换后:,p1,p2)# 7. ctypes 指针print(\n ctypes 指针 )c_valctypes.c_int(42)c_ptrctypes.pointer(c_val)print(原始值:,c_val.value)print(指针访问:,c_ptr.contents.value)c_ptr.contents.value100print(修改后:,c_val.value)if__name____main__:main()运行结果类似 对象引用 x: id..., typelist, value[1, 2, 3] y: id..., typelist, value[1, 2, 3] x [1, 2, 3, 4] 不可变对象 a: id..., typeint, value10 b: id..., typeint, value10 a 更新后: id..., typeint, value11 b 仍指向旧对象: id..., typeint, value10 函数修改可变对象 data [1, 2, 3, added] info {name: Python, updated: True} 不可变对象更新 num 6 字符串处理 new_s Python 类模拟指针 交换前: PointerLike(10) PointerLike(20) 交换后: PointerLike(20) PointerLike(10) ctypes 指针 原始值: 42 指针访问: 42 修改后: 100九、亮点总结概念迁移清晰把 C 语言的指针、数组、字符串操作对应到 Python 的引用、列表、字符串和ctypes指针便于跨语言理解。代码可运行示例覆盖对象身份判断、函数传参、可变对象修改、字符串处理、类封装和底层指针模拟。区分引用与复制重点说明y x不一定复制数据避免把 Python 变量直接理解成 C 语言指针。强调不可变性通过整数和字符串示例说明不可变对象不能原地修改只能通过生成新对象更新。函数传参解释到位用列表和整数分别展示“引用修改”和“重新赋值”的区别。安全使用底层指针ctypes示例展示了真正的指针操作同时指出它主要用于与 C 库交互。结构完整从基础引用机制到底层指针模拟再到综合示例适合用于技术报告或课程拓展阅读。总体来看Python 虽然没有 C 语言中*、这样的指针语法但它的引用机制、对象模型和可变性规则已经覆盖了“通过间接方式访问和修改数据”的核心思想。学习时可以把 C 指针作为理解底层内存的入口但在 Python 中更应关注对象身份、可变性、引用共享和安全的数据操作方式。