一.C语言引入
作为一个嵌入式工程师,说起c语言一定不会陌生,之前有位老师说越接近底层程序越高效,处理器越喜欢,相反人编程难度就大了许多,现在有许多高级语言,包括现在大火的python几行代码就能搞定,所以C/C++ CPU执行效率更高,python编写效率更高,所以根据不同的应用场景选择合适的语言。
发展历史:
贝尔实验室 UNIX c语言
接下来你会觉得我会说if else 然后 数组 指针,念书式的顺一遍,当然不是啦,通过这么长时间的工作,发现数组,指针,结构体,内存分配等知识越来越重要,现在我们着重说说这部分,老说些基础的知识,应用的时候还是不知道咋用。
二.C语言的内存管理模型
在以前的学习中,我们也学习过,c/C++定义了4个内存区间
代码区 静态变量区(全局变量) 栈区(局部变量) 堆区(动态存储区)
栈区:由编译器自动分配内存,存放函数的参数,局部变量,自动变量,自动回收。
堆区:由程序员分配释放(malloc, free)
静态区:所有的 全局变量+静态变量(static)+字符串常量。
代码区:所有可执行代码(指令,常量字符串)加载到代码区
注:栈区生命周期:函数调用结束,内存空间就会释放
静态区生命周期:整个程序结束内存才会释放。(整个源程序)
这里主要说明要了解局部变量,全局变量,静态变量等的生命周期,在应用中指针操作,可能语法上并不会出错,但是执行结果是错误的,找的时候够你受的。
就加了一个static,变量就从栈区的生命周期变为了静态变量的生命周期,加static就运行正常,不加就打印数据错误。
以后我们在编程的时候就能做到心中有数,定义什么样的数据类型,他们都运行在什么样的内存空间,使内存得到充分的合理的利用。
比如我们明确数据占用多少内存,那么数据量小的时候我们用栈,较大时候用堆
不知道数据量大小,可能较大时,用堆
需要动态创建分配,用堆。(防止越界访问)
P作为一个指针放在栈上,执向堆空间上的内存地址,当输入超过了堆的内存分配空间,P就会出现了一个越界访问,出现报错。
还有就是我们写了一个递归函数,如果没写好出口,就会容易出现栈溢出错误。
内存分配举例:
三.应用中的思考
在我们的单片机编程中,程序通过软件进行编译输出如下信息
Program Size: Code=10904 RO-data=940 RW-data=40 ZI-data=1736 (stm32程序)
Code是代码占用的空间;
RO-data是 Read Only 只读常量的大小,如const型;
RW-data是(Read Write) 初始化了的可读写变量的大小;
ZI-data是(Zero Initialize) 没有初始化的可读写变量的大小。ZI-data不会被算做代码里因为不会被初始化;
烧写的时候是FLASH中的被占用的空间为:Code + RO Data + RW Data
程序运行的时候,芯片内部RAM使用的空间为: RW Data + ZI Data
比如我们经常遇到的内存溢出错误就是RW Data + ZI Data 大于了我们芯片的内存空间。
四.详细理解动态内存分配
在程序中我们经常用到动态内存分配,那么详细说一下动态内存分配。
我们在实际的代码开发中,有些操作对象只有在程序运行时才能确定,所以需要动态内存分配。所有的动态内存分配都是在堆上进行的。
这里我们就会想到很熟悉的两个函数,malloc free两个函数,动态内存的生命周期由我们决定,有了掌控权感觉自己拽拽的,但是也导致程序出现很多bug,还有很多隐患。所以要求在使用的时候,理解要深入,编写要规范。
堆需要显示释放,也需要显示初始化。(自己编程控制)
例:
Char *p
P =(char *)malloc(10*sizeof(char)); //显示转换为所需要的指针类型
If(p == NULL){ return 0}; //判断是否内存申请成功,以前都忽视,不然傻呵呵做半天,内存都 没成功,做毛线啊。
Free(P); //一般我们申请了内存就要释放,不然就会内存占用越来越多,同时释放一部分内 存是不被允许的。
现在我们分配了10个字符的内存空间,假如现在我们输入超过了分配的大小。
P指针位于栈上,指向位于堆上的内存地址,超过分配大小,P还会继续向下指,就会出现越界访问。所以在编程的时候要注意。
Free(P)实际上删除的肯定就是p指向的堆空间,free成功了,分配的空间肯定就不能使了,这些都很好理解。但是当执行完这个操作,P指针一直保存了分配空间的起始地址(空悬指针),这时候如果不当操作,肯定也会出问题,所以我们在free(P),之后 加P=NULL;操作代码更规范。
只申请不释放,会造成内存泄漏,但是申请了频繁释放也会出现问题。
比如我们free了两次,也会出现问题,当我们释放了一次之后,内存就变为了私有内存了,能够再次被分配,不是你的你在释放,谁不跟你急啊。
所以动态分配的时候,整个生命周期我们要心里有数。
野指针:不是NULL指针,是指向垃圾内存的指针
原因:指针变量没有初始化
P被free后,没有置为NULL
指针操作越界。
这个没有进行初始化,就进行了赋值。
总结:
typedef struct student{
int no;
char name[20];
float score;
}Stu;
Stu * get_info()
{
Stu *p;
if((p=(Stu *)malloc(sizeof(stu)))==NULL){
printf(“malloc failed\n”);
return NULL;
}
p->no =1;
strcpy(p->name,”tom”);
p->score =90;
return p;
}
int main(int argc,const char *argv[])
{
Stu *s;
if((s=get_info())==NULL){
return 0;
}
printf(“student info:%d %s %.2f\n”,s->no,s->name,s->score);
return 0;
}