1、函数(封装、复用)
功能性:最基本的特性;
扩展性:对于时刻变化的需求易于扩展;
维护性:对于时刻变化的需求易于维护,易于编码变更;
封装性:不要把所有的代码都写到一个文件或者一个函数里边去 模块 函数;
可读性:写的代码让人容易理解,要求足够的注释、结构清晰、多使用宏定义、枚举类型等。
1.1、函数的实际参数与形式参数
函数参数分为形参和实参两种,作用是实现数据传送。
形参出现在函数定义中,只能在该函数体内使用。发生函数调用时,调用函数把实参的值复制1份,传送给被调用函数的形参,从而实现调用函数向被调用函数向被调用函数的数据传送。
1.2、数组作为函数参数
为什么C语言创造者在设计函数时,当数组作为实际参数的时候,函数的形式参数会默认退化为指针类型?
如果传的是整个数组,调用又会开辟空间,栈内存会耗费非常快。作为指针会节省内存空间,地址或者指针只占用有限的内存空间(一般32位机器上只占用4个字节,64位机器上所有的指针类型占用空间,都是8字节)
传数组的时候没有必要完全复制实际参数,是需要将数组的首地址或者指针传入即可。因为数组的其他元素,可通过首地址+偏移找到,所以也要传递数组的大小。
1.3、函数的递归调用
企业编码规范中,一般要求尽量避免使用递归!
1.4、内部变量与外部变量
C语言中所有的变量都有自己的作用域。变量说明的位置不同,其作用域也不同,据此将C语言中的变量分为内部变量和外部变量。
在一个函数内部说明的变量是内部变量,它只在该函数范围内有效。也就是说,只有在包含变量说明的函数内部,才能使用被说明的变量,在此函数之外就不能使用这些变量了。所以内部变量也称“局部变量”。
关于局部变量的作用域还要说明以下几点:
1、主函数main()中定义的内部变量,也只能在主函数中使用,其他函数不能使用。同时,主函数中也不能使用其他函数中定义的内部变量。因为主函数也是一个函数,与其他函数是平行关系。这一点是与其他语言不同的,应予注意。
2、形参变量也是内部变量,属于被调用函数;实参变量,则是调用函数的内部变量。
3、允许在不同的函数中使用相同的函数名,它们代表的对象,分配不同的单元,互不干扰,也不会发生混淆。
4、在复合语句中也可定义变量,其作用域只在复合语句范围内。
尽量少使用全局变量,即使使用了全局变量也进行把它封装起来!
全局变量多了,维护性差!全局变量作用域是整个程序,谁都可以改!
1.5、extern关键字的使用
总结:
1、头文件一般是做外部变量以及外部函数声明用的,如果本文件的一些变量或者函数想给“别人”用的话,就 需要将这些变量或者函数声明到其对应的头文件中。
2、头文件中不建议定义变量!(容易产生变量重定义的错误)
3、变量的声明和定义之间的区别:
变量的定义是需要给变量分配内存空间的,并且可以初始化;
变量的声明是告诉编译器,我有这个变量存在,并不给变量分配内存空间,因此也就不能在声明的时候进行初始化赋值等操作。
1.6、外部函数与内部函数
变量的声明和定义的区别:
声明不分配内存空间,变量定义分配内存空间。
变量定义在头文件中好不好?
首先,编码规范规定,变量的定义一定放在源文件中,声明在头文件。
防止头文件重复包含,应该怎么做?(头文件使用#ifndef #define #endif作用 )
头文件展开多次,变量会多次定义。
2、实践案例
3、内存模型(memory layout)
3.1、内存类型的划分
Stack 栈区(非staic的局部变量,函数参数)声明周期是局部变量定义开始到函数结束。
Heap 堆区(malloc,calloc,realloc等动态分配的内存)声明周期是从malloc动态开辟空间开始,到调用free结束。
静态全局变量(静态变量,全局变量,常量)声明周期是从静态变量,全局变量,常量定义开始,到程序结束。(.bss .data .rodata)
代码区(存放函数体的二进制代码)用于存储程序编译连接后生成的二进制机器码指令的内存区域只读不可以更改。(.text)
其中text表示只读区(.text和.rodata),data为.data初始化的全局变量或静态变量,.bss表示未初始化全局变量或静态变量。dec为前三者的和,hex为dec列的16进制表示。
3.2、RAM和ROM,Flash Memory的物理特性y
RAM又称随机存取器,存储的内容可通过指令随机读写访问。RAM中的存储的数据在掉电是会丢失,因而只能在开机运行时存储数据。其中RAM又可以分为两种,一种时Dynamic RAM(DRAM 动态随机存储器),另一种是Static RAM(SRAM,静态随机存储器)。
ROM又称只读存储器,只能从里面读出数据而不能任意写入数据。具有掉电后数据可保持不变的优点。因此常用存放一次性写入的程序和数据,比如主板的BIOS程序就是ROM存储器。
Flash Memory 由于ROM具有不易更改的特性,后面就发展了Flash Memory。Flash Memory不仅具有ROM掉电不丢失数据的特点,又可以在需要的时候对数据进行更改,不过价格比ROM高。
不同数据的存放位置:
由前面的分析我们知道,代码区和常量区的内容是不允许被修改的,ROM(STM32就是Flash Memory)也是不允许被修改的,所以代码区和常量区的内容编译后存储在ROM中。
而栈、堆、全局区(.bss段、.data段)都是存放在RAM中。
嵌入式中RAM资源比ROM更紧缺!
面试:
1、一个嵌入式系统欢迎语句应该如何定义 下面哪种更好?为什么?
char welcome[] = “welcome”; //.data中
const welcome[] = “welcome”; //.reodata或者text 加上const更好
char* welcome = “welcome”; //.reodata或者text 加上const更好
const char* welcome = “welcome”; //.reodata或者text 加上const更好
首先,在嵌入式系统RAM资源比ROM资源更紧缺。
第一种变量 占据栈资源,存放在RAM中。
第二种是常量 占据在静态全局变量中.rodata,存放在ROM中。
所以第二种更好。
2、char* welcome = “welcome”;
(补充:以上写法和char welcome[] = “welcome”; 两种写法完全不一样 可以通过welcome[0]='D’修改)
可以使用welcome[0] = ‘D’;去修改welcome指向的字符型吗?
不能,因为welcome指向的字符串存放在.rodata(只读)中,不允许修改。
3、某个局部变量定义为Size巨大的数据和结构体,好吗?为什么?
不好,局部变量是分配Stack区,RAM中,空间有限,很有可能造成栈溢出问题。
4、实践作业
修改图形成变量,查看割内存占用大小。