扩容版
一 从系统视角看C语言学习
C语言是一种系统,也是一种表达方式
首先,从系统的角度来看,C语言本身就是一个完整的体系。它是一种“语言”,而语言的本质是用来表达人的创造力和思想的工具。大家常说C语言是“过程式语言”,其实这很贴合我们日常的思考和处理事情的方式——我们每天做的很多事情都是一步步按过程来的,C语言正好和我们的思维习惯很契合。
顺便说一句,在B站上做了一些视频,大家可以先去看看,感受一下C语言和我们日常生活的联系。看完四个视频后(按时间顺序),如果还感兴趣,再接着往下读这篇文章。
C语言要实现 用户想要的 系统的输入、输出和功能
假设你已经看过视频了,现在我们从系统的角度再来看C语言。
任何系统,无论是软件还是硬件,核心都有三个要素:输入、输出和内部功能。输入和输出又分为三种形式:物质、能量和信息。
- 能量:软件无法直接输入能量,它只能控制硬件去输入能量,但自身不“知道”如何输入能量。
- 物质:在软件中,物质可以理解为代码里准备处理的数据对象。
- 信息:信息是我们用来指导处理的方法和规则,比如函数的参数、算法的逻辑等。
所以,C语言程序的输入主要是“物质”和“信息”:物质是程序处理的数据,信息是用户对程序处理这些数据的要求(控制)。
二 C语言的输入——从键盘谈起
无论是考试还是将来在工程实践中,输入输出都是C语言学习和应用的核心内容。那么,C语言的输入到底是什么样的呢?
从历史看输入方式的演变
C语言诞生于计算机发展还比较底层的年代。那个时候,输入方式远没有现在这么多样化。你可以想象一下:那时没有AI语音输入,也没有触摸屏,甚至键盘的普及都算是个“奢侈品”。
回顾一下计算机历史,早期的输入方式有打卡、穿孔卡片等,非常原始。后来有了键盘,这才算是真正方便的输入设备。相比这些,现代的多元化输入方式已经非常丰富了,但C语言的设计环境和应用场景依然偏向底层,键盘输入依然是最基本也是最重要的输入方式。
理解键盘输入的重要性
键盘输入不仅仅是字母和数字这么简单,键盘上还有各种符号:空格、井号、百分号、导入符号……这些“奇奇怪怪”的符号其实都是输入的一部分。
对于C语言学习者来说,深入理解键盘输入的本质非常关键。无论是应试、求职还是实际工作,掌握如何处理键盘输入,都是必备技能。
输入的本质:物质与信息
从系统的角度看,输入可以分成两类:
在实际操作中,我们和用户需要提前“约定”好输入的内容:到底是作为物质数据输入,还是作为信息输入。
底层原理的简要说明
再往底层一点看,键盘的每一次按键,其实就是电路的开关动作,最终表现为电子在硅基片上流动,形成特定的信号。
虽然这个过程非常复杂,但我们在学习C语言时,不必深入到这么底层的细节。我们只需要关注C语言这个设计层次上的输入——也就是程序如何接收和处理这些按键信号。
当然不是,键盘只是在很长时间内,人机互动中,人输入复杂“物质/信息”最方便的形式。在嵌入式应用中,计算机系统与外部环境发生关系(或者说感知外部环境),键盘反倒很罕见了。相对简单的环境信号可以通过 对管脚(某个或者某几个I/O接口)的影响 来通知 CPU。因为简单很多时候,同一个/组管脚不仅用作Input输入,还能用作Output输出,当然也是为了节省管脚数量。不排除,但可能性很小。毕竟是第一门编程课,毕竟是纸面上考核。除非Lab里面反反复复在玩 一个/组单刀开关 整亮 一个/组Led的层次的游戏,纸面上 都很难考到 多组8bit的输入 之间的 (位)操作。两个设备的串口通信之类的,如果站在管脚的设计层次写代码,太复杂了。站在串口通信的层次,那就是调用现成的串口函数了。考试的时候,只有一双手,考官觉得大家天天按键盘(包括手机上),让大家边手写代码边回忆 按键盘的感觉,是正常的。考官自己边手写边想象 对着某个管脚控制的感觉,或者回忆串口函数的名字,也是极困难的(想起input/output,就已经很好了)。对了,如果手写代码的时候,一下子想不起 输入输出的标准函数的准确名字了 (比如get/scan/print/ 系列etc),写出一对 代表 输入输出的“英文单词”(如果给出英文版注释 就更好了-- 中文版示例/* 老师,我搞忘函数名了,我用incoming来代表从键盘输入字符串那个函数,不好意思哈*/),老师一眼能看出你在说啥子,大概率分数基本上就到手了。因为,当年他也有搞忘的时候,甚至对着电脑敲代码时也有卡壳的时候。真正重要的,不是 死死地记住了 printf这样的名字,而是对“系统的输入输出”有深刻的理解。三 C语言的输出——从字符到控制符
有了输入,自然就要谈输出。回顾历史,早期计算机的输出最容易实现的是什么呢?
输出的历史与基础
早期的输出设备主要是显示器,配合键盘作为输入设备,共同构成了最基础的人机交互方式。虽然现在我们可以用声音、图像等多种方式输出,但在C语言诞生和普及的那个时代,输出主要还是字符。
你在屏幕上看到的字符,本质上其实是以图像的形式呈现的,但它的基本单位是字符。这是计算机对世界最初的描述方式——用字符来表达信息。
字符输出的重要性
字符输出不仅是C语言的基本功能,也是评估C语言能力的重要指标。输出的字符包括大小写字母、数字,还有各种符号。
看似简单的字符输出,背后其实隐藏着复杂的设计考量。因为输出的是信息,这些信息既可能被人类直接理解,也可能被其他程序当作数据继续处理。
键盘字符的双重角色与转义字符
当年C语言的编程环境限制了输入设备只能是键盘,因此键盘上的所有字符都被充分利用。但这带来了一个问题:
- 有些字符不仅用作普通字符,还被赋予了特殊的控制功能,比如百分号(%)常用于格式控制。
- 这样就产生了“歧义”:当我们想输出一个百分号时,是当作普通字符输出,还是当作控制符处理?
为了解决这个问题,C语言引入了“转义字符”的概念。例如:
这些转义字符帮助程序区分普通字符和控制字符,保证输出的准确性和灵活性。
字符输出的现实意义
虽然现在我们习惯了多媒体丰富的输出方式,但在机器与机器之间,或者在底层系统中,字符依然是最简单、最通用的交流方式。
C语言虽然看起来“落后”,但正是因为它的底层架构,使得它能方便地控制硬件设备,处理底层数据,字符输出正是这种设计理念的体现。
四 系统功能--内部处理——从基础操作到函数封装
输入和输出都讲完了,接下来就是功能(系统内部如何处理数据)的问题了。
复杂代码考察的现实
无论是面试还是期末考试,考察复杂代码的可能性其实不大。尤其是在没有电脑操作环境的情况下,考官更多考察的是你在纸面上识别和书写代码的能力。
换位思考一下:老师或面试官自己在纸上写复杂代码的难度也很大,所以考试通常会设计相对简单的题目。
简单题目的意义
如果题目太简单,可能无法体现你的真实水平;因此,考试和面试往往会设计一些核心任务,考察你是否能理解并运用编程的基础知识。
编程的核心,不是为了写代码而写代码,而是利用程序实现基础功能,再将这些基础功能组合成复杂的功能。换句话说,就是如何把用户需求拆解成基础操作(比如循环、分支、函数、数据结构、算法),用程序实现。
基础知识的重要性
通常,考试或面试会通过一段描述,提出一个问题或功能需求,要求你用基础的数据结构和算法来解决。
这些基础内容包括:
- 调用现成函数:如
printf,这是大家都熟悉的,不需要重新设计。你可以尝试看看 printf 的源码,感受其复杂性。 - 内存管理:数组和指针紧密相关,内存的分配和管理是C语言的核心。
- 线性表和链表:线性表简单,链表稍复杂,都是常见的数据结构,涉及内存管理和优化。
- 队列、字符串处理
- 简单排序和查找算法
复杂的代码题通常不会在纸面上考察,因为写起来难度大且容易出错。
纸面考核难度的天花板示例:
递归。结构上简洁(优雅漂亮),边界条件又容易写错,操作次数过多又溢出来了(比如2^n,很容易就超过 char,unsigned char的最大值了),考官当年自己写代码的时候,也晕过才正常。
系统视角中:结构和边界是非常重要的两个元素
函数封装和模块化设计
C语言是一个载体,考试或面试更关注的是你的编程思维:
理想的代码结构是:
这种分级管理方式,类似人类社会组织复杂对象的方式,层层分工,管理有序。
最后:考核设计的逻辑:为什么不考复杂代码?
我们前面提到过,不管是期末还是招聘,考核一般不会考察非常复杂的代码,主要原因是复杂代码在纸面上写很容易出错。
那么,这背后的考题设计目的是什么呢?
考题目的不是让你出错
如果考试设计得太复杂,大家都容易犯错,那么考核的价值就大打折扣了。考官看不清楚谁真正掌握了知识,谁没有,这样的考试对评判程序员能力没有帮助。
程序员价值的核心评判
无论是企业面试还是学校考试,真正看重的是:
最基础内容的组合才是关键
因此,考核内容往往是最简单、最基础的知识点的组合,而这样的组合,本质上就是未来建构 任何系统时最基础也是最重要的工作方式。这些内容构成了一个程序员的“入门门槛”,也是评价一个程序员是否合格的最基本标准。
换句话说,考核考察的不是你能写多复杂的代码,而是你能否扎实掌握基础,灵活运用基础知识解决问题。
跟踪调试是语言学习里面最好玩的地方了,找一个集成开发环境/调试工具,
看看系统的
输入中的“物质(数据)”--待处理的对象,是如何在内存中被腾挪被变化的
输入中的“控制信息” 以及 系统实现中的控制(包括输出控制)--用户输入的控制要求 以及自己设计的控制要求,是如何一步步执行 折腾 “物质”(包括为了实现功能,自己设计的“临时物质/内部变量”)的。
再回头看,日常生活学习中,做什么,哪怕去食堂吃饭,都是编程,都是针对需求设计一个系统, 都有输入、输出和为了完成用户需求而设计的内部功能(流程,结构,边界条件etc)。