从 Python 到 C 语言:初学函数,我颠覆了对 “代码封装” 的认知
最近从零开始啃 C 语言,在此之前我已经用 Python 写了不少小脚本,最开始总觉得:函数嘛,不就是把重复的代码块封装起来,在哪写不都一样?可真正沉下心学完 C 语言的函数,从main函数的执行逻辑,到实参与形参的底层设计,我才发现,原来两种语言的函数,从根上的思维模式就完全不一样。
一、最直观的冲击:main函数,给了程序一个绝对清晰的 “起点”
学 Python 的时候,我最习惯的就是 “脚本式写代码”—— 顶格写一行print("hello world"),直接运行就能出结果;函数可以随便写在文件的任意位置,想调用的时候直接写函数名就行,整个文件没有强制的执行入口,更像一篇想到哪写到哪的散文,灵活又自由。
但 C 语言完全推翻了我的这个认知:所有 C 语言程序,唯一的、强制的执行入口,就是main函数。无论你在文件里定义了多少个功能函数,程序永远会从main函数的第一行代码开始执行,所有的功能函数,都必须在main函数里被调用,才能真正跑起来。
就像我前几天写的两个练习:一个是自定义行数的乘法口诀表,一个是数组的初始化、打印、逆置功能。我把乘法口诀表的打印逻辑封装成了printMultiplicationTable函数,把数组的三个操作分别封装成了init、print、reverse函数,这些函数全都定义在main函数之外,而main函数就像一个总导演,只负责接收用户输入、调度对应的功能函数、把控整个程序的执行流程。
// 所有功能函数定义在main之外
void init(int arr[], int size) {
for (int i = 0; i < size; i++) arr[i] = 0;
}
int main() {
// 程序永远从这里开始执行
int arr[10];
int size = sizeof(arr) / sizeof(arr[0]);
init(arr, size); // 只有在这里调用,init函数才会执行
print(arr, size);
return 0;
}
最开始我很不适应这种约束,觉得不如 Python 灵活,可写了几个练习之后才明白这种设计的好处:它逼着你从一开始就想清楚程序的完整执行流程,哪里是起点,哪里是终点,每一步要做什么,调用哪个函数。这种强结构的设计,让整个程序的逻辑变得无比清晰,不会出现 Python 里偶尔会遇到的 “脚本执行顺序混乱” 的问题,也让我真正理解了 “程序是顺序执行的” 这句话的底层含义。
二、最深刻的感悟:实参与形参的设计,原来藏着这么多巧妙的底层逻辑
如果说main函数只是让我改变了写代码的习惯,那 C 语言里实参和形参的设计,直接让我对 “函数传参” 这件事有了颠覆性的认知。
学 Python 的时候,我对传参的理解一直很模糊:反正把变量丢进函数里就行,可变对象改了外面也会变,不可变对象改了外面不变,至于为什么,从来没深究过。直到学 C 语言的实参和形参,我才真正搞懂了 “函数传参时,到底发生了什么”。
C 语言里有一句核心的话:实参传递给形参时,形参只是实参的一份临时拷贝。这句话我背了很多遍,直到踩了坑才真正理解。
最开始写数组逆置的reverse函数时,我很疑惑:为什么我在函数里交换了数组的元素,main函数里的原数组也跟着变了?按照 “形参是临时拷贝” 的说法,不是应该不影响原数组吗?后来才明白,数组名作为实参传递时,传的不是整个数组,而是数组的首元素地址 —— 形参拿到的是地址的拷贝,却能通过这个地址,找到内存里真正的数组,从而修改原数组的内容。
而与之对应的,是普通整型变量的传参:如果我把一个int a=10传给函数,在函数里修改形参的值,main里的a不会有任何变化,因为形参只是a的值的一份拷贝,两个变量存在完全不同的内存空间里,修改形参根本碰不到原变量。
还有一个让我印象深刻的点:C 语言的函数里,没办法直接获取传入数组的长度。Python 里给函数传一个列表,直接len(list)就能拿到长度,可 C 语言里,数组传入函数后,会退化成一个指针,函数根本不知道这个数组有多少个元素。所以我必须给每一个操作数组的函数,都额外传一个size参数,告诉函数这个数组的长度。
我还因为这个踩过坑:最开始写代码的时候,函数参数里写的是size,main函数里却定义了len变量,调用函数的时候直接传了len,结果编译报错,才发现自己连变量名都没统一。也正是这个坑,让我明白了 C 语言的严谨性:它不会给你做任何多余的 “贴心处理”,你给函数什么,它就只能用什么,每一个参数的意义、每一个变量的作用域,都必须由你自己想清楚、定义明白。
这种 “把底层逻辑摊在台面上” 的设计,和 Python 的 “黑盒式传参” 完全不同。Python 的传参很方便,可你很难知道底层到底发生了什么;而 C 语言的实参与形参,逼着你去理解内存、地址、变量的生命周期,每一次传参,你都清清楚楚地知道,函数拿到了什么权限,会不会修改原数据,这种 “一切尽在掌控” 的感觉,是我之前学 Python 时从未有过的。
三、从 “调用工具” 到 “创造工具”:函数让我真正理解了代码的底层逻辑
学 Python 的时候,我写函数更多是为了 “减少重复代码”,大部分常用的功能,Python 都给你封装好了现成的方法:数组初始化全 0,直接[0]*n;数组逆置,直接list.reverse();打印数组,直接print(list)。我只需要调用这些现成的工具,很少去想这些工具背后是怎么实现的。
但 C 语言不一样,它没有这么多现成的 “轮子”,所有的功能,都需要你自己用函数一点点实现。想要初始化数组为全 0,就要写一个循环,遍历数组的每一个元素,挨个赋值为 0;想要逆置数组,就要用双指针法,首尾元素挨个交换;想要打印数组,就要循环遍历每一个元素,挨个输出。
这个过程看似麻烦,却让我真正搞懂了这些功能的底层逻辑。之前我用 Python 的reverse()方法用了无数次,却从来没想过 “逆置” 这个动作到底是怎么实现的;而当我自己用 C 语言写完reverse函数之后,我才明白,原来数组逆置的核心,就是首尾元素的交换,直到两个指针相遇。
也是在这个时候,我才真正理解了函数的意义:它不只是 “减少重复代码” 的工具,更是模块化编程的核心。把一个大的程序,拆成一个个各司其职的小函数,每个函数只做一件事,做好一件事。就像我写的数组操作,init只负责初始化,print只负责打印,reverse只负责逆置,main函数里只需要调用这几个函数,整个代码的逻辑一目了然,后期想要修改某个功能,直接改对应的函数就行,不会影响其他部分的代码。
四、写在最后:从 Python 到 C,是思维方式的彻底转变
这段时间学 C 语言函数的过程,踩了不少坑,也有很多豁然开朗的瞬间。我最大的感受是:从 Python 到 C 语言,从来都不是简单的语法切换,而是编程思维的彻底转变。
Python 是面向结果的,它帮你屏蔽了大量的底层细节,让你能快速实现自己的想法,它告诉你 “你可以做什么”;而 C 语言是面向底层的,它把内存、地址、执行流程全都摊开在你面前,让你清清楚楚地知道,每一行代码到底在计算机里做了什么,它告诉你 “这件事到底是怎么实现的”。
main函数的存在,让我养成了 “先规划流程,再写代码” 的习惯;实参与形参的设计,让我对计算机的内存管理有了最基础的认知;而亲手实现一个个功能函数的过程,让我摆脱了对现成方法的依赖,真正开始理解代码的本质。
当然,我现在也只是刚推开 C 语言的大门,后面还有指针、递归、函数指针等等更多的知识点等着我去啃。虽然 C 语言比 Python 要严谨、甚至苛刻得多,但每搞懂一个知识点,每踩完一个坑,都能感受到自己对计算机的理解又深了一步,这种踏实的成长感,真的太让人上瘾了。
也送给和我一样初学 C 语言的小伙伴:别怕踩坑,别怕报错,每一个报错的背后,都是你理解底层逻辑的最好机会。慢慢来,一步一个脚印,我们都会在编程这条路上,越走越远。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)