1.
一个函数/过程里.有几个参数.怎么知道?每个参数的类型又是什么...都要如何知道...代码实现.2.类中取函数:
如有:
ta=class
public
sum(x,y:Integer):integer;
end
那根据 字符串"sum"能知道函数sum的存在? 如果知道..又如何根据 字符串"sum" 调用 sum函数吗?
一个函数/过程里.有几个参数.怎么知道?每个参数的类型又是什么...都要如何知道...代码实现.2.类中取函数:
如有:
ta=class
public
sum(x,y:Integer):integer;
end
那根据 字符串"sum"能知道函数sum的存在? 如果知道..又如何根据 字符串"sum" 调用 sum函数吗?
解决方案 »
- 关于ToolBar的问题
- IdUDPServer问题,请高手指点!
- 求教idea加密算法问题
- 请问怎么得到 DBGrid 当前过滤后的显示的行数 或是一个过滤后的TABLE 的记录条数 ???谢谢
- 如何显示所有的windows 服务,并清除掉不用的服务。谢谢
- 请教高手:一个查询问题?
- DBGRID的picklist問題,在线等!
- Delphi 用odac连接数据库出错。
- 编译时,出现statement expected but 'procedure' found 是什么意思?
- 写64bit应用程序,使用ado连接csv遇到问题..
- 我想知道这个Sender是什么,怎么传递进来的,有什么办法吗,谢谢啊
- 关于FTP映射为本地驱动器问题
但是,一个程序被编译后,它就是机器码,根本没有“参数”和“变量”的概念。如果非要说“参数”,那用汇编助记符翻译机器码,如:004521C7 BAE0214500 mov edx,$004521e0
004521CC 8BC3 mov eax,ebx
004521CE E835FFFFFF call TForm1.Test
(这几句汇编助记符即下面的程序中的TForm1.Button2Click过程的X:=Test('OK!');这句。返回值的处理在子程序里)第一句,是把一个常量内存区的地址交给edx寄存器,这个地址是一个字符串。
第二句,应该是把Self指针交给eax寄存器。
第三句,是进入TForm1.Test汇编子程序。
上面的第一二句可以理解为“参数处理”。在子程序里,将用到这两个寄存哭的值。超过三个以上的参数,以及stdcall方式传参的话,将通过栈传递,道理是一样的。 所以说,我的理解,问题1基本无意义,因为程序在编译后就是一成不变的了,几个参数,参数是什么类型,都是固定的了(机器码)。-------------问题2、以下演示,只是演示使用MethodAddress方法。如果是通过类名字符串使用类,那至少这个类要注册。unit Unit1;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
function Test(A : string): boolean;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.dfm}function TForm1.Test(A : string): boolean;
begin
Showmessage(A);
Result := True;
end;procedure TForm1.Button1Click(Sender: TObject);
var
MN : ShortString;
P : Pointer;
F : function(Self : TObject; A : string): boolean;
begin
MN := 'Test';
P := Self.MethodAddress(MN);
if P <> nil then
begin
@F := P;
F(Self, 'OK?');
end else Showmessage('没有这个方法');
end;procedure TForm1.Button2Click(Sender: TObject);
var X : boolean;
begin
X := Test('OK!');
end;end.
var
F : function(Self : TObject; A : string): boolean;
begin
@F := Self.MethodAddress('Test');
if @F <> nil then F(Self, 'OK?')
else Showmessage('没有这个方法');
end;
http://community.csdn.net/Expert/topic/5550/5550177.xml?temp=.3402826
var
i1,i2,i3,i4 : Integer;
begin
asm
mov eax,$f1
mov ecx,$f2
mov edx,$f3 mov i1,eax
mov i2,ecx
mov i3,edx
end; ShowMessage(Format('%x %x %x %x',[i1,i2,i3,i4]));
ShowMessage(Format('%x %x %x %x',[n1,n2,n3,n4]));
end;procedure TForm1.Button1Click(Sender: TObject);
var
i : Integer;
begin
i := f(1,2,3,4);
end;
Unit1.pas.49: i := f(1,2,3,4);
00452A84 6A04 push $04
00452A86 B903000000 mov ecx,$00000003
00452A8B BA02000000 mov edx,$00000002
00452A90 B801000000 mov eax,$00000001
00452A95 E8DAFEFFFF call f
Unit1.pas.28: begin
00452974 55 push ebp
00452975 8BEC mov ebp,esp
00452977 83C4C4 add esp,-$3c
0045297A 53 push ebx
0045297B 56 push esi
0045297C 57 push edi
0045297D 33DB xor ebx,ebx
0045297F 895DC4 mov [ebp-$3c],ebx
00452982 895DE8 mov [ebp-$18],ebx
00452985 8BF9 mov edi,ecx
00452987 8BF2 mov esi,edx
00452989 8BD8 mov ebx,eax
0045298B 33C0 xor eax,eax
0045298D 55 push ebp
0045298E 68572A4500 push $00452a57
00452993 64FF30 push dword ptr fs:[eax]
00452996 648920 mov fs:[eax],esp
函数在执行begin时,已经将参数从edx,ecx,eax中放到了其它寄存器
在begin以后,edx,ecx,eax存放的已经不是参数了
即使能从edx,ecx,eax中得到东西,也仅仅是巧合而已
“函数”“过程”是高级语言的特性,
参数个数是否必须固定,是否强制类型检查都是由语言本身约定的,
参数个数检查、类型检查这些工作都是由编译器完成,
编译器如何得知参数个数,如何进行类型检查,只有作者最清楚
目前还没听说Delphi提供了这种能让我们检查参数个数的机制LZ第二个问题:
对于类中的成员方法,是可以通过名字去执行这个成员方法的
lihuasoft(坐井观天)已经回答了
那段反汇编是从我机器上得到的,
装了cnpack以后,可以将CPU窗口中的代码复制到剪贴板register是delphi的默认调用方式,有没有register都是一样的
函数在执行begin时,已经将参数从edx,ecx,eax中放到了其它寄存器
在begin以后,edx,ecx,eax存放的已经不是参数了
即使能从edx,ecx,eax中得到东西,也仅仅是巧合而已------------------------不过你这样说, 的确容易误导他. 对于汇编子程序(不是说高级语言的函数和方法)来说,“参数”(应该说子程序需要的数据)的确是通过上述三个寄存器或栈传递的。至于子程序内部又对寄存器进行处理,那是另外一回事了。不是巧合。
对于你在那篇帖子最后的代码,是巧合还是必然,还得另外证明不过直觉上巧合的可能要大
在这个帖子里讨论另一个帖子,也不太合适
-_- zzzzz
我的意见:对于编译后的机器码,如果翻译为汇编助记符,对于其中的一个汇编子程序来说,子程序需要的数据(可以用“参数”来形容它)是通过寄存器或栈传递到汇编子程序中的。(附:“参数”个数及使用方法已在编译时确定)
对此,大头鸟在前面举了一个例子,用以说明“子程序内部从eax,edx,ecx中获得正确的‘参数’值只是巧合”。
我看了一下大头鸟的例子,的确,ShowMessage(Format('%x %x %x %x',[n1,n2,n3,n4]));时,n1,n2,n3,n4值的获得,不是通过上述寄存器,而是通过栈。
我是这样理解的:Delphi编译器“优先”(注意,是“优先”不是“绝对”!)使用eax,edx,ecx寄存器传参。但Delphi不可能,任何工具也不可能做得比人脑还要智能、灵活;所以,当子程序内部调用Delphi内建函数(很多的,不仅仅Format)时,需要使用eax,edx,ecx等寄存器向内建函数传参,内建函数返回后,这时,Delphi就无法从eax等寄存器中得到参数值了,这样,Delphi就无法保证“优先”使用寄存器传参了,于是改为栈传递。可以看一下,ShowMessage(Format('%x %x %x %x',[n1,n2,n3,n4]));时,n1-n4的值应该是通过栈顶偏移获得的。
大头鸟做的是在使用参数前,人为地破坏三个寄存器的现值。这与我说的调用内建函数占用寄存器,是一样的效果。
我的理解,这并不能说明Delphi过程的参数不是通过寄存器和栈传递的。只能说,Delphi“优先”使用寄存器传参,无法保证“优先”时,才使用栈。 Delphi能够保证“优先”使用寄存器传参时,不是“巧合”。 另外,关于我那个相关贴子里最后几层的代码,需要注意,我用的是全汇编。另外,前面本贴我用马甲回贴时已有“不安全”之说。^_^
我是主张尽量不要在Delphi里用汇编的(很可能我们写的汇编代码不如Delphi生成的汇编代码简洁高效)
push ebp
mov ebp,esp
add esp,-$3c
push ebx '注意这里三个入栈操作
push esi '这是为了后面要使用这三个
push edi '寄存器暂存前三个参数值(From eax,edx,ecx)
xor ebx,ebx
mov [ebp-$3c],ebx
mov [ebp-$18],ebx
mov edi,ecx '因为下面要人为地破坏
mov esi,edx 'eax,edx,ecx的值,所以
mov ebx,eax '此处做了值备份(即大头鸟说的“函数在执行begin时,已经将参数从edx,ecx,eax中放到了其它寄存器”) xor eax,eax
push ebp
push $00452a57
push dword ptr fs:[eax]
mov fs:[eax],esp
mov eax,$000000f1 '开始进行人为破坏
mov ecx,$000000f2 '
mov edx,$000000f3 '破坏完毕
mov [ebp-$04],eax '对位于栈上的局部变量赋值
mov [ebp-$08],ecx '
mov [ebp-$0c],edx '
//此处略去ShowMessage(Format('%x %x %x %x',[i1,i2,i3,i4]));的汇编码
//在此期间未用到edi,esi,ebx
//以下是ShowMessage(Format('%x %x %x %x',[n1,n2,n3,n4]));
lea eax,[ebp-$3c]
push eax
mov [ebp-$38],ebx '注意! 从ebx取值! ebx值从何来? 返回看一下mov ebx,eax那句
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mov byte ptr [ebp-$34],$00
mov [ebp-$30],esi '注意! ....
mov byte ptr [ebp-$2c],$00
mov [ebp-$28],edi '注意! ....
mov byte ptr [ebp-$24],$00
mov eax,[ebp+$08] '这是第四个参数, 直接栈顶偏移获得
mov [ebp-$20],eax
mov byte ptr [ebp-$1c],$00
lea edx,[ebp-$38] '下面开始准备进入Format子程序了
mov ecx,$00000003 '...
mov eax,$004521d8 '...
call Format
mov eax,[ebp-$3c] '下面开始准备进入ShowMessage子程序了
call ShowMessage
//....下面的出栈等操作略可见, 前三个参数值的的确确是通过这三个寄存器传入了函数。传入后,eax,edx,ecx每时每刻都有每时每刻的用处。在需要备份其值以备后用时,Delphi会为其暂存在其他寄存器中的。
使用register传递参数时,将前三个参数放入eax,ecx,edx,其余参数从左到右入栈
这一过程,是发生在函数被调用的时候i := f(i1,i2,i3,i4);2、在函数的begin执行时,eax,ecx,edx的数据会被保存到其它寄存器,
而eax,ecx,edx本身则被“挪用”做别的事情
在函数内部,期望从eax,ecx,edx中取得参数值的做法,是不可靠的,
因为我们无法保证不会产生类似于
asm
mov eax,$f1
mov ecx,$f2
mov edx,$f3
这种改写eax,ecx,edx的动作
某些情况下我们之所以能从eax,ecx,edx中取得参数值,
仅仅是因为编译器“不想理会”eax,ecx,edx,
设想一下:哪天编译器心情好了,去改一下eax,ecx,edx
我们从eax,ecx,edx中取得参数值的代码立即会失效
所以我认为函数内部能够从eax,ecx,edx中取得参数值,仅仅是“巧合”
不能作为一个“办法”3、这篇帖子有一个问题和那篇帖子相关,
就“任意类型”以及“可变数量”参数的函数问题
我的观点是:使用variant、无类型参数、指针,可以使Delphi函数支持“任意类型”参数的
而函数参数“可变数量”,从BCB和Delphi对Format函数的定义可以看出来
C/C++支持这种特性,Delphi是不支持的,
Delphi在语法上就没有提供这种机制,得通过数组一类的模拟
1. MethodAddress只能得到pulished 方法或属性的地址, 否则得不到;
2. 从汇编码找不到解决方法, 要从RTII(runtime type information) 入手, 该方法是published可以得到的, <<delphi6开发人员指南>>与<<Inside VCL>>中有相关的资料;
3.反射(reflect)是Java, .net才有的概念在 delphi for win32中没有的.
继续>>>>
而函数参数“可变数量”,从BCB和Delphi对Format函数的定义可以看出来
C/C++支持这种特性,Delphi是不支持的,
Delphi在语法上就没有提供这种机制,得通过数组一类的模拟Delphi不是支持吗? Format后面不就是可变数量和类型吗?? array of const
第二个参数是一个数组用BCB里的Format说明C/C++支持可变参数不太合适
可以看一下printf,就是一个可变参数的函数
int printf( const char* format, ...);Delphi对于这种可变参数的特性,没有提供语法层面支持,
而是在编译器上提供的“特殊支持”,这一点从WriteLn可以证明,
帮助里是这么写的:
procedure WriteLn([ var F: Text; ] P1 [, P2, ...,Pn ] );
但实际上Delphi根本没有这种语法
一个函数/过程里.有几个参数.怎么知道?每个参数的类型又是什么...都要如何知道...代码实现.-----
* 如果是问编译器如何知道,那当然完全是根据源程序了.
例如,如果你在程序中写了一句
writeln(a,b);
输出语句,编译器根据该语句括号中的“a,b”可知道是2个参数,
至于a,b是什么类型,则要查此前的var语句中对变元a,b的说明了。* 如果是一个程序员想用某个函数/过程,但不知道几个参数,它们
又是什么类型,那当然应该查可靠的资料,如“用户手册”或“联机帮助”。
2.类中取函数: 如有:
ta=class
public
sum(x,y:Integer):integer;
end
那根据字符串"sum"能知道函数sum的存在? 如果知道..又如何根据 字符串"sum" 调用 sum函数吗?
---这明显是在问:编译器如何具体实现函数/过程的编译了。
编译技术虽然有一定繁琐(所以中国至今没有人能开发一个实际可用商品编译器),
但应该说,其原理实际并不难了解,是万全可以想得通的事情。例如:函数sum存在与否?
如果sum()是delphi本身提供的函数(系统函数),当然知道;
如果sum()不是delphi提供的系统函数,则一定是用户自己定义的;
如果用户自己预先没有定义sum,而在某处使用(调用)了sum,
则在编译过程中,由于查不到sum的原型,证明是非法调用,就可报错。