1、什么是8位,16位,32位单片机一般情况下看各个MCU的寄存器的位数,字的长度,总线的宽度,或者每个地址的存储位数。
例如
1)32位的单片机,在系统.s文件里定义了堆栈大小是1024,那么这个堆栈是多大呢,答案是4k;
2)8位MCU的寄存器看手册,只有8个bit,而32位的MCU一般是32个bit;
当然选用几位机,需要看系统的具体参数要求以及成本。
2、关于结构体的定义在32位机中
如果一个结构体内部包含了8位数据,16位数据,32位数据,那么定义的时候尽量按照每32个字节打一个包开始定义;
据说在分配RAM时可以做到一整个Struct地址的完整性
例如
Struct test{
INT8U A;
INT8U B;
INT8U C;
INT8U D;
INT32U E;
INT16U F;
INT16U G;
};
3、对于一些特殊的变量或者标志位,请用宏定义,另外局部变量,全局变量,命名简单易懂的,这样程序交接维护起来方便;
4、位数据的解码,技巧;
例如:某个数据是通过uart口传送过来的,但是需要对其中的某些位进行解码,可以定义如下:
typedef union
{
INT8U a;
struct{
INT8U ON:1; //开关机状态;
INT8U Status:5; //状态位;
INT8U NoUse:3; //预留未用;
}Bits;
}ComData;
如上可以对数据中的位进行很好的解码;
5、中断接收数据的数组最好定义为Volatile,这样这个数据就不会被优化,以保证数据的实时更新性;
6、对于一些重要的局部变量或者函数最好使用Staic进行修饰,这样这些数据就具有保存特性,同时限定了其使用范围,只在本函数或者C文件中;
7、关于全局变量extern的用法,建议在需要用的c文件处进行extern,这样可以一定程度的避免该全局变量过度曝光,导致数据莫名错误。
以下内容来源网络:
1)能不用全局变量尽量不用,我想除了系统状态和控制参数、通信处理和一些需要效率的模块,其他的基本可以靠合理的软件分层和编程技巧来解决。 2)如果不可避免需要用到,那能藏多深就藏多深。 (1)如果只有某.c文件用,就static到该文件中,顺便把结构体定义也收进来; (2)如果只有一个函数用,那就static到函数里面去; (3)如果非要开放出去让人读取,那就用函数return出去,这样就是只读属性了; (4)如果非要遭人蹂躏赋值,好吧,我开放函数接口让你传参赋值;5)实在非要extern**我,我还可以严格控制包含我.h档的对象,而不是放到公共的includes.h中被人围观,丢人现眼。 3)全局变量是不可避免要用到的,每一个设备底层几乎都需要它来记录当前状态,控制时序,起承转合。但是尽量不要用来传递参数,这个很忌讳的。 4)尽量把变量的作用范围控制在使用它的模块里面,如果其他模块要访问,就开个读或写函数接口出来,严格控制访问范围。这一点,C++的private属性就是这么干的。这对将来程序的调试也很有好处。C语言之所以有++版本,很大原因就是为了控制它的灵活性,要说面向对象的思想,C语言早已有之,亦可实现。 5)当一个模块里面的全局变量超过3个(含)时,就用结构体包起来吧。要归0便一起归0,省得丢三落四的。 6)在函数里面开个静态的全局变量,全局数组,是不占用栈空间的。只是有些编译器对于大块的全局数组,会放到和一般变量不同的地址区。若是在keilC51,因为是静态编译,栈爆掉了会报警,所以大可以尽情驰骋,注意交通规则就是了。
8、数据的强制转换 两个低位数据合并强制转换为高位数据,例如两个16位数据合并成一个32位数据,需要将准备放到高位的数据先进行强制转换,后进行移位; 如果是将32位数据拆成16位,则高位转换需要先移位,再进行数据强制转换;
9、sizeof陷阱,如果sizeof一个字符串,其返回值一定会比真实的字符串数大一个,“\n”;
10、STM32以及在MDK里面段的划分。MDK下Code,RO-data,RW-data,ZI-data这几个段:1) 内存不只分为堆和栈,还有静态存储区(全局变量,静态变量都是从这里分配的) 其中堆是用来分配动态内存的(也就是c中的malloc,free才需要的),如果没用到,堆设置为0也没问题 栈则是局部变量,函数形参所在的位置,当然栈中也可能保存运算的中间值 无论是局部常量,全局常量(包含宏定义,const变量)都保存在ROM中,内存管理虽然在CPrimer Plus中有,但讲的最细的还是<高质量 C++/C 编程指南> 第七章,你可以了解下 2)内存栈指向那里理解倒是没问题,不过你因为代码里面没用到malloc/free,所以编译器并没有分配堆空间,你可以自己在MAP文件看下。 3)code代码区,这就其实就是汇编指令的二进制代码 RO-data只读数据,就是定义的const变量, 可能还包含一些全局宏,字符串等 RW-data已初始化的全局变量,静态变量 ZI-data包含栈、堆空间,未初始化的全局变量,静态变量
局部变量都是在栈里面的,注释不注释当然不会有变化,只要栈分配了,占据的空间就是固定的了我看了下,你代码里面是相互作用了,有些全局变量如果没有被使用到,编译器会直接给优化掉(这个volatile也是阻止不了的,你也可以全局在定义个变量,会发现编译结果没有变化),所以你这测试本身就存在问题。另外初始化为0的全局变量/静态变量,也会算到ZI区域,因此把ZI称为零初始化区域比未初始化区域更准确(可参考下面来自MDK手册的图片)
11、程序若出现变量偶然溢出或者出现错误值,可以打开map文件,找到对应的变量,将上下文中的变量空间进行拓展。 分析map,你会发现是堆和栈占用的,在startup_stm32f10x_md.s文件中,它的前面几行就有以上定义,这下该明白了吧。 Stack_Size EQU 0x00000400 Heap_Size EQU 0x00000200 对于一个C语言程序而言,内存空间主要由五个部分组成:代码段(.text)、数据段(.data)、静态区(.BSS)、堆和栈组成。 BSS段:BSS段(bsssegment)通常是指用来存放程序中未初始化的全局变量和静态变量 (这里注意一个问题:一般的书上都会说全局变量和静态变量是会自动初始化的,那么哪来的未初始化的变量呢?变量的初始化可以分为显示初始化和隐式初始化,全局变量和静态变量如果程序员自己不初始化的话的确也会被初始化,那就是不管什么类型都初始化为0,这种没有显示初始化的就是我们这里所说的未初始化。既然都是0那么就没必要把每个0都存储起来,从而节省磁盘空间,这是BSS的主要作用)的一块内存区域。BSS是英文BlockStarted by Symbol的简称。BSS段属于静态内存分配。BSS节不包含任何数据,只是简单的维护开始和结束的地址,即总大小,以便内存区能在运行时分配并被有效地清零。BSS节在应用程序的二进制映象文件中并不存在,即不占用磁盘空间 而只在运行的时候占用内存空间 ,所以如果全局变量和静态变量未初始化那么其可执行文件要小很多。 数据段:数据段(datasegment)通常是指用来存放程序中已初始化的全局变量和静态变量的一块内存区域。数据段属于静态内存分配,可以分为只读数据段和读写数据段。 字符串常量等,但一般都是放在只读数据段中 。 代码段:代码段(code segment/textsegment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等,但一般都是放在只读数据段中 。 栈区:由系统自动分配,栈区的分配运算内置于处理器的指令集,当函数执行结束时由系统自动释放。存放局部变量。栈的缺点是:容量有限,当相应的区间被释放时,局部变量不可再使用。查询栈容量的命令:ulimits-s。栈是一块连续的区域,向高地址扩展,栈顶和容量是事先约定好的。 堆区:在程序的执行过程中才能分配,由程序员决定,编译器在编译时无法为他们分配空间,只有在程序运行时分配,所以被称为动态分配。堆是不连续的区域,向高地址扩展。由于系统用链表来描述空闲的地址空间,链表的遍历是由地地址向高地址的,故堆区是不连续的动态的存储空间。
12、RAM存放空间分部图:即先存放全局变量等,然后再开辟空间给栈stack存放局部变量,最后是堆heap空间;
|