摘要:假期再读一遍,仍有新感受。我想到了大三时,受老师委托,给学院智能小车团队新人招募出几道面试题。这个团队强力需要单片机的手艺,于是,必然得想办法考察一下学弟学妹们的单片机水平。
书稿中我写到了自由:
假期再读一遍,仍有新感受。我想到了大三时,受老师委托,给学院智能小车团队新人招募出几道面试题。这个团队强力需要单片机的手艺,于是,必然得想办法考察一下学弟学妹们的单片机水平。
有些题型墨守陈规,甚至专攻死记硬背,搞那种“请画出8051引脚图这类“常规趣味”,弄得面试者甚至还得想办法背诵引脚图,毫无意义,而老王当然花样多,不会按套路出牌。
我出的题是这样的:
在开发经典51系列单片机时,如果用C语言编写如下代码:
int main
{
unsigned char test_array[96] = {0};
······
}
① 会出现什么问题?②为什么?③有何解决方案?
此题文字不复杂,C语言代码也很简单,平平无奇,但其实考察的东西非常深,C语言,编译,链接,存储分配,架构都涉及。初学者也许直接看不出哪里有问题,而进阶选手或可触到本质,专业级选手则能挖出更多细节,并给出解决方案,体现出系统性思维。
这题其实考察了一个几乎是每位单片机初学者都会“踩爆”的坑,简单来说就是:
经典8051单片机的片内RAM只有区区128个字节,而这个局部变量数组test_array有96个字节,编译器会把它分配到这片内RAM。
编译时也许能过,链接时可能也能过,但烧录后运行,很危险。
因为一旦有中断或稍深的函数调用,返回地址 + 现场保护会很吃栈空间,当栈顶撞进这数组区,数组里边的数据就会混乱,程序就可能失控、跑飞、死机;而如果修改IDE设置,把数组安排到大容量外扩RAM,这倒是可行,但前提是你板子上真有外扩RAM芯片,否则,机器码中的MOVX的访问会打空,也是直接跑飞。
有人说,我在Windows上用这段代码写一个控制台应用,运行没事啊。
是的,那是因为PC一般内存较充足,起码几个GB,因此这样的“大”数组不算事。但嵌入式系统就非常不同了,几十KB就算是“大内存”,而经典51单片机的RAM甚至就只有百B级别,于是便成了“往茶缸里塞西瓜”,要么缸裂,要么吃瓜。
我在“重新理解编程”专栏中讲了这底层的相关概念,在此不赘。今天我是想抒发一个感想:
自由是要付出代价的。
比如C语言。
C语言的设计理念是“相信程序员”:
编译器不会替你做过多限制;不会自动检查栈是否够;不会验证数组越界;甚至不会报错,只默默生成机器码。就像开着法拉利上山路:
油门随便踩、护栏没有,高手可以在山间穿梭飞行炫技走捷径,但一不留神也会摔下悬崖。而新手,如果没有敬畏之心和对现实的充分掌握,上一秒是感觉良好真刺激,下一秒就又冲出跑道鼻青脸肿怀疑人生,一会天上一会地里。
就出现了“情绪不稳定”。
这和人生很像。
如果给一个人自由,但他却尚未看明世界的规则和真相,即没有足够的认知和能力去驾驭这自由,那便很可能出现这类似的困惑:
“我明明没错,却为何落得这般地步?”
不是没bug,而是还没意识到bug藏在何处。
但这是好事。
因为,当一个人遇到这样的困扰和痛苦时,便意味着,他已经开始从“抄袭别人的人生代码”,走向“反思、查bug,并理解自己人生”的新篇章。
来源:王珂