产品选型

单片机c语言指令集 单片机为什么要使用C语言?

小编 2024-10-06 产品选型 23 0

单片机为什么要使用C语言?

单片机为什么要使用C语言呢?创客学院两分钟带你搞懂

将C向MCU(俗称单片机)8051上的移植始于80年代的中后期。客观上讲,C向8051 MCU移植的难点不少。如:

8051的非冯·诺依慢结构(程序与数据存储器空间分立),再加上片上又多了位寻址存储空间;片上的数据和程序存储器空间过小和同时存在着向片外扩展它们的可能;片上集成外围设备的被寄存器化(即SFR),而并不采用惯用的I/O地址空间;8051芯片的派生门类特别多(达到了上百种之多),而C语言对于它们的每一个硬件资源又无一例外地要能进行操作。

这些都是过去以MPU为基础的C语言所没有的。经过Keil/Franklin、Archmeades、IAR、BSO/Tasking等公司艰若不懈的努力,终于于90年代开始而趋成熟,成为专业化的MCU高级语言了。过去长期困扰人们的所谓“高级语言产生代码太长,运行速度太慢,因此不适合单片机使用”的致使缺点已被大幅度地克服。目前,8051上的C语言的代码长度,已经做到了汇编水平的1.2~1.5倍。4K字节以上的程度,C语言的优势更能得到发挥。至于执行速度的问题,只要有好的仿真器的帮助,找出关键代码,进一步用人工优化,就可很简单地达到十分美满的程度。如果谈到开发速度、软件质量、结构严谨、程序坚固等方面的话,则C语言的完美绝非汇编语言编程所可比拟的。今天,确实已经到MCU开发人员拿起C语言利器的时候了。

下面结合8051介绍单片机C语言的优越性:

不懂得单片机的指令集,也能够编写完美的单片机程序;无须懂得单片机的具体硬件,也能够编出符合硬件实际的专业水平的程序;不同函数的数据实行覆盖,有效利用片上有限的RAM空间;程序具有坚固性:数据被破坏是导致程序运行异常的重要因素。C语言对数据进行了许多专业性的处理,避免了运行中间非异步的破坏;C语言提供复杂的数据类型(数组、结构、联合、枚举、指针等),极大地增强了程序处理能力和灵活性;提供auto、static、const等存储类型和专门针对8051单片机的data、idata、pdata、xdata、code等存储类型,自动为变量合理地分配地址;提供small、compact、large等编译模式,以适应片上存储器的大小;中断服务程序的现场保护和恢复,中断向量表的填写,是直接与单片机相关的,都由C编译器代办;提供常用的标准函数库,以供用户直接使用;头文件中定义宏、说明复杂数据类型和函数原型,有利于程序的移植和支持单片机的系列化产品的开发;有严格的句法检查,错误很少,可容易地在高级语言的水平上迅速地被排掉;可方便地接受多种实用程序的服务:如片上资源的初始化有专门的实用程序自动生成;再如,有实时多任务操作系统可调度多道任务,简化用户编程,提高运行的安全性等等。

看完这篇,轻松弄懂STM32 C语言变量的定义和初始化

我们今天探讨C语言变量的定义和初始化。那么我们首先要明确三个问题。第一,我们要明白什么是变量,或者为什么C语言一定要有变量;第二个在C语言中如何去表达这些变量,或者说C语言都有什么类型的变量如何定义这些变量;第三,变量为什么要初始化,以及如何初始化。

第一个问题,关于变量,一个最通俗的理解就是变化的量。本来外在的物质世界就是在不断变化的,不是有句话么:“唯一不变的就是变化”。C语言作为描述客观世界变化的一种语言,首先就是要有能够对外界事物变化状态量化的工具,那么这个工具就是变量。数字世界,首先就是量化,这个是一切后续工作的基础。

我们下面拿几个常用的变量类型进行说明,比如char型变量,它主要是用来应对0 – 255之间变化的事物的,比如字符什么的。比如float,浮点型的变量,它主要是量化客观世界中模拟量的事物,比如汽车的速度、太阳光的强度等等;再比如int型的变量,它的描述范围就比char型大得多了,它主要是应对整数变化的客观事物的,比如学生的个数、苹果的个数等等。

那么实际上,我们说C语言的变量远不止这些简单的数据类型,是吧。我们还有数组,结构体,还有指针、栈、链表等等。每种数据类型的出现都是为了解决一个量化的问题,比如指针,它主要是定位量化计算机中内存寻址问题;比如结构体,它的定位主要是用来描述复杂事物的,就比如汽车,它不仅有行驶的速度,还有轮子的个数,椅子的个数等等;再比如栈,它的主要作用是解决任务切换以及函数调用时,程序现场的保护问题。

那么也就是说每一个变量类型或者量化工具类型的出现,都是有原因的,都是为了解决实际问题的。当我们从这个视角去看这些变量类型和必要性的时候,我们的理解就会深刻很多。举个例子,比如面向对象数据类型的产生,就是把方法或者函数集成到了一个类型中,这样就可以更为准确的去描述客观事物,比如一个狗狗,它不仅有一条大尾巴,还可以跑得非常快,大尾巴是数据,会跑且跑得快是方法。面向对象的语言比如C++或者Java,就把这些变量和方法封装起来,形成一个新的更为综合的量化工具,那就是对象。

站在C语言的基础上,往上看是C++语言等面向对象的,但是如果往下看,比如到了汇编级别,就是另外一番场景。我们知道汇编语言是最接近机器的语言,对于某一类型的单片机,它一般有几十条特定中汇编指令。但是我们说汇编语言是没有数据类型的,它操作的只有二进制类型的数据,并没有对这些数据进行按照其属性进行分类。没有根据属性对数据进行分类,其实也就是说没有对量化工具进行分类,那么人类的大脑就要耗费更多的能量去理解汇编程序,人的大脑本身都是很懒的,能省能量肯定是想办法节省能量。从这个角度上的看,汇编语言更像是机器语言。

但是我们说电脑本身就是机器,不同汇编语言的指令集才能真正反映各个芯片架构的不同,指令集不同可能对应的电路也是不同的。任何高级语言最后还是要在特定的机器上运行的,那也就是说这些高级语言最后还是要翻译成特定的汇编语言。这个翻译的工作就是编译器要做的事情。另外,软件的开发还要有量好的代码编辑环境以及调试环境(比如支持单步调试,实时查看寄存器及存储单元的数据),所以一款新的单片机是不是好用会有多方面的因素影响的,也不能只看指令集的执行效率。

第二个问题,C语言都有什么类型的变量呢?我们可以用一张表来大概描述一下,下面这张表对C语言的数据类型进行了相对完整的总结。大家可以看一下,整体的数据类型被划分为四类:基本类型,构造类型(组合类型),指针类型还有空类型。基本的数据类型肯定是根本,C语言在级别数据类型的基础上构造出更为复杂的数据类型,用于描述相对复杂的事物,比如结构体等等。那么C语言就是使用这些相对抽象的基本类型,去量化和描述纷繁复杂的外部世界的。我们在前面已经提到了绝大多数数据类型产生的原因,这里就不再赘述了。如果有还不理解的同学,可以自己上网去查一查资料。

从下面这幅图可以看出,不同的基础数据类型器长度是不一样的,而且同一种数据类型在不同的机器和编译器编译下其数据长度也是不一样的。不同的基础数据类型,所占据是二进制数据位数发生不同,这个是可以理解的。对于相对简单的事物,比如字符,本来就不需要使用那么长的位数来表达,这个是基本诉求。再一个,比如对于char类型这种需要较少位数就可以表达的可以量化的事物,如果使用int这种长度的数据去表达,本身也是对计算机存储资源的浪费。基于上述两个原因,才出现了不同的数据类型有不同的长度的现象。我们在实际编程的时候,从设计的角度上来看,肯定是选择使用最少的存储位数来量化自己要描述的事物,这样占用的资源才能是最少。当程序代码行数非常多的时候,这种差异就会相对非常明显了。

下面,我们来探讨第三个问题,变量为什么要初始化以及如何初始化。我们首先解释一下为什么单片机数据最好要初始化。众所周知,变量是存储在RAM中,掉电后即丢失,上电后默认全为0。那么这样的话没赋初值的变量值全为0,这也应该是大家认为理所当然的。但是实际上并不是这样,有些类型的单片机,当单片机复位的时候(包括硬件复位即按下复位按钮,看门狗复位,以及其它软件程序复位),单片机只是重新跳回到main函数开始执行,而并没有清空RAM!所以,那些只是定义而没有赋初值的变量(尤其是全局变量)依然会使用复位前留下来的值!那么这样,程序运行可能就会出现异常的结果,尤其是指针变量。数据有一个初始的值,整个程序也就有了一个初始状态,初始状态确定了,如果程序设计得没有问题,那么就可以按照既定的规则跑下去。如果程序错误发生在初始位置上,那就太可惜了。大家在编程的时候,一定要注意这个现象。

那么下面我们看一下,如何对变量进行初始化。不同的变量类型,初始化的方式肯定是不一样的。首先对于基础的数据类型,可以直接初始化成想要的值即可。那么对于数组、结构体等类型,初始化的方法就具体问题具体分析,各具特色了。我们下面举例子进行说明。

一维数组:

int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

int a[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

int a[10] = {0};

int a[] = {1, 2, 3, 4, 5};

二维数组:

int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};

int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

int a[3][4] = {{1}, {5}, {9}};

/*

1 0 0 0

5 0 0 0

9 0 0 0

*/

int a[3][4] = {{1}, {0, 6}, {0, 0, 11}};

/*

1 0 0 0

0 6 0 0

0 0 11 0

*/

int a[3][4] = {{1}, {5, 6}};

/*

1 0 0 0

5 6 0 0

0 0 0 0

*/

int a[][4] = {{0, 0, 3}, {}, {0, 10}};

/*

0 0 3 0

0 0 0 0

0 10 0 0

*/

字符数组:

char c[10] = {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y'};

char c[2][3] = {{'y', 'o', 'u'}, {'a', 'r', 'e'}};

char c[] = {"I am happy"};

char c[] = "I am happy"; // 可以省略花括号

char c[] = {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y', ''}; // 与上面等价

char *p;

p = "I love China"; // 正确

char c[14];

c = "I love China"; // 错误

c[14] = "I love China"; // 错误

结构体:

struct {

char name[20];

int age;

}student1, student2;

//匿名结构体

struct Student {

char name[20];

int age;

}student1, student2;

//声明结构体

struct Student {

char name[20];

int age;

}student1 = {"xiaoming", 20};

struct Student student1={.age=12}; // C99可以只对age进行初始化,其他变量初始化成零

联合体:

union Data {

int i;

char ch;

float f;

}a = {1, 'a', 1.5}; // 错误,不能同时初a.ch = 'A'; // 正确

对共用体赋值要指明赋值对象,如

a.f = 1.5; // 正确

a.i = 40; // 正确

a = 1; // 错误,没有指明赋值对象始化3个

union Data a = {16}; // 正确

union Data a = {.ch='j'}; // 正确 C99新增

枚举:

第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。

声明

enum Weekday {sun, mon, tue, wed, thu, fri, sat};

定义

enum Weekday workday, weedkend;

赋值

enum Weekday {sun=7, mon=1, tue, wed, thu, fri, sat};

// sun=7, mon=1, tue=2, wed=3, thu=4, fri=5, sat=6

把上述三个问题到搞清楚后,我们的这篇文章到此就结束了。

参考资料

(复制链接在浏览器打开)

① C语言数据类型总结

https://blog.csdn.net/xingjiarong/article/details/46942649

② C++继承

https://www.runoob.com/cplusplus/cpp-inheritance.html

③单片机C语言探究--为什么变量最好要赋初值

https://blog.csdn.net/weixin_34342207/article/details/92999746

④ C语言-定义与初始化总结

https://blog.csdn.net/syzdev/article/details/103532435

相关问答

单片机 C语言 编程很好学,为什么还要用汇编呢?

现在一般单片机都支持C语言和晦涩难懂的汇编语言,在许多介绍单片机应用技术的教材中有相当一部分是用汇编语言编写的这说明汇编语言在学习单片机和编写单片机程...

请问有谁会用 单片机 汇编语言 编一个小型数字程控交换机的程...

[回答]每一种系列不同的单片机产品都有自己不同的汇编指令集。8051的汇编是intel公司51系列单片机汇编集最基本的指令集,使用这个汇编指令集的常见的单片...

51 单片机 的程序是怎么制的?

51单片机程序是通过编写汇编语言或C语言代码来实现的。首先,需要了解51单片机的指令集和寄存器,以及外设的操作方式。然后,根据设计需求,编写相应的程序代码...

如果有配套的编译器,JAVA 语言 可不可以编写 单片机 程序呢?

这个评估还是把单片机字长,操作系统,指令系统给忽略的情况下做出的。说到Java的本质,就是运行在哪里,就要把编译器安装到哪里的累赘语言。此外,由于Java使用...

如何把程序写到 单片机 ?

程序写入单片机的过程一般被叫做烧录,烧录的实际过程是:1.通过编译器编译生成单片机能够识别的执行程序2.通过上位机按照某种通讯协议,把二进制可执行文件...

单片机 介绍表格?

单片机详细介绍STC89C52是一种带8K字节闪烁可编程可檫除只读存储器(FPEROM-FlashProgramableandErasableReadOnlyMemory)的低电压,高...

AT89 C 51 单片机 主要功能有哪些啊?

AT89C51是一种带4K字节闪存可编程可擦除只读存储器(FPEROM—FlashProgrammableandErasableReadOnlyMemory)的低电压、高性能CMOS8位...

单片机 除法指令计算公式?

单片机除法指令的计算公式如下:结果寄存器(quotient)=被除数寄存器(numerator)/除数寄存器(denominator)其中,被除数寄存器存储被除数的值,除数寄存...单...

目前常用的 单片机 型号有哪些?都有什么优缺点?

现在市场上的单片机型号可以说是非常多的,其中有通用型的单片机,还有专用型的单片机。专用型单片机一般我们不常见到。今天我们结合这个问题来介绍一下目前常...

单片机 中的代码密度指的是什么?

同样的一段代码,对于不同的单片机来说,编译之后多数情况下占用的空间是不相同的。占用的空间越大,则说明代码密度越低,反之异然。代码密度与编译器有关,对...

猜你喜欢