第二章 单片机中c的基础知识
文章目录
前言
单片机入门之前,c语言的学习也越来越重要,当然我们可以在学习单片机的过程中,慢慢的完善c语言的知识,本文就绍了单片机学习需要c语言的基础知识。
了解位、字节、字的关系。
加强认识和理解栈的使用过程是RTOS多任务的核心,堆比栈较简单。
本文档参考韦东山老师课程讲解。
一、语法基础资料
https://book.douban.com/subject/4279678/
二、基础知识
1.预处理
#include<stdio.h>
#define GPIOC (GPIO_RegistStruct*)GPIOBase0400010001
#if
...
#elseif
...
#else
...
#endif
2.数据存储
static
extern
const
volatile
3.数据结构
struct
enum
union
typedef
4.数据类型
char
short
int
long
signed
unsigned
float
double
sizeof
5.运算和控制
=
+
-
*
while
do-while
if-else
switch-case
continue
break
return
6.位操作和逻辑运算
<<
>>
&
|
~
^
三、位、字节、字的关系
1.位(bit)
计算机中最基本的单位, 取值0或1
2.字节(Byte)
计算机中数据存储的基本单位, 8位=1字节
3.字(Word)
计算机进行数据处理和运算的单位,
32位机中,4字节=1字;
64位机中,8字节=1字;
四、栈和堆
1.堆
堆比栈较简单
堆就是一块空闲的内存,你可以管理内存,从这一块内存中来取出一部分,用完后再释放回去,
比如:定义char buff[1024],他就是一块空闲的内存,只要在它上面实现内存的分配和释放,该数组就是一个堆。
我们来实现一个简单的函数
char buff[1024];//1.先在buff中分配一小块内存,下标就是从0到1023
char * buf_pos=0;//2.定义一个buf_pos=0,让这个首位置返回去
//使用一个整数buf_pos=0表示一块空闲内存的位置
void *malloc(int size)//分配size大小的空间
{
int old_pos=buf_pos;//5.使用一个整数old_pos表示一块空闲内存的位置
buf_pos+=size;//4.让这个buf_pos等于原来的位置加上这个size
return &buff[old_pos];//3.返回buf_pos这个位置的地址,然后还要调整一下这个地址
}
Malloc函数比较简单,就是先在buff中分配一小块内存,下标就是从0到1023。
如果我们想分配size大小的空间,怎么分配呢?
定义一个pos=0,让这个首位置返回去,然后让这个pos等于原来的位置加上这个size,
当我们在这块空闲的buff内存上实现malloc时,这块内存成为堆。
void free(void*buff)//假装我们在buff实现了free
{
/*err8/
}
使用一个整数pos表示一块空闲内存的位置。
使用这种最简单的方法不能实现free函数,如果要释放掉你分配掉的那块内存,假装我们在buff实现了free,就可以在主函数中使用了。
int main()
{
int i;
char *buf=my_malloc(100);//然后在里面添加内容,先定义一个i
for(i=0;i<26;i++)
{
buf[i]=’A’+i;
}
}
然后使用debug模拟器仿真一下,数组buff[]里面是否存入数据。
如果使用中文目录退出debug时会报错。
2.栈
栈是RTOS的基础,离开栈是不行的,每个任务他都要有自己的栈,互相独立。
栈的使用过程是RTOS多任务的核心,在后面创建任务时你会发现,你要创建一个任务,你必须给这个任务单独分配栈。
栈的作用:
栈是一个幕后英雄,在我们编写程序的时候感觉不到栈的存在。
我们写一个简单程序,让main函数调用a_test(),a_test()中定义一个vale值,a_test()再调用b_test()和c_test()。
void c_test()
{
}
void b_test()
{
}
void a_test()
{
int num=5,vale;
num=num+vale;
b_test();
c_test();
}
int main()
{
a_test();
return 0;
}
在这个过程中没有感觉到栈的存在,但是我来问几个问题,
第一个问题:从C语言中可以直接看到返回到哪里,但是返回地址保存在哪里?
比如main函数他怎么调用a_test()函数,我得记录下a_test()的返回地址。
第一步:他把返回地址保存在某个寄存器里面,LR(link register)讲ARM架构时会讲到这个寄存器的,可以保存一些值,main函数调用a_test()函数之前,他会把a_test()函数下一句要执行的语句保存,LR=a_test()函数执行完后下一语句的地址,return 0;
第二步:然后干嘛呢?然后调用a_test()函数。
同理,a_test()函数调用b_test()函数时,他也是一样的,做同样的事情,先保存b_test()函数下一句要执行的语句返回地址值保存,LR=b函数执行完后下一语句的返回地址,然后调用b函数。
那么问题就来了,a_test()函数执行完后下一语句的地址保存到LR里面,b_test()函数执行完后下一语句的地址也保存到LR里面,LR被覆盖?
如果你不做处理的话,LR肯定被覆盖,那怎么处理呢?简单。
在a_test()函数内部,他要把LR的值保存起来,把LR存入栈中,后面调用b_test()函数时,LR就不怕被覆盖掉了,函数b他也会在函数b_test()内部也把返回值保存到栈中。
你看,第一个问题不就解决了,返回位置保存到每个任务自己的栈中。
什么是栈,栈就是一块空闲的内存,比如打开汇编代码,他在执行main函数之前,他要使用汇编代码设置这个栈,SP就是栈寄存器,让他指向一块空闲的内存,然后就可以调用c函数了。
第二个问题:整个使用栈的过程是怎么样的呢?
根据C函数开头知道:
1、先划分栈,(划分出来的栈用来保存LR等寄存器,还有局部变量)
2、显然LR等寄存器存入栈
3、执行代码。比如num=5的话,不是刚刚给num分配了栈空间了吗?把8这个值写到那个栈空间里面去。
BL main会把下一条指令地址保存到LR里面,所以说BL main他会做两件事情,
第一件LR=返回地址,第二件然后执行main函数
,main函数会做什么事情呢?
main函数他会划分出自己的栈空间,SP=SP-N,这N字节的内存就是main函数自己的栈(main’s stack),在里面会保存什么呢?
会保存LR等寄存器,还会保存局部变量。
a_test()\b_test()函数与主函数类似,从这个过程中可以发现,栈在保存LR返回地址时是怎么起作用的。栈对局部变量起作用也是分配局部变量空间,在运行到赋值指令时,让局部内存的值变为5,在运行到局部变量加vale的时候,他就让5+vale.
总结
了解位、字节、字的关系,加强认识和理解栈的使用过程是RTOS多任务的核心,堆比栈较简单。