返回信息流本人突然对函数参数的 传递 和 返回值的 传递 产生了 兴趣。先发一贴来问问。
下面的是我的源代码
#include <iostream>
using namespace std;
class X
{
public:
int a;
void modify(){a++;}
};
void f(X x)
{
x.modify();
}
int main()
{
X a;
f(a);
return 0;
}
下面的是我从2003里反汇编里挖出来的东东。
int main()
{
00411A70 push ebp
00411A71 mov ebp,esp
00411A73 sub esp,0D8h
00411A79 push ebx
00411A7A push esi
00411A7B push edi
00411A7C lea edi,[ebp-0D8h]
00411A82 mov ecx,36h
00411A87 mov eax,0CCCCCCCCh
00411A8C rep stos dword ptr [edi]
00411A8E mov byte ptr [ebp-0D1h],0
X a;
f(a);
00411A95 cmp byte ptr [ebp-0D1h],0
00411A9C jne main+3Bh (411AABh)
00411A9E push 411AF2h
00411AA3 call @ILT+565(__RTC_UninitUse) (41123Ah)
00411AA8 add esp,4
00411AAB mov eax,dword ptr [a]
00411AAE push eax
00411AAF call f (41151Eh)
00411AB4 add esp,4
return 0;
00411AB7 xor eax,eax
}
这个反汇编我看不太懂,还请哪位高手指点一下。
我想知道的是,在函数参数是一个对象的时候,编译器是不是将整个类的所有东西都压到了堆栈里?还是只是压了一个指针?
这是一条镜像帖。来源:北邮人论坛 / soft-design / #16482同步于 2007/4/7
该镜像源已超过 30 天没有更新,可能在源站已被删除。
SoftDesign机器人发帖
[求助]关于C++中函数参数是类的传递方式
hman
2007/4/7镜像同步13 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
这个就是是那Stack和Heap的区别啦~~~
在OOP中所有的值类型value type如int 都是在stack上建立的
引用类型如对象都是在heap上创建的。。。然后在stack上压入一个该对象的引用~~~(也就是你说的指针啦。。。这样说不太严格啦。。。OOP指针和引用还是有不小的区别的。。。自己找点资料看吧。。。XD)
关于stack和heap在《The JavaTM Virtual Machine Specification》中是这么说的:
[QUOTE]3.5.2 Java Virtual Machine Stacks
Each Java virtual machine thread has a private Java virtual machine stack, created at the same time as the thread.3 A Java virtual machine stack stores frames (§3.6). A Java virtual machine stack is analogous to the stack of a conventional language such as C: it holds local variables and partial results, and plays a part in method invocation and return. Because the Java virtual machine stack is never manipulated directly except to push and pop frames, frames may be heap allocated. The memory for a Java virtual machine stack does not need to be contiguous.
The Java virtual machine specification permits Java virtual machine stacks either to be of a fixed size or to dynamically expand and contract as required by the computation. If the Java virtual machine stacks are of a fixed size, the size of each Java virtual machine stack may be chosen independently when that stack is created. A Java virtual machine implementation may provide the programmer or the user control over the initial size of Java virtual machine stacks, as well as, in the case of dynamically expanding or contracting Java virtual machine stacks, control over the maximum and minimum sizes.4
The following exceptional conditions are associated with Java virtual machine stacks:
If the computation in a thread requires a larger Java virtual machine stack than is permitted, the Java virtual machine throws a StackOverflowError.
If Java virtual machine stacks can be dynamically expanded, and expansion is attempted but insufficient memory can be made available to effect the expansion, or if insufficient memory can be made available to create the initial Java virtual machine stack for a new thread, the Java virtual machine throws an OutOfMemoryError.
3.5.3 Heap
The Java virtual machine has a heap that is shared among all Java virtual machine threads. The heap is the runtime data area from which memory for all class instances and arrays is allocated.
The heap is created on virtual machine start-up. Heap storage for objects is reclaimed by an automatic storage management system (known as a garbage collector); objects are never explicitly deallocated. The Java virtual machine assumes no particular type of automatic storage management system, and the storage management technique may be chosen according to the implementor's system requirements. The heap may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger heap becomes unnecessary. The memory for the heap does not need to be contiguous.
A Java virtual machine implementation may provide the programmer or the user control over the initial size of the heap, as well as, if the heap can be dynamically expanded or contracted, control over the maximum and minimum heap size.5
The following exceptional condition is associated with the heap:
If a computation requires more heap than can be made available by the automatic storage management system, the Java virtual machine throws an OutOfMemoryError. [/QUOTE]
然后呢stack和heap间传递涉及到的boxing和unboxing(C# & JDk5.0)啦
java/C#的finalize() 和C++/C#的析构函数啦~~~
garbage collectiong啦。。。这些东西都是相关的。。。自己找资料慢慢看吧。。。
再深入的话还有OS的MM那部分对Stack和heap的处理。。。
东西太多了。。。Orz。。。
[QUOTE]int main()
{
00411A70 push ebp
00411A71 mov ebp,esp
00411A73 sub esp,0D8h //调整堆栈指针 分配0D8H的空间
00411A79 push ebx
00411A7A push esi
00411A7B push edi //默认保存寄存器 也就是Win32asm写在函数名后uses后面那些寄存器
00411A7C lea edi,[ebp-0D8h]
00411A82 mov ecx,36h
00411A87 mov eax,0CCCCCCCCh
00411A8C rep stos dword ptr [edi] //36h*4=0D8h 以0CCCCCCCCH值初始化已分配的0D8H局部变量内存空间
00411A8E mov byte ptr [ebp-0D1h],0
X a;
f(a);
00411A95 cmp byte ptr [ebp-0D1h],0 //看看a有没有引用对象啊
00411A9C jne main+3Bh (411AABh) //有。。。那就去411AABh执行f(a)吧
00411A9E push 411AF2h
00411AA3 call @ILT+565(__RTC_UninitUse) (41123Ah) //抛出一个运行时检测(Run-Time Error Checks)异常。。。
00411AA8 add esp,4 //刚才压的411AF2h没用了。。。调整一下esp
00411AAB mov eax,dword ptr [a]
00411AAE push eax //把a的地址存入eax。。。然后eax入栈作为f()的参数
00411AAF call f (41151Eh) //调用f
00411AB4 add esp,4 //存入的a的地址没用了。。用add esp来调整堆栈。。。反正后面是要一次返回到ebp的保存地址的。。。所以这里不用pop要不还得浪费一个寄存器。。。而且pop耗时比add要多很多
return 0;
00411AB7 xor eax,eax //清空eax作为返回值 也就是return后面的0
}
//这后面应该还有堆栈平衡的代码
够详细了吧。。。
[/QUOTE]
【 在 hman 的大作中提到: 】
: 本人突然对函数参数的 传递 和 返回值的 传递 产生了 兴趣。先发一贴来问问。
: 下面的是我的源代码
: #include <iostream>
: ...................
00411A95 cmp byte ptr [ebp-0D1h],0
00411A9C jne main+3Bh (411AABh)
00411A9E push 411AF2h
00411AA3 call @ILT+565(__RTC_UninitUse) (41123Ah)
你的程序是不能正确运行的。你试试。
修改好后,再研究汇编代码。
都抛出RTC的未初始化异常了。。。
而且上面用的是复制实参。。。再怎么modify a本身还是不会变的。。。XD
不过这些和类的实例是存在heap里这个事实一点关系都没有吧。。。>_<
【 在 Jarod 的大作中提到: 】
: 00411A95 cmp byte ptr [ebp-0D1h],0
: 00411A9C jne main+3Bh (411AABh)
: 00411A9E push 411AF2h
: ...................
非常感谢CNLAS对本问题作出如此详细的解答。
我现在的理解是,新生成的对象是放在heap里的。在函数调用的时候,把该对象的首地址push到stack里。 然后就可以在函数中用了?
那这样的话我想提出一个问题了。如果这个函数在函数体中改变了一个传入对象的成员,那么当这个函数返回后,作为参数传入的这个对象的成员也会发生相应的改变么?
就我给出的这个例子而言,对象X a;传入f()返回后,a.a的值会发生变化么?
JAVA中对象是传引用的,那一定是要改变的。
C++应该是看传的是引用还是复制的对象吧?LS的问题还是应该看传递的参数到底是不是该对象的引用还是只是它的一个copy
【 在 Jarod 的大作中提到: 】
: 00411A95 cmp byte ptr [ebp-0D1h],0
: 00411A9C jne main+3Bh (411AABh)
: 00411A9E push 411AF2h
我回去又看了一下,发现原来的代码的确有问题。现在改了一下帖上来。
int main()
{
00413670 push ebp
00413671 mov ebp,esp
00413673 sub esp,0CCh
00413679 push ebx
0041367A push esi
0041367B push edi
0041367C lea edi,[ebp-0CCh]
00413682 mov ecx,33h
00413687 mov eax,0CCCCCCCCh
0041368C rep stos dword ptr [edi]
X a(1);
0041368E push 1
00413690 lea ecx,[a]
00413693 call X::X (411528h)
f(a);
00413698 mov eax,dword ptr [a]
0041369B push eax
0041369C call f (41151Eh)
004136A1 add esp,4
return 0;
004136A4 xor eax,eax
}
我感觉 mov eax, dword ptr [a]这句话是取得对象a的地址然后push到了堆栈里。
不知道这样理解对不?
我还把f()函数的汇编拷出来了。
void f(X x)
{
00411A30 push ebp
00411A31 mov ebp,esp
00411A33 sub esp,0C0h
00411A39 push ebx
00411A3A push esi
00411A3B push edi
00411A3C lea edi,[ebp-0C0h]
00411A42 mov ecx,30h
00411A47 mov eax,0CCCCCCCCh
00411A4C rep stos dword ptr [edi]
x.modify();
00411A4E lea ecx,[x]
00411A51 call X::modify (411519h)
}