C_and_CPP 板


LINE

2009/06/20 AM 05:48 「组语注」部份补充:lea 指令 2009/06/20 AM 04:01 名词修正:原文提到 dereference 是我一时没想清楚,容易混淆 以 CPU 的观点,变数名称 arr 只是代表一个记忆体位址,比如 0x12345678,从这位址 取出内容值,比较好一点的说法是 CPU 对 arr 做 Load Memory (to register)。以下 简写为 LM。 -- 假设在档案 1.cpp 定义初始化 int arr[2] = {100,200}; 在另一个档案 src.cpp 用 extern 连结此阵列时,可以欺骗 compiler 做出错误操作。 #include <stdio.h> extern int *arr; int main(void){ printf("arr = %d\n", arr); //印出100,而非位址 printf("arr[0] = %d\n", arr[0]); //Segmentation fault (存取违规) return 0; } 此例,当要把 arr 当作参数传给 printf 时,编译器会用以下指令来取 arr 的值: mov eax,dword ptr [arr] mov ecx,dword ptr [eax] 也就是先把 arr 存的值当做「位址」丢给 eax,再从 eax 做 「LM」 把值给 ecx 但是把 arr 存的值 (第一个值) 100 当记忆体位址不是我们要的,所以才会存取错误。 -- 组语注:对 masm 组合语言来说,符号加上 [] 就是做「LM」,对 nasm 来说亦同。 如果要得到符号代表的记忆体位址,也就是 C/C++ 里的 &arr, 那 masm 就要用 offser arr 来表示。 但 nasm 是采用直接写 arr 去掉 [] 来代表 &arr。 另外在VC下中断点後,就可按右键选「移至反组译码」跳到组合语言视窗。 里面写到符号比如 arr 时,会在旁边标他的 offset arr 值,如 arr (1234h)。 # 额外注意的是,像这样一个符号旁边旁标了 offser 位址,表示可在 程式执行前就知道其记忆体位址 (当然 relocation 是没有的)。换言之,因为 arr 不是产生在堆叠里的变数,所以编译器在产生执行档前就能决定其数值。 当有一个区域变数比如 void foo(void){int a = -1;} 时,我们没有办法事先 知道记忆体位址 (&a),没有办法用 offser a 获得。因为函数 foo() 在被呼叫 之前的 ESP 值是未知的,要到 Runtime 了才可决定。因此要获得区域变数 的记忆体位址,应改用另外一个指令 lea eax, [a] 去将区域变数 a 的位址在 Runtime 时由 CPU 去计算出来。(Load Effective Address) 注意 a 要加上 [] 才正确。比如自行在 C 里写 __asm{ lea eax,x } 还是会被 编译器把你那行改成 lea eax,[x] 来执行 (至少 VC 测试过是这样)。 按 ctrl+g 後输入位址可以跳到该位址去,比如跳去看副程式,或观看资料。 alt+5 可叫出暂存器视窗。alt+6 可叫出记忆体视窗,比如在上面打 ESP,然後 选右边的「自动重新评估」按钮,就可以持续追踪堆叠的记忆体内容。 当然基本的指令追踪快速键还是跟以前一样 F10、F11(会跳进去副程式追踪) -- 当程式码如下时,并不会有错误 int arr2[2]={0,0}; int *ptr = arr; printf("val = %d", ptr[0]); 「= 运算子」一定先会等右边的 arr 取出值後,才把该值传给「另外一个变数」(ptr) 我们知道阵列名称跟其他变数都不一样,对阵列名称取值是得到「所在位址」 若有 int val = 3; 对 val 取值是得到 「所在位址内的存放值」 那 extern 欺骗了编译器,告诉编译器:arr 不是阵列名称 所以当我们如法炮制,要取值给「另外一个变数」ptr 时,就会取出错误数据。 一般没有办法对变数做重新解读,只能将变数的值取出後,才用 static_cast 去重新解读之:int *p = 0; int a = (int)p; -- 利用 extern 可以对全域变数「重新宣告」资料型态一次。但怪就怪在 不能把全域变数 double a = 0; 重新宣告成 extern int a; 却可以把阵列错误的重新宣告成指标,导致潜在的错误出现。 当然一般情况都是老实的照着原本的型态宣告而已,不太需要担心。 不过,在 VC9 的 Release 模式 Build 专案时,有看到以下警告 (Debug 模式无警告) warning C4743: 'int * arr' 在 1.cpp 及 src.cpp 中有不同的大小: 8 和 4 位元组 warning C4744: 'int * arr' 在 1.cpp 及 src.cpp' 中有不同的型别: 'array (8 bytes )' 和 'pointer' -- 结论:阵列名称跟指标我们可以拿来当作等价物使用,但不能让编译器把阵列当指标跑。 我们可以不分辨(赚钱),但编译器不能不分辨。 --



※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 124.8.129.228
1F:推 VictorTom:小弟先推前一句: 如果你欺骗 compiler, 06/19 09:31
2F:→ zlw:我忘了下一句是什麽了... 06/19 09:35
3F:推 VictorTom:好像是: compiler会向你复仇....XD 06/19 10:00
4F:→ zlw:了解 06/19 10:36
5F:推 herman602:if you lie complier, you will get its revenge. 06/19 11:57
查这句话的出处,google 第一页就看到一篇文章, 刚好就是在讲 extern 这个。 google 搜寻 "If you lie to the compiler, it will get its revenge" 第一篇「Software Engineers Toolbox by Ian Cargill」 节录如下: Of course at this stage, you might be saying "Well if the compiler always makes the corrections, why do I have to bother about the difference?" The answer is that if you don't know the facts, you will one day lie to the compiler and, as Henry Spencer said, if you lie to the compiler, it will get its revenge. When passing arrays or pointers as function parameters it is pretty difficult to go wrong, but consider the case we had earlier of having a variable defined as an array in one file, but a declaration in another file (or in a header file) in the form: extern char *arry; This time, think about what the compiler will do when it then encounters a statement: c = arry[1]; ※ 编辑: zlw 来自: 124.8.129.228 (06/19 12:24)
6F:推 duidae:array跟pointer不是同一个东西 不过大部分人都会搞混 06/19 18:35
7F:→ duidae:expert C programming这本书不错 有提到元po这个问题 06/19 18:36
8F:→ zlw:没有看过,有机会看看,谢谢。另外我修正一下,错误的extern 06/19 18:38
9F:→ zlw:本来就会让编译器输出错的指令。只是後面看能不能再骗过linker 06/19 18:38
10F:→ zlw:的把关而已。 06/19 18:38
11F:推 softwind:这根本不可能过 arr linker一定报错 找不到ref. 06/20 01:10
※ 编辑: zlw 来自: 124.8.129.45 (06/20 04:01)
12F:→ zlw:gcc方面跟cole945前辈讲的应该一样,可以正常输出执行档。而VC 06/20 04:13
13F:→ zlw:用 cl /TC src.cpp 1.cpp 强制编译成C後还是可以任意extern的 06/20 04:14
※ 编辑: zlw 来自: 124.8.129.45 (06/20 05:48)







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:iOS站内搜寻

TOP