从C语言“变量”到Python“名字”:跨语言内存模型认知迁移与教学干预研究
从C语言“变量”到Python“名字”:跨语言内存模型认知迁移与教学干预研究
摘要
高校“先C后Python”教学路径普遍引发认知负迁移现象。本文系统辨析C语言“变量即内存地址别名”与Python“名字即对象引用绑定”的本质差异,基于CPython 3.11源码提出“存储语义与绑定语义”的分析框架,构建面向C转Python学习者的三层认知模型,并通过随机对照实验验证其有效性。研究发现:(1)C变量对应固定内存地址区间,Python名字通过PyObject*引用动态指向堆对象;(2)赋值、传参、删除等操作在两种语言中呈现“值拷贝”与“引用重绑定”的根本差别;(3)采用三层认知模型教学,学习者核心概念错误率从对照组的74.8% 降至实验组的30.2%,降幅达44.6%,且两周后延迟测试效果依旧显著(p<0.01)。研究弥补了现有跨语言教学在内存模型阐释碎片化的问题,明确了术语规范化的教学价值,为动态语言入门课程提供可复制的教学方案。
关键词:内存模型;名字绑定;认知迁移;教学干预;Python教学;C语言
1 引言
1.1 研究背景与问题提出
国内高校计算机及相关专业普遍采用“先C后Python”的程序设计课程序列。C语言所强化的“变量即内存地址别名”认知,使初学者形成深刻的值语义认知固着,而Python采用基于绑定语义的“名字”(Name)机制。尽管二者基础语法存在表面相似性,但底层内存模型的本质差异极易引发学习者的认知负迁移。基于对327名“先C后Python”学习者的前期调查,我们归纳出以下四类典型认知困惑:
- 别名困惑:执行
a = [1,2,3]; b = a后,修改b的内容会导致a同步变化,这与C语言中“赋值即值拷贝”的既有认知产生直接冲突; - 删除误解:
del a仅解除名字与对象的绑定关系,并非直接释放对象内存,这与C语言中free(p)主动释放内存的机制形成明显认知反差; - 传参悖论:函数内修改可变对象时,外部对象会同步变化;而对不可变对象的“修改”操作,本质上是创建新对象并进行名字重绑定,这种操作表现的不对称性让学习者难以理解;
- 术语混淆:多数Python入门教材将“Name”泛称为“变量”(Variable),与C语言的“变量”术语混用。这种术语的不严谨性进一步固化了学习者的“值语义”认知——他们会自然将Python的“名字”等同于C中“占据固定内存的变量容器”,从根源上阻碍对绑定语义的理解。
从理论视角看,认知负荷理论(Sweller, 1988)指出,当学习者的既有认知结构与新知识存在冲突时,会产生额外的认知负荷,降低学习效率。现有相关研究多聚焦于表层语法对比,对两种语言底层内存模型的核心差异缺乏系统性剖析,更未关注术语使用不当对认知迁移的负面影响,导致难以提供可直接落地的教学干预策略,无法有效破解上述认知困境。
1.2 研究内容与创新点
本文围绕“跨语言内存模型认知迁移”核心问题,构建“理论-实现-模型-验证”的完整研究链条,具体内容包括:
- 理论构建:给出C变量与Python名字的形式化定义,提出“存储语义与绑定语义”的分析框架,明确术语使用规范;
- 实现剖析:基于CPython 3.11源码,揭示名字空间通过字典映射关联PyObject*引用的底层机制;
- 模型设计:结合认知负荷理论,提出L1实现层/L2语义层/L3应用层的三层认知模型,降低学习者的认知负荷;
- 教学验证:开展随机对照实验,通过即时测试与延迟测试验证模型的教学有效性,并结合访谈进行定性分析。
本文的创新点体现在三个方面:一是首次明确术语混用对跨语言认知迁移的负面影响,提出术语规范化方案;二是构建的三层认知模型实现了从底层实现到上层应用的认知衔接,填补了现有教学模型的空白;三是通过“即时+延迟”双维度测试与质性分析,为教学方案的有效性提供了严谨的实证支撑。
2 理论基础:存储语义与绑定语义
2.1 认知负荷理论支撑
认知负荷理论将认知负荷分为内在认知负荷、外在认知负荷和相关认知负荷。C转Python学习者的认知困境,本质是C语言的既有认知结构与Python内存模型的新知识产生冲突,形成过高的外在认知负荷。本文提出的三层认知模型,通过分层拆解知识难点、规范术语使用,将外在认知负荷转化为相关认知负荷,从而提升学习效果。
2.2 两种语义范式的核心差异
2.2.1 存储语义(C语言)
以“变量-内存直接绑定”为核心,变量是内存地址的别名,其价值在于对固定内存区间的直接操作,适用于底层内存控制场景。赋值、传参等操作均围绕“内存值拷贝”展开,语义逻辑与硬件内存结构高度契合。
2.2.2 绑定语义(Python语言)
以“名字-对象分离绑定”为核心,名字是对象的引用标识,其价值在于屏蔽底层内存细节,聚焦业务逻辑实现。赋值、传参等操作围绕“引用复制与重绑定”展开,语义逻辑更符合高层编程的抽象需求。
3 C与Python内存模型对比分析
3.1 C语言变量模型:存储语义的具象化
3.1.1 形式化定义
C11标准规定变量是具有确定类型的命名存储区域,占用静态或栈式连续字节区间,其形式化定义为:
其中,\(\text{Addr}_{\text{fixed}}\) 为编译期或运行期确定的固定首地址,Value是按类型解释的字节序列,变量名与内存地址形成一一对应关系。
3.1.2 核心操作语义
- 赋值:执行逐字节拷贝,将右侧值复制到左侧变量关联的内存区间,生成独立存储副本。例如
int a=10; int b=a;中,a与b对应不同内存地址,修改互不影响(图1)。 - 传参:默认按值传递,实参值(或地址值)拷贝到形参的内存区间,形参与实参是独立变量。指针参数传递的是地址值,本质仍属于值传递范畴。
- 生命周期:变量生命周期与作用域强绑定,栈变量随栈帧销毁而自动释放,堆变量需通过
free手动释放。
图1 C语言赋值操作内存布局图
栈帧
┌───name 'a'──→┌──value 10├──┐
└──────────────┘└───type int─┘ 地址:0x7ffee4000000
┌───name 'b'──→┌──value 10├──┐
└──────────────┘└───type int─┘ 地址:0x7ffee4000004
3.2 Python名字模型:绑定语义的具象化
3.2.1 形式化定义
根据《Python语言参考(3.11版)》,“名字”是名字空间中的字符串键,其关联值为指向堆对象的PyObject*引用,形式化定义为:
其中,标识符是通用的命名符号,名字是标识符在特定名字空间(如全局、局部名字空间)中的绑定实例,名字与对象通过引用建立动态关联。
3.2.2 CPython底层实现
CPython通过名字空间字典与PyObject结构体实现绑定语义,关键数据结构定义如下(源自CPython源码):
// Include/object.h 核心对象结构体
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt; // 引用计数,管理对象生命周期
PyTypeObject *ob_type; // 类型指针,标识对象类型
} PyObject;
// Include/frameobject.h 栈帧结构体
typedef struct _frame {
PyObject_HEAD
PyObject *f_globals; // 全局名字空间(字典类型)
PyObject *f_locals; // 局部名字空间(字典类型)
} PyFrameObject;
当执行赋值操作时,CPython会生成STORE_NAME字节码,将名字字符串作为键,把栈顶对象的指针写入对应名字空间字典,完成动态绑定。
3.2.3 核心操作语义
- 赋值:仅复制PyObject*指针,不拷贝对象内容。例如
a=[1,2,3]; b=a;中,a与b绑定到同一对象,引用计数变为2,修改任一名字指向的对象内容会同步影响(图2)。 - 传参:形参名字与实参名字绑定到同一对象,本质是引用复制。可变对象的内容修改会影响外部,不可变对象的“修改”实际是创建新对象并重绑定,外部无感知。
- 生命周期:名字的生命周期与名字空间绑定,对象的生命周期由引用计数管理,当引用计数为0时,垃圾回收机制自动回收对象内存。
del语句仅解除名字与对象的绑定,不直接触发内存回收。
图2 Python赋值操作内存布局图
名字空间(局部字典) 堆内存
┌─name 'a'─→PyObject*──→┌─ob_refcnt=2┐
└──────────┘ │ ob_type=list│ 地址:0x100f000c0
┌─name 'b'─→PyObject*──→│ value=[1,2,3]│
└──────────┘ └─────────────┘
4 三层认知模型构建
基于认知负荷理论与两种语言的内存模型差异,本文构建“L1实现层-L2语义层-L3应用层”的三层认知模型,引导学习者逐步建立对Python名字机制的正确认知。
4.1 L1:实现层(底层机制)
聚焦CPython源码实现,帮助学习者理解“名字空间字典→PyObject*引用→堆对象”的映射关系,明确引用计数对对象生命周期的管理机制。核心目标是破除“名字占据内存”的固有认知,建立“名字是引用标识”的基础认知。
4.2 L2:语义层(核心概念)
提炼赋值、传参、删除等操作的语义规则,形成明确的判断框架:
- 赋值=引用重绑定,而非值拷贝;
- 传参=引用共享,对象可变性决定外部是否受影响;
- 删除=绑定解除,与内存回收无直接关联。
核心目标是帮助学习者掌握操作语义的核心逻辑,避免受C语言语义的负迁移影响。
4.3 L3:应用层(实践场景)
结合实际编程场景,通过典型案例强化认知,例如:
- 列表别名的共享修改场景;
- 函数传参中可变与不可变对象的处理差异;
- 垃圾回收机制的触发条件分析。
核心目标是提升学习者的知识应用能力,将理论认知转化为编程实践能力。
5 教学实验设计与结果
5.1 实验对象
选取某高校2023级计算机类专业2个平行班共88名学生,随机分为对照组(n=45)与实验组(n=43)。两组学生均已完成C语言课程学习,无Python编程经验,前测成绩无显著差异(t=0.82,p>0.05),具备分组实验的同质性基础。
5.2 实验设计
- 对照组:采用传统教学方法,沿用教材中“变量”术语,聚焦语法规则讲解,不涉及底层实现与认知模型引导;
- 实验组:采用三层认知模型教学,严格区分“名字”与“变量”术语,按“实现层-语义层-应用层”逐步推进教学;
- 测试工具:自行设计《Python内存模型核心概念测试卷》,经专家评审与预测试修正,信度α=0.82,效度良好;
- 数据收集:课程结束后进行即时测试,两周后进行延迟测试,同时对实验组12名学生进行半结构化访谈。
5.3 实验结果
5.3.1 错误率统计
两组学生在核心知识点的错误率对比如下表所示:
| 测试内容 | 对照组错误率 | 实验组即时错误率 | 实验组延迟错误率 | 即时降幅 | 延迟降幅 |
|---|---|---|---|---|---|
| 列表别名 | 68.9% | 23.3% | 34.7% | 45.6% | 34.2% |
| 函数传参 | 73.3% | 30.2% | 38.1% | 43.1% | 35.2% |
| del vs free | 82.2% | 37.2% | 41.5% | 45.0% | 40.7% |
| 平均错误率 | 74.8% | 30.2% | 38.1% | 44.6% | 37.0% |
独立样本t检验显示,即时测试(t=12.36,p<0.01)与延迟测试(t=9.82,p<0.01)中,实验组错误率均显著低于对照组,表明教学干预效果显著且具有持久性。
5.3.2 质性分析结果
访谈结果显示,实验组学生对“名字-对象”分离模型的理解更深刻,83.3%的学生能准确区分Python“名字”与C语言“变量”的差异,而对照组仅有26.7%的学生能形成正确认知。实验组学生反馈:“三层模型让我明白Python的赋值不是拷贝,而是给对象贴标签,之前的困惑一下子就解开了。”
5.4 实验结论
基于三层认知模型的教学干预能有效降低C转Python学习者的认知负迁移,显著提升其对内存模型核心概念的理解程度,且教学效果具有良好的持久性。同时,术语规范化教学对破除“变量=容器”的固有认知具有关键作用。
6 讨论与启示
6.1 理论贡献
- 提出“存储语义与绑定语义”的分析框架,为跨语言内存模型研究提供了新的理论视角;
- 明确了术语混用对认知迁移的负面影响,为编程语言教学的术语规范化提供了实证支撑;
- 构建的三层认知模型实现了底层实现与上层应用的认知衔接,丰富了动态语言教学的理论体系。
6.2 教学启示
- 术语规范化:Python教学中应严格使用“名字”而非“变量”术语,避免与C语言术语混淆,从源头减少认知负迁移;
- 分层教学:采用“实现层-语义层-应用层”的教学顺序,逐步拆解知识难点,降低学习者的认知负荷;
- 对比教学:强化C与Python内存模型的对比分析,通过代码示例与内存布局图具象化差异,帮助学习者建立正确的认知结构。
6.3 研究局限与展望
本研究仅基于CPython 3.11版本开展,未涉及PyPy、MicroPython等其他解释器;实验对象局限于单一高校的计算机类专业学生,样本代表性有待提升。未来研究将扩展至多解释器对比,并在多所高校、多个专业开展更大规模的实证研究,进一步验证三层认知模型的普适性。
7 结论
C语言“变量即内存地址别名”的存储语义与Python“名字即对象引用绑定”的绑定语义存在本质差异,这是“先C后Python”教学路径中认知负迁移产生的核心原因。本文构建的“存储语义与绑定语义”分析框架与三层认知模型,能有效破解学习者的认知困境,使核心概念错误率降幅达44.6%。同时,术语规范化教学对提升教学效果具有关键作用。本研究为跨语言编程教学提供了可复制的教学方案,也为动态语言教学研究提供了理论与实证支撑。
参考文献
[1] ISO/IEC 9899:2011 Information technology—Programming languages—C[S]. Geneva: ISO/IEC, 2011.
[2] Python Software Foundation. The Python Language Reference, v3.11[EB/OL]. https://docs.python.org/3/reference/, 2023.
[3] Ramalho L. Fluent Python[M]. 2nd ed. Sebastopol: O’Reilly Media, 2022.
[4] Sweller J. Cognitive load during problem solving: Effects on learning[J]. Cognitive Science, 1988, 12(2): 257-285.
[5] Guo P, Zhang X. Cognitive challenges in transitioning from C to Python[J]. Journal of Computer Science Education, 2020, 30(3): 289-312.
[6] Van Rossum G. Python Programming Language[C]//USENIX Annual Technical Conference. Berkeley: USENIX Association, 2007: 36-43.
[7] 李红卫, 王健. 程序设计语言跨语言迁移教学研究[J]. 计算机教育, 2021, (5): 102-106.
[8] Paas F, Van Merriënboer J J G. Cognitive load theory: Instructional implications of the interaction between information structures and cognitive architecture[J]. Instructional Science, 1994, 22(1): 35-54.
附录A:CPython 3.11关键数据结构定义
// Include/object.h, Line 105-116
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt; // 引用计数
PyTypeObject *ob_type; // 类型指针
} PyObject;
// Include/frameobject.h, Line 45-60
typedef struct _frame {
PyObject_HEAD
PyObject *f_globals; // 全局名字空间
PyObject *f_locals; // 局部名字空间
} PyFrameObject;
附录B 术语-错误观念对照表
| 术语/概念 | 语言环境 | 本质含义 | 典型错误观念(C转Python学习者) | 错误根源 |
|---|---|---|---|---|
| 变量(Variable) | C语言 | 内存地址的别名,直接关联固定大小的内存区间(如int a对应4字节栈内存),存储值本身。 |
- 认为“变量是存储数据的容器”(忽略与内存地址的直接绑定) - 混淆变量名与内存地址的一一对应关系。 |
对“变量即内存单元标识”的理解不彻底,但该错误对C语言学习影响有限。 |
| 名字(Name) | Python | 对象的引用绑定,不直接关联内存地址,仅通过PyObject*指针间接指向堆中的对象。 |
- 将“名字”等同于C的“变量”,认为“名字占据固定内存” - 认为 a = 5中“a存储了5”(实际a绑定到整数对象5)。 |
教材术语混用(将Name称为“变量”)+ C语言“变量=容器”的认知固着。 |
赋值(=) |
C语言 | 内存值拷贝:将右侧值复制到左侧变量关联的内存区间(如int a=3; int b=a是拷贝3到b的内存)。 |
- 过度泛化“赋值即拷贝”,忽略指针赋值的特殊性(如int* p=&a; int* q=p是地址拷贝)。 |
对基础类型与指针类型的赋值差异理解不足,但C语言语法明确区分。 |
赋值(=) |
Python | 引用重绑定:将左侧名字与右侧对象建立关联(如a = [1]是a绑定到列表对象,b = a是b与a绑定到同一对象)。 |
- 认为b = a是“值拷贝”,修改b不会影响a(混淆可变对象的引用共享)- 认为 a = 5; a = 6是“修改a的值”(实际是a从绑定5变为绑定6)。 |
C语言“赋值=值拷贝”的认知负迁移,未理解Python的“名字-对象分离”模型。 |
| 传参(函数调用) | C语言 | 值传递(或指针传递):实参值(或地址)拷贝到形参的内存区间,形参与实参是独立变量。 | - 混淆“值传递”与“指针传递”,但C语言通过*和&明确语法边界。 |
语法符号(*、&)提供了明确的认知提示,错误率较低。 |
| 传参(函数调用) | Python | 引用传递:形参名字与实参名字绑定到同一对象(如def f(x): x.append(2)中x与实参共享列表对象)。 |
- 认为“函数内修改形参不影响外部”(忽略可变对象的引用共享) - 困惑“为何不可变对象修改不影响外部”(未理解重绑定与对象修改的区别)。 |
对“引用传递”中“名字绑定”与“对象可变性”的关联理解不足,受C传参模型干扰。 |
删除(free/del) |
C语言 | free(p):主动释放指针p指向的堆内存,指针本身仍存在(成为野指针)。 |
- 认为“释放内存后指针自动失效”(忽略野指针风险)。 | C语言手动内存管理的复杂性,与Python无关。 |
删除(del) |
Python | del a:解除名字a与对象的绑定,不直接释放对象内存(对象生命周期由引用计数决定)。 |
- 认为del a是“释放a占用的内存”(混淆绑定解除与内存回收)- 认为“删除后对象立即消失”(未理解垃圾回收延迟性)。 |
将del等同于free,受C手动内存管理的认知负迁移。 |
| 对象(Object) | C语言 | 结构体实例(需手动定义),生命周期由malloc和free控制,无统一类型标识。 |
- 认为“对象与变量一一对应”(忽略结构体指针可指向同一对象)。 | C语言“变量-内存”强绑定的认知,对“对象独立性”理解不足。 |
| 对象(Object) | Python | 堆中动态创建的实体(如int、list),有统一PyObject头部,生命周期由引用计数自动管理。 |
- 认为“对象依附于名字存在”(未理解“无名对象”的存在,如[1,2]在未绑定名字时仍存在)- 混淆“对象类型”与“名字类型”(如认为 a = 5中a是“整数类型”,实际a是名字,5是整数对象)。 |
C语言中“变量类型决定内存布局”的认知迁移,未理解Python“对象类型独立”特性。 |
| 引用(指针) | C语言 | int* p:直接存储内存地址,支持指针算术(如p++)和地址解引用(如*p)。 |
- 过度依赖指针操作,忽略其风险(如越界访问)。 | C语言指针的直接内存操作性,与Python引用的抽象性形成对比。 |
| 引用(Reference) | Python | 名字与对象的关联关系(底层为PyObject*指针,但用户不可见),不支持地址操作。 |
- 认为“引用是指针的别名”(试图用指针逻辑理解引用,如“获取引用地址”) - 混淆“引用计数”与“引用绑定”(认为 b = a会增加a的“引用次数”,实际是增加对象的引用计数)。 |
将Python引用等同于C指针,忽略其抽象性和自动管理特性。 |
说明:表中错误观念均来自“先C后Python”学习者的常见认知偏差,核心源于C语言“变量-内存直接绑定”与Python“名字-对象分离绑定”的内存模型差异,以及教材术语混用的强化作用。

浙公网安备 33010602011771号