前言:
本人于大学接触的第一门语言为C++,对C的一些语法有些不了解,为便于嵌入式学习,特开此篇。
第一章、数据类型:
基本数据类型:
int——整型,4字节;
char——字符型,1字节;
float——单精度浮点数,4字节;
double——双精度浮点数,8字节。
long (int)——长整型,4或8自己取决于几位机。
在使用一个变脸时,需要先定义类型,来为该变量分配一个内存空间来存储信息。
数组:
为了方便地定义多个相同类型地变量,引入数组的概念。
数组是顺序存储的。格式如下:
数据类型 数组名称[个数];
如此,便创建了三个数组,a为整型数组含10个元素,b为字符型数组含20个元素,c为双精度浮点数型数组含20个元素。
以上为一维数组,下面介绍二维数组:
数据类型 二维数组名[n][m];
二维数组可以当作是一个矩阵,第一个[]内的为行参,第二个[]为列参;可以看作是一维数组的一维数组。
下面介绍数组的初始化:
若您想深入了解,可看我往期书写的博客。
指针:
指针定义、赋值、通过地址取值的方式:
为什么要使用指针呢?
除了上述存放的基本数据外,还可以存放地址(相当于你寝室(空间)的门牌号(地址),通过门牌号能找到你的寝室(空间或数据))。如下图所示,通过地址能够访问到a变量的值:
那么左边那块0x0523的地址变量应该要怎么定义呢?语法如下:
数据类型* 指针名(ptr);
数据类型与要执行的地址所属于的变量类型相同。
知道了指针如何定义后,下面介绍如何初始化指针的值:
数据类型* ptr = &变量;
&为取地址符号,不为右值(简单理解可以记为不在赋值符号右侧)时表示引用(下面介绍)。
知道了如何初始化后,下面介绍如何取出指针变量里面的值:
*ptr;
简单理解,ptr中存放的是地址,而*号代表钥匙,使用*ptr才能去访问该地址中存放的数据。
指针与一维数组:
首先我们要知道,数组名代表着什么,数组名实际存放的是数组的首元素的地址。那么我们可以定义指针如下:
这样有什么好处呢?如此定义指针之后,访问数组的元素就可以分为以下两种方式了:
1、数组名(地址)[下标(index)或者你可以理解为偏移量];
2、*(ptr + 下标(index))
下面的表格第一行为建立的数组a[5],内部元素的值同上的代码图,表格中每列代表的含义相同。
1 | 2 | 3 | 4 | 0 |
a[0] | a[1] | a[2] | a[3] | a[4] |
*(a+0) | *(a+1) | *(a+2) | *(a+3) | *(a+4) |
ptr[0] | ptr[1] | ptr[2] | ptr[3] | ptr[4] |
*(ptr+0) | *(ptr+1) | *(ptr+2) | *(ptr+3) | *(ptr+4) |
当然,除了以首地址(数组名)开头,如下的访问也是可以的,只要保证[]前的是地址,内部参数为偏移量即可:
如要访问a[4]的内容,我们也可以写成(a+4)[0],其本质是无区别的。
指针进阶(有空更新暂不写):
结构体:
结构体类型的定义:
为什么需要结构体?如果你学过C++,你可以简单地将其看做是只有public的Class。下面给出为什么需要结构体的解释:
假如现在程序编写中有n个学生,他们都有姓名,学号,性别等,如果按照我们之前提到的数据类型去存储这些信息会十分繁琐,且信息之间是孤立的。但是使用结构体就能够比较好地来存储这类信息。(struct的定义放在main函数外一般习惯)。
如上定义我们便可以使用结构体student来处理学生的信息啦。
下面介绍结构体变量的创建:
上文中我们创建了一个抽象的学生struct student类型,我们可以用该类型去定义变量,从而实现对学生的信息的管理:
但是如上定义结构体,每次定义变量时都要使用struct student,实在太过于繁琐,所以我们通常这样定义:typedef——给一个类型取一个别名,下文就是将struct student取别名为stu。
结构体变量中属性的获取:
首先我会给出一个单链表的数据结构的struct如下:
对上述别名的解释,其分为两部分:
typedef struct LNode LNode;
typedef struct LNode* Linklist;(即Linklist为结构体指针类型)
下面介绍结构体与结构体指针的成员如何调用:
1、结构体:结构体变量名.结构体成员;
2、结构体指针:结构体指针变量名->结构体成员;
第二章、输入与输出:
基本输出printf:
上文中多次使用了printf()将内容输出到屏幕上,下面简单介绍printf的使用方法:
1、d格式字符:
用于整型数据的输出。
当然也可以在%和格式字符间插入格式修饰符,如%5d表示指定输出的数据占5列且自动向右靠齐(空白(若有)在左,数据靠右),左对齐就加个-号。
2、f格式字符;
用于实数类型的输出,默认实数部分全部输出,小数部分保留6位;
控制小数部分的方法为在%和f间加入.n(n为要保留的位数):
3、c格式字符:
用于输出一个字符;
4、s格式字符:
用于输出一个字符串;
当有多个占位符时,从左往右依次匹配对应的。
基本输入scanf:
当我们想要将一个数据写入到变量中时,我们需要找到他的地址,因此需要&符号来取地址(本身为指针就不用取地址啦);
其格式字符同上,不再赘述;
这里面的19为限制长度最大为19。(与上文结构体中的定义有关)。
第三章、运算符与表达式:
算数运算符:
+、-、*、/、%、++、--;
+、-、*的运算没有什么好解释的,就略过不谈;
/当类型均为整型时,为整除,即没有小数点(直接去尾);
%为去余运算,只能对整形数据;
++:分为前置与后置,当前置时,先自增再返回;否则先返回再自增。
--:同上。
关系运算符:
>(判断是否大于)、<(判断是否小于)、==(判断是否等于)、>=(判断是否大于等于)、<=(判断是否小于等于)、!=(判断是否不等于);
需要注意的是关系运算返回的结果为bool值(True or False,非0看为真)。
逻辑运算符:
!(非运算,将bool值取反)、&&(与运算,同真为真,否则为假)、||(或运算,有真为真)。
&&和||具有短路效应,当表达式1&&表达式2时,若表达式1已经为false,表达式2是不进行任何操作的;对||而言,若表达式1为true则表达式2是没有任何操作的。
条件运算符:
表达式1 ? 表达式2:表达式3;
解释,当表达式1的结果为真时,取表达式2的值,否则取表达式3的值。
位运算符:
运算符 | 含义 | 运算符 | 含义 |
& | 按位与 | ~ | 取反 |
| | 按位或 | << | 左移 |
^ | 按位异或(不进位的二进制加法) | >> | 右移 |
为什么要位运算符,实际是为了遍历stm32的地址操作更加便利~。
第四章、程序设计结构:
循环结构:
while循环:
语法如下:
其中condition为你要输入的条件变量,code部分为循环体代码;
什么意思呢,就是当condition的值为true(非0)时,循环体会一直执行,直到condition的值为false(0)时终止循环,由此可见如果想要终止循环,condition的值应该在code部分中会发生改变,不然会死循环(当然在stm32中往往是需要写成死循环的,但一般的程序设计是不允许的。)
下面来看while循环的特点,可以发现其是先判断循环条件是否成立再进入循环的,也就是说最小的循环次数可以为0。
for循环:
语法格式如下:
参数解释:size_t可以简单地理解为数据类型,i为循环控制的标记,count为你要循环的次数,i++是为了你能够退出循环。
综上可以看出,while适合于循环次数未知的循环,而for循环则适用于循环次数已知的循环(例如数据的输入之类的)。循环的嵌套我就不展开说了,了解了while和for能看懂的。
选择结构:
if...else:
语法如下:
当condition为true时,执行if下的code片段,否则执行else下的code片段。
其也是可以嵌套的,else自动与最近的if语句组合。
switch...case:
语法如下:
这个是什么含义呢,首先通过expression从外界获取一个值,将该值与各个case的值进行匹配,从最上方依次往下匹配,当遇到相等的case时,执行其下方的code,然后通过break跳出循环,否则继续往下遍历,直到default(该处也可以有code)。
第五章、函数:
函数的作用:
当一个功能在程序中要多次调用时,引入函数的概念可以避免我们进行多次重复的操作啦~比如说我们需要多次交换两个数的值,我们就可以写一个swap()函数来便于我们的调用。
参数传递、返回值:
函数的返回值:
首先我们要知道一个函数只能返回一个值,其值通过return函数返回;
函数的类型决定了函数的返回值类型;
不需要返回值的函数可以定义为void。
参数的传递:
在我们定义函数的时候,我们定义的实际上是一个形式参数(重要的是类型),我们在调用函数的时候使用的是实际参数,在编译的过程中实参与形参相融合。
那么我们来思考一个问题,为什么我上文中交换两个数的值的形参用的是指针(地址)而不是直接将两个数传入进去呢,这就得要返回我们的函数返回值讲起了。因为只能够返回一个值,显然不可能将转换后的两个值都返回回去,所以不能return回值,可能又有人要问,我以及在函数中将两个值进行了交换,为什么输出的时候发现两个值还是和未交换前一样呢?这是因为在C语言中,参数的传递默认是值传递,其会建立一个副本,在函数调用时是对这个副本进行操作而不是对原来的值进行操作,在函数调用结束后这个副本就消失了,所以输出的结果也就变啦。
因此当函数中有对参数进行改变且想要主函数中同步改变的时候,应该传入指针,在调用时的实参为&参数名。