《 C 语言的一些“骚操作”及其深层理解》之字节快速位逆序
扫描二维码
随时随地手机看文章
字节快速位逆序
我给大家出一道有意思的题目:如何快速得到一个字节的位逆序字节。比如0X33的位逆序字节是0XCC。
有人给了我这样一段代码:
这段代码很简洁,也很巧妙。但是它却不是最快的。后来作了改进:
这样把循环打开,确实会提速不少。但它仍不是最快的实现方案。请看如下代码:
恍然大悟了没有?使用字节数组事先准备好位逆序字节,然后直接以字节的值为下标索引,直接取数据即可。这种方法被称为“空间换时间”。
这个问题我问过很多人,多数人并不能直接给出最佳方案。倒是有不少人问我这个问题有什么实际意义,为什么要去计算位逆序字节?请大家想想,如果我们把电路上的数据总线焊反或插反了该怎么解决。
关于volatile
现在的编译器越来越智能,它们会对我们的代码进行不同程度的优化。请看下例:
unsigned char a;
a=1;
a=2;
a=3;
这样一段代码,有些编译器会认为a=1与a=2根本就是毫无意义,会把它们优化掉,只剩下a=3。但是,有些时候这段代码是有特殊用途的:
unsigned charxdata a _at_ 0X1111;
a=1;
a=2;
a=3;
a不单单是一个变量,而是一个外部总线的端口(51平台)。向它赋值会产生相应的外部总线上的时序输出,从而对外部器件实现控制。这种时候,a=1和a=2不能被优化掉。举个例子:a所指向的外部总线端口,是一个电机控制器的接口,向它写入1是加速,写入2是减速,写入3是反向。那么上面的代码就是加速->减速->反向,这样一个控制过程。如果被优化的话,那最后就只有反向了。
为了防止这种被“意外”伦的情况发生,我们可以在变量的定义上加一个修饰词volatile。
volatile unsigned charxdata a _at_ 0X1111;
a=1;
a=2;
a=3;
这样,编译器就会对它单独对待,不再优化了。
volatile最常出现的地方,就是对芯片中寄存器的定义,比如STM32固件库中有这样的代码:
#define __IO volatile
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
这是对STM32的GPIO寄存器组的定义,每一项都是一个__IO类型,其实就是volatile。这样是为了对片内外设的物理寄存器的访问一定是真正落实的,而不是经过编译器优化,而变成去访问缓存之类的东西。