昨日与一网友讨论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则是直接从内存中存取,可能速度稍微慢一些.
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则是直接从内存中存取,可能速度稍微慢一些.
解决方案 »
- 如何去重写派生类中的控件的事件
- Oracle dual 无重复取值问题。
- 求助BDE的问题,急!急!急!
- 请问如何实现在fastreport 预览中双击某cell可触发一特定事件
- 打印位置偏移,如何解决?
- 《资料管理专家》,瑞星一样的界面,风格独特,功能强大?
- 为什么没有人回答我???????????????????????????????????????????????????????
- 老问题,但没办法,急死我了!!!大家帮帮忙!!!
- 请问如何在代码中注册ActiveX控件?
- 求救~~~如何将转译visualfoxpro成delphi程序
- 求interbase6.0 或 6.0以上版本源码,另请评论一下interbase和firebird的不同
- 今天女朋友生日高兴,积蓄用掉一半心疼!散分.......散分.......散分.......散分.......
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;
我顶
memory acces but it's packed safety pointer.
赞一个
个人感觉由于Delphi的自动对象引用,所以用Delphi做测试可能会引起混淆。
(notice that:你的测试用例都是基本数据类型而不是UDT)
是的 引用是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會導致一系列複雜的動作(包括構造函數,拷貝構造函數,析構函數等的掉用,就很浪費),所以,一般傳送大對象 就會使用 後面兩種方式呵呵 話說長了
而 by ref && by ptr不能使用register,因為不能對register進行 lea取地址操作...
<上面有注釋了一下>但是 by ref && by ptr在傳大對象的話 速度會快很多 因為不用copy.
我觉得delphi默认都是传值,不管什么类型,除非指定要用引用来传。
我觉得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;
比如
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的值。
reference is object-origent's specility ,but c++ implient it befere,all object param in vb and delphi is reference;
reference is object-origent's specility ,but c++ implient it befere,all object param in vb and delphi is reference;-------------------------------------------------------------------对,没错,对象使用的都是引用变量来操作。但是如果这样说引用是面向对象的特性,我觉得不妥。毕竟参数的传送是不管什么类型的。另外,java中只有传值,没有传引用。
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.
如果按你的理解 pass by pointer也是传值的 因为必须要把这个地址也传过去,这也是一个值
比如C++中
void test(char* pBuf)
{
pBuf = new char[100];
}
call:
char* pBuf = NULL;
test(pBuf);
lstrcpy(pBuf, "AAAAA", 5); // 你这样去看看 会崩溃的...
C++的东西不想在这里多说 可以去C++版说...
lstrcpy(&(*pBuf), "AAAAA", 5);?会崩溃吗?
原因很简单,如果对汇编比较熟悉,自己看就明白了
如果不熟,看了这个文章帮助也不会很大ps:还有一个原因就是我比较懒 :P
one address a var when it was passed to one function by value,copy the var in stack in stack use the copy.
如果你非要说你上面的例子是传引用,可能你也能说出你的理由来。但是如果和真正的传引用(也就是函数中用var指定了的)是有本质区别的。这个问题在java中已经讨论了很久了(可能比较绕吧,不然不会在全世界范围内讨论这么长时间了,到现在还有人说基本类型是传值,类是传引用呢。)。所以我觉得所有参数默认是传值是比较准确的说法。
比如说有两个变量,a,b,都是MyClass类,
在delphi中如果写 a:=b的话,其实就是把a指向了b指向的类,也就是说类的实例还是只有一个,只不过原来是b指向的,现在a也指向了它。但是,我听说在c++中,如果写a=b的话,就会复制一份类的实例,也就是说a和b指向不同的两个类实例,当然这两个类的实例的内容是一样的。如果上面对于c++我所说的是正确的话,也就是说在c++中同样的你上面delphi的代码,因为方法内部和外部变量指向的是不同的实例,所以即使内部改变了实例的内容,外面的也不会有所改变。就是因为这个原因(前提是我上面说的都对,如果c++中并不是我描述的那样,那就另当别论了)。
你才认为delphi中类参数传递的是引用。我对c++不熟,但是在这方面我知道,delphi和java是一样的。
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,那就大错特错了。
如果你想多了解一下这方面的知识,可以在网上搜一下,一堆一堆的。
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;
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方法,当然如果对象里饱含对象类型变量的话,还要涉及到深度克隆等问题。
in java a object can let it's handle to another,the another get the object's reference or not
{
return b;}
main()
{
object a=new ...;
object b;
b=f1(a); // b equ a ?}
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.
是的 在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的區別而已...
TO: KissDelphi(想说爱你不容易) 我看了一下你給的帖子,時間關係我看了開頭跟結束..
不知道你後面那句話 是怎麼理解的... you pass a copy of the reference (the remote control).
(這個reference(the remote control)是怎麼理解的??),雖然很久沒用java了 但是對這個還是很有興趣的...
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 ?
C# Primer中讲述Value Type and Reference Type 的一节讲的很透彻,偶曾经受益匪浅,楼主加上了汇编注释,没看呢就先得佩服
呵呵
对于传值来说
不管是
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才能知道这种区别。
传的都是变量i和Ed的值,当然i的值是一个整数,Ed的值是个对象实例的地址,// 這還有身麼好說的 如果你認為這都是傳值的 我還有身麼話好說.
// 我在前面就說了
////////////////////////////////
这个不叫传引用,如果你看过“java是传值,还是传引用”这片文章的话,就知道是怎么回事了。// 可能你说的传值...理解不台一样
如果按你的理解 pass by pointer也是传值的 因为必须要把这个地址也传过去,这也是一个值
/////////////////////////////////
你說你不是玩文字遊戲 現在看來你不是玩文字遊戲是身麼...
汗一个!!!
传的都是变量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,这个问题到此为止,我本来是想知道传指针和传引用的区别的。没想到撤到这儿去了。
而不是表现在参数本身所代表的意义上。如果混淆在一块的话,是说不清楚的。