Re: [问题] Volatile陷阱

楼主: tinlans ( )   2015-08-15 05:44:48
※ 引述《yshihyu (yshihyu)》之铭言:
: http://adrianhuang.blogspot.tw/2011/08/cvolatile.html
: 程式码我是看这网站
你应该去看看它下面的参考资料
【Reference】
[1] How to Use C's volatile Keyword
http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword
至于 blogspot 那篇建议别看,因为那是作者读完参考资料以后自己随便举的例。
: #include <stdio.h>
: int square(volatile int *var)
: {
: return *var **var;
: }
: int main(void)
: {
: int var = 5;
: printf("result: %d\n", square(&var));
: return 0;
: }
这个例子最失败的地方就是放了 main function,
然后 main function 又不经大脑思索就随便乱写。
这代表了这个例子是个完整程式,square() 的唯一 caller 就是 main()。
因为 var 必然是 main() 的 stack frame 里存放 local variable 的某个 slot,
而这个 slot 的 address 是由 compiler 去决定的,
code 里也没任何特殊的元素去指明 var 应该被怎样特别对待,
所以这个 var 的值压根就不会有任何 compiler 意料之外的变动。
既然他根本懒得设计 square() 的 caller,其实他只放这样就能暗示无限多种可能:
int sqaure(volatile int *var)
{
return *var * *var;
}
: 其问题在于square函式的平方算式,*var**var,此指令代表到var位址读取其内容。然而
: ,var位址可能储存硬件暂存器,这些暂存器内容会随时间而改变 (例如: 状态暂存器),
: 有可能第一次读取的时候为4, 下一次读取为5, 导致计算出来的值不正确。
: 可是前面又提到 凡牵涉读取该volatile变量的操作,保证会到该变量的实体位址读取,而不会读取CPU暂
: 存器的内容 (提升效能) , 这样这程式码变量加上 volatile 都会从内存取值,这样不是没问题?
: 怎么感觉讲法有冲突,还是我误解他意思?
:
: 他说正确解法是在square函式宣告一local变量
: int square(volatile int *var)
: {
: int local_var = *var;
: return local_var * local_var;
: }
: 谢谢
所以大家常说要读书就要读外文书或中译本。
因为外国的月亮虽然未必真的比较圆,但是事实上却常常比本国的圆很多很多。
volatile int *var 代表什么意思?
代表 var 是一个 pointer,指向一个 volatile int 的资料。
换句话说是被指之物 (位于内存上) 具备 volatile 的特性,
而不是 var (也就是 pointer 本身) 具备 volatile 的特性。
来读一下该文作者写了什么东西:
“其问题在于square函式的平方算式,*var**var,此指令代表到var位址读取其内容。
然而,var位址可能储存硬件暂存器,这些暂存器内容会随时间而改变
(例如: 状态暂存器),有可能第一次读取的时候为4, 下一次读取为5,
导致计算出来的值不正确。 ”
既然他本段中写到“*var**var”,那么这所指的 var 必为 sqaure() 内的 var。
什么叫“var位址”?这是中文的模棱两可之处,所以中文技术文章不应该用这种用语。
如果解读成“var 的位址”,那么显然不通,因为这段程式没有取 var 的位址。
但是如果读者执意这么认为,他就会把这个当成 main function 的 var,产生混乱。
这个“var位址”真要写的话,应该写成“位址var”比较正确。
而作者之所以这样写,只是想表达 var 是个 pointer 罢了,但根本没必要。
“var位址可能储存硬件暂存器,这些暂存器内容会随时间而改变”这也是很糟的句子。
我不管怎么读,都很像是程式把其它国语言硬翻成中文一样,完全不通顺。
“var位址”是主词,“储存”是动词,“硬件暂存器”是受词。
世界上有什么 pointer 或 address 本身可以拿来“储存”硬件暂存器?没有。
根据他的参考资料,可以发现这段想说的叫做 memory-mapped registers。
因为各位很多是大学直接读外文书,可能很少人知道 memory-mapped 的正式译法。
这个形容词的翻译方式叫“内存映射”,所以上文的“储存”应当写成“映射”。
也就是说,前文的“硬件暂存器”所指的暂存器,并不是很多读者误会的那种暂存器。
每次读取 *var 的动作,可能是从某个周边装置循序读取一笔资料;
每次写入 *var 的动作,可能是对某个周边装置循序写入一笔资料。
意思是 *var 这块空间可能是被投射到该周边装置上的某个暂存器上,
该周边装置可能定时检查这个暂存器的值有无变化,有变化就会进行某些操作;
读取该暂存器的动作,可能触发该周边装置去执行某项操作,然后把一个值更新进去;
该周边装置可能不断在做某件事,不断更新该暂存器的内容,所以值常常读出来不一样。
这文章的作者第一段把两种暂存器混在一起,又没有特别提及 memory-mapped 这关键字,
坦白说我有点怀疑他自己其实不懂,也没读通他参考原文的意思。
不过姑且可以理解成他只读外文书,所以中文文章不知道该怎么写了吧。
据说这是某公司的面试考题,如果这是真的...
我只能说直接把网络上的东西复制下来当考题,这出题者也实在太没诚意了。
作者: firose (guest也是也是也是也是也)   2015-08-15 06:57:00
推~ 之前看到 main 就一直觉得这题很怪 XD
作者: kwpn (ITSST)   2015-08-15 17:11:00
真的是国外的比中文的圆超多,工作数月后就不再看中文书籍了
作者: CLANNAD (-クラナド-)   2015-08-15 20:43:00
大神好久没发文了
作者: askacis (ASKA)   2015-08-16 08:06:00
没错,这题真的烂到不行
作者: name2name2 (yang~hi)   2015-08-16 09:51:00
作者: anyoiuo   2015-08-17 14:51:00
透过WriteProcessMemory API,由不同process去修改var变量var就会有意料之外的变动
作者: cobrasgo (人鱼线变成鲔鱼线,超帅)   2015-08-17 22:49:00
原本有main我实在是看不出有什么问题…不加main就有很多case可以讨论了

Links booklink

Contact Us: admin [ a t ] ucptt.com