BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / soft-design / #16482同步于 2007/4/7
该镜像源已超过 30 天没有更新,可能在源站已被删除。
SoftDesign机器人发帖

[求助]关于C++中函数参数是类的传递方式

hman
2007/4/7镜像同步13 回复
本人突然对函数参数的 传递 和 返回值的 传递 产生了 兴趣。先发一贴来问问。 下面的是我的源代码 #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 } 这个反汇编我看不太懂,还请哪位高手指点一下。 我想知道的是,在函数参数是一个对象的时候,编译器是不是将整个类的所有东西都压到了堆栈里?还是只是压了一个指针?
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
CNLAS机器人#1 · 2007/4/7
这个就是是那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> : ...................
Jarod机器人#2 · 2007/4/7
00411A95 cmp byte ptr [ebp-0D1h],0 00411A9C jne main+3Bh (411AABh) 00411A9E push 411AF2h 00411AA3 call @ILT+565(__RTC_UninitUse) (41123Ah) 你的程序是不能正确运行的。你试试。 修改好后,再研究汇编代码。
CNLAS机器人#3 · 2007/4/7
都抛出RTC的未初始化异常了。。。 而且上面用的是复制实参。。。再怎么modify a本身还是不会变的。。。XD 不过这些和类的实例是存在heap里这个事实一点关系都没有吧。。。>_< 【 在 Jarod 的大作中提到: 】 : 00411A95 cmp byte ptr [ebp-0D1h],0 : 00411A9C jne main+3Bh (411AABh) : 00411A9E push 411AF2h : ...................
hman机器人#4 · 2007/4/8
非常感谢CNLAS对本问题作出如此详细的解答。 我现在的理解是,新生成的对象是放在heap里的。在函数调用的时候,把该对象的首地址push到stack里。 然后就可以在函数中用了? 那这样的话我想提出一个问题了。如果这个函数在函数体中改变了一个传入对象的成员,那么当这个函数返回后,作为参数传入的这个对象的成员也会发生相应的改变么? 就我给出的这个例子而言,对象X a;传入f()返回后,a.a的值会发生变化么?
mjhorse机器人#5 · 2007/4/8
JAVA中对象是传引用的,那一定是要改变的。 C++应该是看传的是引用还是复制的对象吧?LS的问题还是应该看传递的参数到底是不是该对象的引用还是只是它的一个copy
hman机器人#6 · 2007/4/9
我提这个问题的目的就是想要知道在C++中,传对象和传指针时的区别是什么。
Quake机器人#7 · 2007/4/9
同学,你没学过C吧?
hman机器人#8 · 2007/4/9
【 在 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) }
mjhorse机器人#9 · 2007/4/9
这和汇编是如何实现的没有什么关系吧。。一个有拷贝一个没有而已。。