编译器的“智商”你不懂
编译器的智商似乎是很“诡异”的,我想大家对这个结论很少会有异议,无奈我们总归还是要去摸清它的脾气,谁让我们是苦逼的的程序员呢?外国有句老话说得好:要理解别人的想法,最好“穿别人的鞋”。
和编译器同穿一双鞋,就是站在编译器的视角,尝试在保持原有代码逻辑的基础上优化代码,以获取更小的代码尺寸或者执行效率。接下来,我们首先看一个例子:
//! 一个全局变量
extern uint32_t g_wDemo;
//! 有一个函数要用到这个全局变量
void example(void)
{
uint32_t i = 0;
for (; i < 10; i++) {
g_wDemo |= 0x01;
}
}
很显然,如果我们是编译器,重复将普通的全局变量BIT0置位100次这种傻事肯定要优化掉——做一次就够了嘛,别做无用功嘛!结果如下:
//! 优化后的结果
void example(void)
{
//uint32_t i = 0;
//for (; i < 100; i++) {
g_wDemo |= 0x01;
//}
}
干得漂亮,我们继续,看下一个例子:
//! 有一个函数要用到这个全局变量
void example(void)
{
uint32_t i = 0;
uint32_t wTemp;
for (; i < 100; i++) {
wTemp = g_wDemo ;
//! 各类针对wTemp的只读操作
...
}
}
这里怎么优化呢?我们注意到g_wDemo在循环中每次都要被读取,单看这个循环,明显只有针对g_wDemo的读取操作,而没有任何对g_wDemo的写入操作——换句话说,在这个循环里面g_wDemo是只读的,内容是不会被修改的!那为啥我每次都要重新读取一遍呢?浪费浪费!优化优化!
//! 有一个函数要用到这个全局变量
void example(void)
{
uint32_t i = 0;
uint32_t wTemp = g_wDemo ; //! 刚开始的时候读一次就够了!!
for (; i < 100; i++) {
//! 各类针对wTemp的只读操作
...
}
}
——“恩,我们又一次拯救了世界。”
——“什么什么?你说我优化错了!”
——“什么?那个全局变量是有来头的?”
——“啥?这个全局变量不是普通的全局变量?你自己写成extern uint32_t g_wDemo,没看到什么特殊的啊?”
——“啥?是一个外设寄存器?那又如何?”
——“啥?这是一个GPIO的Toggle寄存器?每次置位都会取反对应IO?对其读取将获得IO的真实输入电平?”
#define g_wDemo (* (uint32_t *)GPIO_A_TOGGLE_ADDRESS)
——“谁管你!你告诉我这些了?你说,你什么地方告诉我这些了?你说阿!”
——“敢说我们做错了?敢冤枉老子!你找死哦!是你自己没说清楚,怪我咯!你说说你什么地方说过这个变量很重要?你说说什么地方告诉过我,每一次读写都有特别的意义?!”
——“什么?你以为我应该知道的?你不知道要加volatile么?你不给我提示,你让我猜阿!我怎么知道那个地址对应哪个寄存器?我怎么知道你脑子里想什么?!你知道这个全局变量是外设寄存器,我又不是你肚子里的蛔虫?老子辛辛苦苦的给你优化半天,你一个想当然 ‘以为我应该知道’ 就把责任都丢给我了?我凭什么应该知道?我要是能替你决定还要你做萨子!?”
#define g_wDemo (* (volatile uint32_t *)GPIO_A_TOGGLE_ADDRESS)





