• 回复
  • 收藏
  • 点赞
  • 分享
  • 发新帖

单片机入门:C语言讲解(二)

   《 单片机入门:C语言讲解(一)》一文,我花了一个月左右的时间把C语言预编译相关的知识点梳理了一遍,可能还有疏漏的知识点,后面有需要再补充吧。

    接下来这一帖,系统地讲讲编译阶段的C语法,这是大头部分,知识点比较多,也比较庞杂,再接再厉,慢慢梳理,如果有讲的不到位或者错误的地方,希望大家及时斧正,谢谢。

   我的目标是尽量采用通俗易懂的语言讲解C语法以及单片机相关的故事,少出现大段的代码(看别人的代码本身就是一种折磨),将单片机涉及到的功能函数模块化,像使用元器件一样调用各功能函数。

   这是我写帖子的态度,此段写在开头,实时提醒自己。

全部回复(24)
正序查看
倒序查看
2021-06-11 13:01

开始讲解C语法之前,我想问大家两个问题:

第一:C语言的核心是什么?

第二:C语言如何控制单片机实现各种各样的复杂功能?

学会提问,带着问题学习,目的性更强,且效率更高。梳理知识点时,我喜欢先找到该知识点对应的脉络,然后从上往下依次理顺。这样的方式便于记忆以及后期的查漏补缺。

回到开始的第一个问题,C语言的核心是什么,很多人都会说是指针。市面上很多的教材及博客文章都提到了这一点,那为什么有此一说呢?

0
回复
2021-06-11 13:01

  指针的含义是什么,作用又为哪般?为什么开篇讲解C语法,我不从变量(标识符)的定义,命名规则,常用的运算等讲起,而偏偏先将很多人都头疼的指针拎出来呢?

  因为指针是C语言的脉络,所有语法实质上都是围绕着指针展开的。兵法有云,擒贼先擒王,只要抓住了C语言的主线,就能一步步地将所有知识点串联起来。

  什么是指针呢,答案是地址(不太严谨,便于大家理解,后面会补充解释)。单片机中的地址又是什么呢,有什么作用呢?是不是有点像俄罗斯套娃,要解释一个问题,却发现需要用更多的问题来解释。

  单片机中的地址是什么,有什么作用?这就回到文章开始处提到的第二个问题,C语言如何控制单片机实现各种各样的复杂功能?答案是寄存器。

0
回复
2021-06-20 11:39

    寄存器是什么,它是单片机内部用来存放数据的一些小型存储区域,主要作用是暂存参与运算的数据和运算结果,即过程数据。寄存器属于单片机内部存储功能模块中的RAM模块,包括通用寄存器、专用寄存器和控制寄存器。寄存器拥有非常高的读写速度,所以在寄存器之间的数据传送非常快。

    单片机内部的存储功能模块分为两大块,分别为ROM区和RAM区。RAM为随机存储器件,掉电不会保存数据,而ROM可以在掉电的情况下,依然保存原有的数据。ROM是Read Only Memory的意思,也就是说该类存储器只能读,不能写。而RAM是Random Access Memory的缩写,此类存储器可以随机读写。

0
回复
2021-06-20 11:39

    根据二者的不同特性,ROM一般用于存储固定的系统程序和字库等,51单片机ROM对应的C语言关键字为code。这类存储数据只用于读取操作,不涉及数据的修改,即写操作。

    RAM代表的是读写存储器,可在其中的任意存储单元进行读或写操作,单片机断电后其内的中间过程数据将不再保存,重新上电后则从ROM中调取相关的数据配置重新装入,通常用来存放输入输出数据、中间结果等。

    通俗的说,单片机内部的ROM和RAM分别对应着电脑的硬盘和内存。前文反复提到的寄存器其实就相当于是计算机内存中的一小部分。

0
回复
2021-06-20 11:40

    花了这么多口水作为铺垫,我的目的就是想告诉诸位看官“指针的概念”并不高深。指针这一工具的用途只不过是为了更简单明了地标注内存中的某一区域而已。下图为指针与内存单元的关系:

    随着对单片机硬件原理的认识不断加深,慢慢大家会发现计算机底层语言(汇编语言,C语言)的语法规则基本都是围绕着RAM内存展开的,大抵不过是为了更合理便捷地规划使用内存罢了。后续C语言中的诸多语法概念的引出都将围绕着指针及内存来阐述。

0
回复
2021-06-20 11:40

    C语言书籍开篇几乎都是从“hello world!”讲起,然后由变量的定义来进入C语法正题。我们就直接从“定义变量”这个话题入手吧,依旧采用我擅长的咬文嚼字的设问方式推导讲解。

“定义变量”四个字引出两个关键字“定义”和“变量”。什么是“定义”,它的作用为哪般,与“定义”有区别的概念是什么?什么是“变量”,它的作用是什么,有哪些类型,与之相关联的语法有哪些。上述几个问题我依次解答,当然都围绕着“指针”或“内存”的概念阐述。

0
回复
2021-06-21 20:53

    什么是“定义”,它的作用是什么。归纳总结后,我个人对C语法中“定义”的概念及作用解释如下:

    1. 在内存中开辟一个区域(区域的大小由数据变量的类型决定)用于存储合适的数据。

    2. 给开辟的内存空间赋予名称,专业术语称作“标识符”。对于变量而言,此标识符即为变量名。变量名与开辟的内存空间一一对应,此变量名代表关联内存空间的地址,而内存空间则用于存储对应类型及大小的数据。

    需要注意的是,“定义”完成即标志着变量名与对应内存空间的匹配,不可再重复定义,否则编译器会报错。对于全局变量而言,整个.c文件中只能被定义一次,对于局部变量而言,在函数体内部只能被定义一次。

    如何避免头文件中全局变量及函数的重复定义包含问题,就涉及到前文讲解的预编译中的”#ifndef”相关知识点,如若不了解,请回顾前文对应的知识点。

0
回复
2021-06-21 21:09

    给开辟的内存空间赋予名称是“定义”的第二个作用,通俗的讲就是命名。既然是命名,就牵扯出命名规则。命名的对象包括宏名,变量名,函数名,数组名,结构体名等等,C语言将此类命名的对象统称为“标识符”。

“标识符”的命名有一套规则,不可随便命名,否则编译器会报错。

0
回复
2021-06-21 21:09

1.C语言规定,标识符只能由字母(A~Z, a~z)、数字(0~9)和下划线(_)组成,并且第一个字符必须是字母或下划线,不能是数字,上述字符之外的其他字符不允许出现在标识符中。

2. 标识符长度受到不同编译器的限制,同时也受到操作系统的限制。c89规定标识符长度为31个字符以内,c99规定为63个字符以内。为方便记忆,个人建议标识符的长度不超过31个字符。例如某编译器中规定标识符前31位有效,当两个标识符前31位均相同时,则被认为是同一标识符。

3. 标识符区分大小写字母,HELLO与hello是两个不同的变量。

4.C语言中的关键字,有特殊意义,不能作为标识符。

5.自定义标识符最好取具有一定意义的字符串,便于记忆理解和后期的代码维护。

0
回复
2021-06-21 21:09

    在编程初期,大家就应该养成规范的编程习惯(包括命名规范,代码书写规范等)。良好的编程习惯有助于我们编写出更具可读性,可维护性,高效性,健壮性的代码。

    解释了“定义”的含义,接着我们再聊聊与“变量”相关的知识点。什么是“变量”, 有哪些类型的变量,变量的类型与内存的关系是什么,变量的强制类型转换有何意义,什么是变量的作用域及生存周期,与变量相关的语法点还有哪些?

    针对上述“变量”相关的问题,我们围绕指针与内存的原则依次展开。“定义”是划分内存,但是划分多大的内存,这就需要由变量的数据类型决定。划分的内存区域何时释放则取决于变量的生存周期。划分的内存区域中存储的数据能被哪些变量及函数调用呢?这得看变量的作用域。关于变量的数据类型,生存周期及作用域,后面我会详细讲解。

0
回复
2021-06-21 21:09

    在定义变量的同时,通常会对应着变量赋值,该赋值动作一般是取我们需要的常量将其填充至划分的内存单元中。如若仅仅定义了变量,却没有赋值呢?这就牵出变量的初始化问题。不管是全局变量还是局部变量,编译器默认都会对其初始化。只不过全局变量的初始化默认为0,而局部变量的初始化则为随机数。

0
回复
2021-06-22 21:43

    因为全局变量的初始值默认为零,所以该类变量不需要赋初值。不过为了培养良好的代码习惯,个人建议大家最好还是赋初值。另外,定义的全局变量在代码中未被使用,编译器也不会报错。

    局部变量的初始值,编译器默认赋值为随机数,因此不能在其他地方作为右值使用。如若被当作右值使用,编译器会提示错误:“使用了未初始化的局部变量”。局部变量定义后没有赋初值,系统会抛出警告:“xxx为未使用的局部变量”。因此,局部变量定义后就必须要赋初值。补充说明:还得看编译器的环境,在gcc version 8.3.0 (Raspbian 8.3.0-6+rpi1)中编译并没有报错。其他编译器环境,大家可以测试看看。

0
回复
2021-06-23 11:43

    什么是全局变量,什么是局部变量。他们有什么区别。使用问答方式解释相关概念,个人觉得效果很好,这样可以帮助大家养成主动思考的习惯,同时也加深了对知识点的印象。电子行业涉及的琐碎知识点太多,某个知识点在脑海中有了印象,再次遇到此类问题时可以借助电脑,书籍等工具把它搞明白。

    全局变量和局部变量由其定义的位置决定。全局变量定义在所有函数外部,局部变量则定义在函数内部。二者定义的位置不同就引出变量的作用域及生存周期的概念问题。

0
回复
2021-06-23 11:43

    “作用域”作何解释呢,从其字面意思入手吧。“作用”二字包含“有效的,能用的”含义,“域”字则有“范围,边界”的含义。三个字连起来解释为“有效的范围”,再加上“变量”二字,则解释为“变量有效的范围”。再进一步将变量与内存空间挂钩,“变量有效的范围”可以理解为“内存空间中的存储数据的有效范围”。至此我们推导出“变量的作用域”等价于“内存空间中的存储数据的有效范围”。

    “生存周期”又作何解释呢?“生存”有“活着,存在”的含义,“周期”则表示“时长,时段”。“生存周期”可理解为“存在的时长,存在的时间”,再加上“变量”二字,则解释为“变量存在的时间”。进一步联想变量与内存空间的概念,上述概念可解释为“内存空间中存储数据的存在时间”。

0
回复
2021-06-23 11:43

    能不能将“内存空间中存储数据的存在时间”这段文字背后的隐藏含义揭开呢?诸位看官,别急。如果你能坚持看到这里,小可想对您说声“谢谢”。您的求知欲很旺,正是您的激情让我能够坚持将文章写下去。

    谈到内存空间,我们自然而然地想到数据的存储,但大家有没有想过计算机的内存是有限的。如果只是一味地存储数据,内存空间早晚要爆仓(引入快递仓库爆仓的概念,便于大家更形象的理解。快递仓库用于存放物品,内存空间其实也是个仓库,只不过它用于存放数据罢了)。

    为防止内存空间爆仓,编译器建立了一套规则,用于配套存储数据的动作使用,也就是释放数据,专业术语称作“内存回收”。变量占用的内存回收方式取决于变量的存储类型,这一话题后面会详细讲解。

0
回复
2021-06-23 11:43

    脑海中有了内存数据存储与释放的概念,再次回到“内存空间中存储数据的存在时间”这个话题。这段文字背后隐藏的含义已经跃然纸上。它其实想表达的含义是内存中存储的数据何时释放。换句话说“生存周期”的概念表明了内存中的存储数据何时释放。

    至此,废了如此多的笔墨终于将“作用域”及“生存周期”的概念阐述完成。接下来我们可以说说全局变量及局部变量的作用域及生存周期的差别。

0
回复
2021-06-23 11:43

    计算机语言有很多种,在写这篇有关C语法的文章时,我总想着将C语法的知识点讲解的越细越好,但写着写着我突然发现语法固然重要,但隐藏在语法背后的计算机语言的思想更重要。我会尝试着将自己对计算机语法思想的一些感悟心得写进去。

    毕竟计算机的语言太多了,而且有些已经被淘汰或即将被淘汰,还有更多的语言正在孕育诞生中。仅仅局限于单一语法的讲解并不能满足个人对知识背后思想的占有欲,我总在试图抓住一些精髓的东西。

0
回复
2021-06-25 09:30

    回到“作用域及生存周期”的话题。我们知道全局变量定义在所有函数外部,局部变量定义在功能函数内部。二者位置的不同决定着其作用域及生存周期不同。前者位置在所有函数外部,所以它的作用域默认为是整个程序,包括所有的.c和.h文件,换句话说它可以被任意函数调用。而局部变量仅在某功能函数内部定义,这意味着它的作用域仅局限在某单一函数内部。

    至于二者的生存周期,则是永恒与霎那的区别。全局变量伴随着运行的程序同生共灭,只要程序在不间断的运行,那全局变量就会一直存在,永不释放,此即为永恒。局部变量仅在其定义的功能函数被调用时才会存在。一旦功能函数结束调用,局部变量就会消亡,其占有的内存即被释放,这代表着霎那。

0
回复
2021-06-25 09:31

    永恒与霎那本是佛家用语,小子不才,用其论述全局变量与局部变量的区别,望诸位看官海涵。全局永恒,局部霎那,它们各自的意义为哪般,或者说各有哪些优劣呢。常人都向往永生,内心深处对死亡都有着本能的恐惧。

    但人生实苦,肉身永恒背后需承受的诸般痛苦与磨难却非常人所愿。霎那虽短,但也正因其短暂,痛苦与磨难便也不显得那么难以忍受。

    对于时间而言,永恒是其上限,霎那为其下限,能够在时间长河的区域中找到对自己有意义的时段不失为智者的选择,我想这就是生命的意义吧。

0
回复
iszjt
LV.5
21
2021-08-05 11:27

C语言作为一门非常适合编程入门的语言也是嵌入式单片机行业必备的开发语言,打好基础的重要性不言而喻。

0
回复
2021-08-05 12:02

变量的大小,类型:

    定义变量的实质是在内存中开辟一段空间用于存储数据。那问题来了,需要在内存中开辟多大的空间呢,正整数、小数,负数,字符如何表示呢?这就引出了C语言关于数据类型的话题。

    C的数据类型大致可以分为四类,分别是基本类型,构造类型,指针类型及空类型。本着循序渐进的原则,我们就先从基本类型入手。如果各位看官充分理解了数据的基本类型,那后续三种数据类型也是手到擒来。

0
回复
2021-08-05 12:02

    C的基本类型用六个关键字表示,分别为char、short、int、long、float、double,依次代表的含义为字符型,短整型,整型,长整型,单精度浮点型,双精度浮点型。其中前四种类型用于表示各类整型数据,既然是整型数据,必然涉及到符号问题。

    整型数据有无符号使用关键字signed(有符号)和unsigned(无符号)表示。默认情况下,整型变量均带有符号,即隐式定义为signed 型。如此规定整型数据的符号规则优点是便于用户进行数值运算,但缺点也很明显,那就是整型数据表示的范围减少了一半。

0
回复
2021-08-05 12:03

    计算机大部分时间主要是处理各种数据信息,而数据信息通常是无符号整型。如果程序员未注意此类细节问题,有可能因为数据溢出导致程序宕机。

    前文讲过定义变量其实就是在内存中画一块连续的空间而已。而内存不可分割的最小单元为一个字节,那问题就简单了。在32位系统中,C语法规定char型变量占用一个字节,short型变量占用两个字节,int型变量占用四个字节,long型变量占用四个字节,long long型变量占用八个字节。

    后续会列出不同类型数据的表示范围

0
回复
aisi999
LV.1
25
2021-12-01 13:05

写得太好了!真正的智者。意犹未尽,后文呢......

0
回复