关键字volatile、const
1 |
这三个宏定义都是用来替换成volatile和coast的,所以我们先了解这两个关键字的作用。
volatile
** 简单的说,就是不让编译器进行优化,即每次读取或者修改值的时候,都必须重新从内测或者寄存器中读取或者修改**。
一般来说,volatile常用在如下的几个地方:
1、中断服务程序中修改的供有其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义。
**CONST
** 只读变量,即变量保存在只读静态存储区。编译时,如何尝试修改只读变量,则编译器提示出错,就能防止误修改。const和define
两者都是用来定义变量,但是const定义时,定义了常量的类型,所以更精确一些(其实const定义的是只读变量,而不是常量)。#define只是简单的文本替换,除了可以定义常量外,还可以用来定义一些简单的函数,有点类似内置函数。const和define定义的常量可以放在头文件里面。
I:输入口。既然是输入,那么寄存器的值就随时会外部修改,那就不能进行优化,每次都要重新从寄存器中读取。也不能写,即只读,不然就不是输入而是输出了。
O:输出口,也不能进行优化,不然你连续两次输出相同值,编译器认为没改变,就忽略了后面那一次输出,假如外部
在两次输出中间修改了值,那就影响输出。
__IO:输入输出口,同上。
浮点数在内存的存放
浮点数:NUM = (-1)^S * 1.M * 2^(E - 127)

S:符号位,0代表为正数,1代表为负数
E:阶码位,如果是float型E = 8,double型E = 11
M:尾数位,如果是float型M = 23,double型M = 52
示例:求98.46在内存中的存储?
- 第一步,将十进制转换成二进制
98.46 转换二进制:

结果:1 100010 . 0110 0110 0110 0110 0110
- 第二步,根据二进制数求S、E、M
由于98.46是正数:S = 0。
由于小数点向后移动了6位,所以6为阶数,1.M * 2^6,根据1.M * 2^(E - 127),则有E - 127 = 6, 求得E = 133,二进制:E = 10000101。
M就是尾数,即1 100010 . 0110 0110 0110 0110 0110 ,去掉小数点的阶数,小数点向前移动阶数6,1 .100010 0110 0110 0110 0110 0110 ,即 M = 100010 0110 0110 0110 0110 0110(二进制) 。
- 第三步,组合S、E、M得到98.46在内存的存储形式
S = 0,E = 10000101, M = 1000 1001 1001 1001 1001 10
内存储存形式:0 10000101 1000100110011001100110
浮点数的自增陷阱
1.++–适用于浮点数
2.实际不使用浮点数的++–
浮点数 包括float 、double、 long double等等都是近似存储
所以每一个浮点数的值都不是准确的
于是如果采取自加 在一些特殊情况下
a的近似值和a+1的近似值会等同
即自加完全没有效果
如果用在循环中 就有可能导致死循环.
1 |
|
double类型的数据用自增和自减没有啥意义,整型数据的自增和自减的意义是只需要一个指令就可以完成,执行的效率很高。浮点数比较复杂,不可能一个指令就能完成自增和自减,所以并不能提高执行效率
示例:
1 | main() |
float转int型,float x = 1,在内存中32位全部为0,x++后x = 2,在内存中0 00000001 00000000000000阶数为1, x再加1等于3,在内存中0 00000001 10000000000000阶数为1,将这段内存转换成int类型 0 00000001 10000000000000即y的值。
运行结果:以上答案全都不正确

浮点数判断与0的大小
无论 是float还是double类型的变量,都有精度限制。
其中EPSINON是允许的误差(即精度)。
1 |
|
为什么浮点数的精度是0.000001?
因为浮点数在内存中:尾数M最大用23位表示。
单精度8位阶码,1位符号,剩下23位尾数,算出2的负23次方,得到0.00000011920928955078125。
双精度11位阶码,1位符号,剩下52位尾数,算出2的负52次方,得到0.00000000000000022204460492503130808472633361816。
前面0有多少个,就表示能精确到哪一位。
单片机的开发则大不相同,其开发环境中往往不会提供日志接口。单片机的驱动库往往只提供基础的操作外设的接口,如uart,i2c,spi,而不提供高层次的SDK。这就需要我们自己来设计。
