昨日与一网友讨论pass by value, pass by reference, pass by pointer,后来想干脆拿出来说个明白,然后晚上就测试了一下,以下为例.1. pass by value
i := 10;
mov  esi, $0000000a // esi的值就是10 (i使用esi寄存器)j := 5;
mov  edi, $00000005 // esi的值就是5  (j使用edi寄存器)Edit1.Text := IntToStr(Params.SwapByValue(i, j));
mov   edx, edi // edx中的值是edi中的值,即同为j的值,注意 不是j的地址!!
mov  eax, esi // esi中的值是esi中的值,即同为i的值,注意 不是i的地址!!
call  SwapByValue // 所以成为 pass-by-valuelea edx, [ebp-$04]
call IntToStr
//////////////////////////////  SwapByValue
tmp := i;
mov  ecx, eax // eax的值为i的值,传给ecx,(tmp使用ecx寄存器,这是编译器的优化),注意 不是i的地址!!i := j;
mov eax, edx // edx的值为j的值,传给eax,(eax中原值为i,而不是i的地址!!)

j := tmp;
mov edx, ecx // ecx中的值为tmp(i)的值,传给edx,(edx原值为j, 而不是j的地址!!)
// 所以更改后 对函数外面的参数 根本没有影响.
Result := i + j;
add  edx, eax
mov eax, edx///////////////////////////////////////////
2. pass by reference
i := 10;
mov  [ebp-$04], $0000000a // i使用内存 非寄存器 因为对寄存器不能进行取地址j := 5;
mov  [ebp-$08], $00000005 // j使用内存 非寄存器 因为对寄存器不能进行取地址Edit1.Text := IntToStr(Params.SwapByRef(i, j)); // 注意参数压栈方式为 从右到左
lea  edx, [ebp-$08] // 注意!! edx为j的地址 j使用内存 非寄存器 因为对寄存器不能进行取地址
lea eax, [ebp-$04] // 注意!! eax为i的地址 i使用内存 非寄存器 因为对寄存器不能进行取地址
call  SwapByRef // herelea edx, [ebp-$0c]
call IntToStr//////////////////////////////// SwapByRef
tmp := i;
mov ecx, [eax] // eax为一个指针,里面的值为变量i的地址,所以需要再寻址一次 通过[eax]i := j;
mov ebx,   [edx] // edx为一个指针,里面的值为变量j的地址, ebx为j的值
mov [eax], ebx // 把eax这个指针指向的地址的值赋为ebx(j), 注意!!eax的内容就是函数外i的地址,
// 所以相当于直接修改i的内容,所以i被修改!!
j := tmp;
mov [edx], ecx // ecx的值为tmp(i), 
// 把edx这个指针指向的地址的值赋为ecx(tmp/i),注意!!edx的内容就是函数外j的地址,
// 所以相当于直接修改j的内容,所以j被修改!!
Result := i + j;
mov eax, [eax]
add eax, [edx]//////////////////////////////////////////////////////
3. PassByPtri := 10;
mov [ebp-$04], $0000000a // i使用内存 非寄存器 因为对寄存器不能进行取地址j := 5;
mov [ebp-$08], $00000005 // j使用内存 非寄存器 因为对寄存器不能进行取地址Edit1.Text := IntToStr(Params.SwapByPtr(@i, @j));
lea edx, [ebp-$08]; // 注意!! edx为j的地址 j使用内存 非寄存器 因为对寄存器不能进行取地址
lea  eax, [ebp-$04]; // 注意!! eax为i的地址 i使用内存 非寄存器 因为对寄存器不能进行取地址
call SwapByPtr;lea edx, [ebp-$0c]
call IntToStr//////////////////////////////  SwapByPtr
SwapByPtr
beginpush ebxtmp := i^;
mov  ecx, [eax] // eax为一个指针,里面的值为变量i的地址,所以需要再寻址一次 通过[eax]
// 保存i^的值到ecx
i^ := j^;
mov ebx,  [edx] // edx为一个指针,里面的值为变量j的地址, ebx为j的值
mov  [eax], ebx // 把eax这个指针指向的地址的值赋为ebx(j), 注意!!eax的内容就是函数外i的地址,
// 所以相当于直接修改i的内容,所以i被修改!!j^ := tmp;
mov ebx,   ecx // ecx的值为tmp(i), ebx的值也为ecx中的值,即为(tmp/i)
mov [edx], ebx // 把edx这个指针指向的地址的值赋为ecx(tmp/i),注意!!edx的内容就是函数外j的地址,
// 所以相当于直接修改j的内容,所以j被修改!!Result := i^ + j^;
mov eax, [eax]
add  eax, ebxend;
opo ebx
ret
lea  eax, [eax+$00]  结论:其实一个东西是否指针 就是看是否对其寻址 比如
mov eax, ebx  // 那么ebx就是一个普通的值
mov eax, [ebx]  // 那么ebx就是一个指针 这里取ebx中的值作为,然后再寻址一次.传值 vs 传引用 vs 传地址
重要的是区别就是:
传值:  外面(函数外面)的地址与里面(函数里面)操作的数(内容)(的地址)不是同一个,所以根本没有修改.
传引用,传地址: 外面(函数外面)的地址与里面(函数里面)操作的数(内容)的地址是同一个,所以在函数里面修改的话,影响外面.以前看过类似功能的函数用VC编译后的asm代码,里面大致类似,就是delphi使用的寄存器会多一些,而VC则是直接从内存中存取,可能速度稍微慢一些.

解决方案 »

  1.   

    源码为:
    caller: ********************************************
    procedure TForm1.Button1Click(Sender: TObject);
    var
      i, j:Integer;
    begin
      i := 10;
      j := 5;
      Edit1.Text := IntToStr(Params.SwapByValue(i, j));
      Edit2.Text := IntToStr(i);
      Edit3.Text := IntToStr(j);
    end;procedure TForm1.Button2Click(Sender: TObject);
    var
      i, j:Integer;
    begin
      i := 10;
      j := 5;
      Edit1.Text := IntToStr(Params.SwapByRef(i, j));
      Edit2.Text := IntToStr(i);
      Edit3.Text := IntToStr(j);
    end;procedure TForm1.Button3Click(Sender: TObject);
    var
      i, j:Integer;
    begin
      i := 10;
      j := 5;
      Edit1.Text := IntToStr(Params.SwapByPtr(@i, @j));
      Edit2.Text := IntToStr(i);
      Edit3.Text := IntToStr(j);
    end;callee: *******************************************
    function SwapByValue(i, j: Integer): Integer;
    var
      tmp: Integer;
    begin
      tmp := i;
      i := j;
      j := tmp;
      Result := i + j;
    end;function SwapByRef(var i: Integer; var j: Integer): Integer;
    var
      tmp: Integer;
    begin
      tmp := i;
      i := j;
      j := tmp;
      Result := i + j;
    end;function SwapByPtr(i: PInteger; j: PInteger): Integer;
    var
      tmp: Integer;
    begin
      tmp := i^;
      i^ := j^;
      j^ := tmp;
      Result := i^ + j^;
    end;
      

  2.   

    跟着 beyondtkl老师学:
    我顶
      

  3.   

    reference is c++ provide to c programer's help ring,like pointer reference is also lower
    memory acces but it's packed safety pointer.
      

  4.   

    真长,楼主真能写
    赞一个
    个人感觉由于Delphi的自动对象引用,所以用Delphi做测试可能会引起混淆。
    (notice that:你的测试用例都是基本数据类型而不是UDT)
      

  5.   

    to: wfhlxl() 
    是的 引用是C++中新增的功能,就是對指針的一層封裝...從而使用引用同樣具有指針的速度,方便,而且比直接用指針更安全.to: firetoucher
    是的 這裡我還沒有寫自定義類型的引用,在delphi裡面,好像自定義類型默認就是 pass by reference的, 這是不錯,但是這算是編譯器背後的動作,所以大家應該弄清楚就是. 我這裡舉例只是說明一般的區別, udt應該也是同樣的道理,就是一個指針<同一個地址>只不過後面的地址的內容更為複雜,需要在一次尋址 從而定位到 其相應的 data member而已...
    在C++裡面 引用使用
    void func(yourClass &u);
    void func(const yourClass &u);
    void func(yourClass u); // 因為C++默認都是pass by value  而黨傳大數據對象時,如果
    pass by value會導致一系列複雜的動作(包括構造函數,拷貝構造函數,析構函數等的掉用,就很浪費),所以,一般傳送大對象 就會使用 後面兩種方式呵呵 話說長了
      

  6.   

    呵呵 看來 d版的對這些不感冒 或者 基礎都很好 或者....算了.. 還是去Cpp版...下班接鐵...
      

  7.   

    hehe^^ 是我们学的不够深,没法和你“过招”
      

  8.   

    是亞..傳值 直接使用register 快了很多
    而 by ref && by ptr不能使用register,因為不能對register進行 lea取地址操作...
    <上面有注釋了一下>但是 by ref && by ptr在傳大對象的話 速度會快很多 因為不用copy.
      

  9.   

    //是的 這裡我還沒有寫自定義類型的引用,在delphi裡面,好像自定義類型默認就是 pass by reference的,这句话我不太清楚,为什么说自定义类型默认是传递引用???
    我觉得delphi默认都是传值,不管什么类型,除非指定要用引用来传。
      

  10.   

    //是的 這裡我還沒有寫自定義類型的引用,在delphi裡面,好像自定義類型默認就是 pass by reference的,这句话我不太清楚,为什么说自定义类型默认是传递引用???
    我觉得delphi默认都是传值,不管什么类型,除非指定要用引用来传。// no. it's true. 默認是pass by ref的 你可以自己實驗以下
    比如
    procedure test(adoQry: TAdoQuery); // no var
    begin
      adoQry.Close;
      adoQry.SQL.Clear;
      adoQry.SQL.Text := 'chagned';
    end;call:
    adoQry := TAdoQuery;
    adoQry := TAdoQuery.Create(Self);
    adoQry.SQL.Text := 'hello';
    ShowMessage(...);
    test(adoQry);
    ShowMessage(...);
    adoQry.Free;
      

  11.   

    早就发现   beyondtkl(大龙驹) 乃一强人
      

  12.   

    // no. it's true. 默認是pass by ref的 你可以自己實驗以下
    比如
    procedure test(adoQry: TAdoQuery); // no var
    begin
      adoQry.Close;
      adoQry.SQL.Clear;
      adoQry.SQL.Text := 'chagned';
    end;call:
    adoQry := TAdoQuery;
    adoQry := TAdoQuery.Create(Self);
    adoQry.SQL.Text := 'hello';
    ShowMessage(...);
    test(adoQry);
    ShowMessage(...);
    adoQry.Free;------------------------------------------------------------这个不叫传引用,如果你看过“java是传值,还是传引用”这片文章的话,就知道是怎么回事了。
    就说你这个吧,你外部的变量 adoQry,和你所调用过程中的adoQry变量,是两个变量,而不像引用所指的是种别名,也就是说这两个变量指向的是同一个对象,自然你改变过程内部变量所指向的对象的内容,外部所指向对象的内容也随之改变了,但是有一点就是,如果你改变test过程中的变量adoQry所指向的对象。也就是说,让它指向另外一个对象,它是不会影响外不变量adoQry的指向的。所以你写的这个例子,还是传值,传的是变量adoQry的值。
      

  13.   

    not only c++ but almost all object-orient language have reference specility,inclide vb ,delphi for example in vb there is byref param ,in delphi var param is reference so
    reference is object-origent's specility ,but c++ implient it befere,all object param in vb and delphi is reference;
      

  14.   

    not only c++ but almost all object-orient language have reference specility,inclide vb ,delphi for example in vb there is byref param ,in delphi var param is reference so
    reference is object-origent's specility ,but c++ implient it befere,all object param in vb and delphi is reference;-------------------------------------------------------------------对,没错,对象使用的都是引用变量来操作。但是如果这样说引用是面向对象的特性,我觉得不妥。毕竟参数的传送是不管什么类型的。另外,java中只有传值,没有传引用。
      

  15.   

    另外,java中只有传值,没有传引用。// 不对吧。。java中没有指针 都是reference.....
      

  16.   

    to KissDelphi:
    but c# can pass ref param by ref key word.java pass value ,whether you can confrim ? if java can not pass ref param addtional virual machine, java's effect is very slow, ref can be in object-oritend extend use, because effect in main.
      

  17.   

    这个不叫传引用,如果你看过“java是传值,还是传引用”这片文章的话,就知道是怎么回事了。// 可能你说的传值...理解不台一样
    如果按你的理解 pass by pointer也是传值的 因为必须要把这个地址也传过去,这也是一个值
    比如C++中
    void test(char* pBuf)
    {
      pBuf = new char[100];
    }
    call:
    char* pBuf = NULL;
    test(pBuf);
    lstrcpy(pBuf, "AAAAA", 5); // 你这样去看看 会崩溃的...
    C++的东西不想在这里多说 可以去C++版说...
      

  18.   

    求教
    lstrcpy(&(*pBuf), "AAAAA", 5);?会崩溃吗?
      

  19.   

    以前分析过vc的,不过没打算写出来
    原因很简单,如果对汇编比较熟悉,自己看就明白了
    如果不熟,看了这个文章帮助也不会很大ps:还有一个原因就是我比较懒 :P
      

  20.   

    in my mean, pass by value is :
    one address a var when it was passed to one function by value,copy the var in stack in stack use the copy.
      

  21.   

    to  beyondtkl(大龙驹<*to be by your side:C++&&戀上叔本华*>) 这不是理解的问题,当然我也不想玩文字游戏,具体是传值还是传引用,要比较来说,
    如果你非要说你上面的例子是传引用,可能你也能说出你的理由来。但是如果和真正的传引用(也就是函数中用var指定了的)是有本质区别的。这个问题在java中已经讨论了很久了(可能比较绕吧,不然不会在全世界范围内讨论这么长时间了,到现在还有人说基本类型是传值,类是传引用呢。)。所以我觉得所有参数默认是传值是比较准确的说法。
      

  22.   

    to beyondtkl(大龙驹<*to be by your side:C++&&戀上叔本华*>) 我想你认为那是传引用还有一个原因,我不知道自己理解的对不对,因为我对c++一点都不熟,你可以帮我纠正一下。在类方面,delphi,和c++有一个不同点,就是互相赋值的时候。
    比如说有两个变量,a,b,都是MyClass类,
    在delphi中如果写 a:=b的话,其实就是把a指向了b指向的类,也就是说类的实例还是只有一个,只不过原来是b指向的,现在a也指向了它。但是,我听说在c++中,如果写a=b的话,就会复制一份类的实例,也就是说a和b指向不同的两个类实例,当然这两个类的实例的内容是一样的。如果上面对于c++我所说的是正确的话,也就是说在c++中同样的你上面delphi的代码,因为方法内部和外部变量指向的是不同的实例,所以即使内部改变了实例的内容,外面的也不会有所改变。就是因为这个原因(前提是我上面说的都对,如果c++中并不是我描述的那样,那就另当别论了)。
    你才认为delphi中类参数传递的是引用。我对c++不熟,但是在这方面我知道,delphi和java是一样的。
      

  23.   

    回复人: wfhlxl() ( ) 信誉:100  2004-12-08 17:54:00  得分: 0  
     
     
       to KissDelphi:
    but c# can pass ref param by ref key word.java pass value ,whether you can confrim ? if java can not pass ref param addtional virual machine, java's effect is very slow, ref can be in object-oritend extend use, because effect in main.
      
     
    ------------------------------我知道你是怎么理解的,因为以前我和你的想法一样。
    你一定要注意一点,也就是说传值还是传引用是对照着c++,delphi中的传引用来说的,
    如果脱离了这一点,那就成了“一个意思,两种表述了”,这样再讨论下去就没有什么意义了。java中,类变量,具体来说,就是一个对象实例的引用变量,如果你传送这个变量的值,当然可以说成是传送这个对象实例的引用了。
    但是如果因为这个,你就说java中是by ref,那就大错特错了。
    如果你想多了解一下这方面的知识,可以在网上搜一下,一堆一堆的。
      

  24.   

    to KissDelphi:
    now can you do select:
    in java
    object a=new objectcontrutor();
    object b;
    b=a;
    1. a and b is one object
    2. or two object;
      

  25.   

    回复人: wfhlxl() ( ) 信誉:100  2004-12-08 19:54:00  得分: 0  
     
     
       to KissDelphi:
    now can you do select:
    in java
    object a=new objectcontrutor();
    object b;
    b=a;
    1. a and b is one object
    2. or two object;
    -------------------------------------------这是一个很基本的问题,准确的表述是a和b指向同一个对象实例。如果你想复制一份对象实例,要用clone的。如果是在delphi中,要自己实现assigned方法,当然如果对象里饱含对象类型变量的话,还要涉及到深度克隆等问题。
      

  26.   

    not understand.
    in java a object can let it's handle to another,the another get the object's reference or not
      

  27.   

    object f1(b as object)
    {
      return b;}
    main()
    {
    object a=new ...;
    object b;
    b=f1(a); // b equ a ?}
      

  28.   

    你写的这个程序假设说是java程序(有很多的错误)这个程序和刚才那个是一样的效果,还是只有一个实例。我已经说了,如果你想复制一份实例,要用clone方法。这些都是java的基础知识,建议你先稍微学一下java
      

  29.   

    in my mean: the way of passing param  is pass by reference.
      

  30.   

    天呢,我前面说了这么多,怎么还不明白呀,都白说了。给你个地址,你自己慢慢看吧,java只有传值,没有传引用。http://www.javaranch.com/campfire/StoryPassBy.jsp
      

  31.   

    一个意思,两种表述:
    in c++, not only referece but also pointer's value are long  in machine.
    but the long is memory's address,* p like asm [bx],your mean java's reference object not like 
    c++ and delphi.
      

  32.   

    在delphi中如果写 a:=b的话,其实就是把a指向了b指向的类,也就是说类的实例还是只有一个,只不过原来是b指向的,现在a也指向了它。但是,我听说在c++中,如果写a=b的话,就会复制一份类的实例,也就是说a和b指向不同的两个类实例,当然这两个类的实例的内容是一样的。---------------
    是的 在C++ 是這樣的...MyClass b; MyClass a = b; // 會發生copy constructor...
    而 MyClass& a = b;則還是指向同一份內存地址 那就是b的地址,這就是傳引用
    為身麼會這樣呢, 是因為 C++裡面引用是明確使用 & 這個符號的, 而delphi,java卻不是這樣的..TO:  lw549(那个孩子他爹)
    不懂asm就沒一點幫助麼?? 我上面的注釋寫的很清楚.var,跟ptr在函數體內操作的是同一個地址,所以傳入的辯量可以在裡面修改...然後據此去說明 by value,by ref,by ptr的區別而已...
      

  33.   

    Java is pass-by-value.For primitives, you pass a copy of the actual value.For references to objects, you pass a copy of the reference (the remote control).///////////////////////
    TO:  KissDelphi(想说爱你不容易) 我看了一下你給的帖子,時間關係我看了開頭跟結束..
    不知道你後面那句話 是怎麼理解的... you pass a copy of the reference (the remote control).
    (這個reference(the remote control)是怎麼理解的??),雖然很久沒用java了 但是對這個還是很有興趣的...
      

  34.   

    to  beyondtkl(大龙驹):
    char* pBuf = NULL;
    void objet::test(char* pBuf)
    {
      pBuf = new char[100];
    this-> pBuf=pBuf;
    }
    call:
    test(pBuf);
    lstrcpy(pBuf, "AAAAA", 5); // do this can destroy or not ?
      

  35.   

    樓上的....你看好了...我開始寫的代碼跟你的代碼有身麼不同.我只是舉個例子而已.. 這類問題 等我們去C++板討論 OK???
      

  36.   

    先顶
    C# Primer中讲述Value Type and Reference Type 的一节讲的很透彻,偶曾经受益匪浅,楼主加上了汇编注释,没看呢就先得佩服
    呵呵
      

  37.   

    to beyondtkl(大龙驹<*to be by your side:C++&&戀上叔本华*>) 其实,我前面已经说过了,传值还是传引用,都是相对于c++,delphi中的传引用来说的,
    对于传值来说
    不管是
    procedure test(i:integer),还是procedure test(Ed:TEdit),
    传的都是变量i和Ed的值,当然i的值是一个整数,Ed的值是个对象实例的地址,我们来比较一下procedure test(var i:integer)和procedure test(Ed:TEdit),
    很显然当你调用的时候你虽然参数写的是i,但是传送的却是i的引用,
    但是Ed就不是,你填写的Ed,那么它传送的就是Ed的一个值,
    当然这儿Ed是一个对象实例的引用,但是你这儿调用的时候不是填写的一个对象实例呀,如果你填写的是一个对象实例(事实上这也不大可能,对象要通过引用才能操作),那么如果程序帮你传送的是实例的引用,那才叫by ref呢。只有对照着c++,delphi中真正的by ref才能知道这种区别。
      

  38.   

    procedure test(i:integer),还是procedure test(Ed:TEdit),
    传的都是变量i和Ed的值,当然i的值是一个整数,Ed的值是个对象实例的地址,// 這還有身麼好說的 如果你認為這都是傳值的 我還有身麼話好說.
    // 我在前面就說了 
    ////////////////////////////////
    这个不叫传引用,如果你看过“java是传值,还是传引用”这片文章的话,就知道是怎么回事了。// 可能你说的传值...理解不台一样
    如果按你的理解 pass by pointer也是传值的 因为必须要把这个地址也传过去,这也是一个值
    /////////////////////////////////
    你說你不是玩文字遊戲 現在看來你不是玩文字遊戲是身麼...
      

  39.   

    楼主在delphi已经双星了。
    汗一个!!!
      

  40.   

    procedure test(i:integer),还是procedure test(Ed:TEdit),
    传的都是变量i和Ed的值,当然i的值是一个整数,Ed的值是个对象实例的地址,// 這還有身麼好說的 如果你認為這都是傳值的 我還有身麼話好說.
    // 我在前面就說了 
    ////////////////////////////////
    这个不叫传引用,如果你看过“java是传值,还是传引用”这片文章的话,就知道是怎么回事了。// 可能你说的传值...理解不台一样
    如果按你的理解 pass by pointer也是传值的 因为必须要把这个地址也传过去,这也是一个值
    /////////////////////////////////
    你說你不是玩文字遊戲 現在看來你不是玩文字遊戲是身麼...
    ---------------------------------------------------------------------------如果你这么说,我就无话可说了,你现在的想法和我以前的一样,
    我也曾经认为这是文字游戏,
    如果你非要认为那是by ref的话,也无所谓,只要知道它和真正的delphi和c++中by ref是不一样的就可以了。(我上面也做了比较)其实delphi中没人讨论这个问题,就是因为delphi本身就支持by ref,所以很容易就看出区别来。但是java就不一样了,因为java是不支持by ref的,只支持by value(这也是java的发明人的结论),所以才会有那种大讨论。其实呢,这个问题搞不清楚也无所谓,因为基本上不会影响实际的应用。只不过是为了严谨,才下的一个结论而已。okey,这个问题到此为止,我本来是想知道传指针和传引用的区别的。没想到撤到这儿去了。
      

  41.   

    我还想说一点的就是,是by value ,还是by ref是体现在调用方法时,方法对于参数的处理上,
    而不是表现在参数本身所代表的意义上。如果混淆在一块的话,是说不清楚的。