《 C 語(yǔ)言的一些“騷操作”及其深層理解》之字節(jié)快速位逆序
字節(jié)快速位逆序
我給大家出一道有意思的題目:如何快速得到一個(gè)字節(jié)的位逆序字節(jié)。比如0X33的位逆序字節(jié)是0XCC。
有人給了我這樣一段代碼:
這段代碼很簡(jiǎn)潔,也很巧妙。但是它卻不是最快的。后來作了改進(jìn):
這樣把循環(huán)打開,確實(shí)會(huì)提速不少。但它仍不是最快的實(shí)現(xiàn)方案。請(qǐng)看如下代碼:
恍然大悟了沒有?使用字節(jié)數(shù)組事先準(zhǔn)備好位逆序字節(jié),然后直接以字節(jié)的值為下標(biāo)索引,直接取數(shù)據(jù)即可。這種方法被稱為“空間換時(shí)間”。
這個(gè)問題我問過很多人,多數(shù)人并不能直接給出最佳方案。倒是有不少人問我這個(gè)問題有什么實(shí)際意義,為什么要去計(jì)算位逆序字節(jié)?請(qǐng)大家想想,如果我們把電路上的數(shù)據(jù)總線焊反或插反了該怎么解決。
關(guān)于volatile
現(xiàn)在的編譯器越來越智能,它們會(huì)對(duì)我們的代碼進(jìn)行不同程度的優(yōu)化。請(qǐng)看下例:
unsigned char a;
a=1;
a=2;
a=3;
這樣一段代碼,有些編譯器會(huì)認(rèn)為a=1與a=2根本就是毫無意義,會(huì)把它們優(yōu)化掉,只剩下a=3。但是,有些時(shí)候這段代碼是有特殊用途的:
unsigned charxdata a _at_ 0X1111;
a=1;
a=2;
a=3;
a不單單是一個(gè)變量,而是一個(gè)外部總線的端口(51平臺(tái))。向它賦值會(huì)產(chǎn)生相應(yīng)的外部總線上的時(shí)序輸出,從而對(duì)外部器件實(shí)現(xiàn)控制。這種時(shí)候,a=1和a=2不能被優(yōu)化掉。舉個(gè)例子:a所指向的外部總線端口,是一個(gè)電機(jī)控制器的接口,向它寫入1是加速,寫入2是減速,寫入3是反向。那么上面的代碼就是加速->減速->反向,這樣一個(gè)控制過程。如果被優(yōu)化的話,那最后就只有反向了。
為了防止這種被“意外”倫的情況發(fā)生,我們可以在變量的定義上加一個(gè)修飾詞volatile。
volatile unsigned charxdata a _at_ 0X1111;
a=1;
a=2;
a=3;
這樣,編譯器就會(huì)對(duì)它單獨(dú)對(duì)待,不再優(yōu)化了。
volatile最常出現(xiàn)的地方,就是對(duì)芯片中寄存器的定義,比如STM32固件庫(kù)中有這樣的代碼:
#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;
這是對(duì)STM32的GPIO寄存器組的定義,每一項(xiàng)都是一個(gè)__IO類型,其實(shí)就是volatile。這樣是為了對(duì)片內(nèi)外設(shè)的物理寄存器的訪問一定是真正落實(shí)的,而不是經(jīng)過編譯器優(yōu)化,而變成去訪問緩存之類的東西。