|
vc6 下反汇编代码:
char s[]="HELLO";
mov eax,[string "HELLO" (0042801c)]
mov dword ptr [ebp-8],eax
mov cx,word ptr [string "HELLO"+4 (00428020)]
mov word ptr [ebp-4],cx
12: return s;
lea eax,[ebp-8]
可见,字符串"hello\0"位于一个固定的内存区域,如果你在其他地方也引用了"hello",也是编译指向该地址.我们通常称其为字符串常量.C/C++编译器会将字符串常量放于内存某个区域(通常我们称其为常量区),并用指针代替字符串引用.而不做优化的编译器有可能在内存中生成多个相同的拷贝. :0
ebp-8 是字符数组s的开始地址, 将该地址放于寄存器eax中返回. 但是堆栈指针寄存器esp在函数返回时,已经指向ebp+某常数的位置了, 亦即我们说的出栈, 但其低地址的内容是否被销毁了呢? 其实没有, 但如果又有进出栈的话, 原内存地址的内容可能被覆盖,即 s 数组可能会被覆盖. 从上面我们可以看出 s 数组的大小为dword + word =6字节 , 其实在"hello"+5处存放的是0 .
---------------------------------------------------------------------------------------------------
23: int *p;
24: int i = 32;
dword ptr [ebp-8],20h
25: p = &i;
lea eax,[ebp-8]
mov dword ptr [ebp-4],eax
26: return p;
mov eax,dword ptr [ebp-4]
看第一句定义了一个指针,然而却没有对应的汇编代码.没错, 所有变量在最后其实都变成了对一个地址的引用,如ebp-8 处是i , ebp-4处是p; 但是不同的变量占有不同字节的内存, 那么我们怎么知道要操作多少字节呢? 呵呵,这就是编译器的事了, i 是int , p是int *. 所以操作都是dword . 更复杂的, 对结构体是如何处理的呢?大家有空自己上网搜一搜.
cout char * 是输出字符串, cout int *是输出地址, 为什么会有乱码和输出地址应该可以总结出来了. 大家也可以想一下cout重载<<运算符的实现,嗯,应该也是靠判断末尾0来判断一个字符串的结束吧.
=========================================================
* f() 即取s 数组第一个字符( 因为f()返回的是 s 的地址) , 其实你还可以试试, *( f()+1) 就是e , +2 就是 l , 嗯, 这就是我们前面所说的, 函数虽然返回了, 但其原先的参数是否销毁了呢? 没有(也没有必要,难道特意增加指令去将这些内存单元置0?) . 但当cout 函数调用时, s 数组有可能要被覆盖了. 因为有新的进出栈的动作.(所以产生了乱码) . 需要弄清楚的是函数调用的先后, cout << f() , 先调用 f(), f()的返回值作为参数再调用operater<< 函数..
:) 以上是我的一点理解 |
|