[原]深析DELPHI in 操作实现

时间:2022-01-05 12:29:16
群友探讨到这个问题顺便有空分析了下delphi编译器的实现方式,有错误地方敬请指出。
function AnsiStrInCharSet(ch: char; CharSet: TSysCharSet): boolean;
begin
  Result := ch in CharSet;
end;
测试调用:if AnsiStrInCharSet('a', ['a'..'z']) then ... 

实际编译器翻译为以下代码:
0045BF7A  |.  F3:A5         rep     movs dword ptr es:[edi], dword ptr [esi] ;  
复制ESI( ['a'..'z']) 到BT指令标志判断dest操作数,也就是CharSet: TSysCharSet 的内容由编译器固定编译为一个位信息常量,该常量长度为32byte,定义结构为:

TBitInfoField =array [0..7] of dword;

暂称该常量为BitInfoField:TBitInfoField 具体规则后面讲述。

0045BF7C  |.  0FB6C0        movzx   eax, al                                  ;  
赋值到BT指令标志判断src操作数,也就是ch: char

0045BF7F  |.  0FA345 E0     bt      dword ptr [ebp-20], eax                  ; 
[ebp-20] 实际指向BitInfoField,eax是位数,作用就是把BitInfoField的第eax位送CF标志

比如['a'..'z']编译器编译的BitInfoField为:
0012F588  00 00 00 00 00 00 00 00 00 00 00 00 FE FF FF 07  ............? 
0012F598  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

[#0..#255]编译器编译的BitInfoField为:
0012F588  FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  
0012F598  FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  

为什么编译器会这么编译呢?因为bt指令是一个标志测试指令具体语法格式为:
 BT r/m16,r16
 将所选的位存储到 CF 标志
 
 BT r/m32,r32
 将所选的位存储到 CF 标志
 
 BT r/m16,imm8
 将所选的位存储到 CF 标志
 
 BT r/m32,imm8
 将所选的位存储到 CF 标志
 这里有两个公式确定位值:

 1. BIdx= BitOffset div 32 

 2. BBit= BitOffset  mod 32

 


我们这里的in编译器使用的是 BT m32,r32格式,它的作用就是把m32内存中BitInfoField[BIdx(r32)]常量的数据的第BitInfoField[BBit(r32)]位送CF标志.
假设枚举范围是['a'..'z'] 则BitInfoField为:

 00 00 00 00 00 00 00 00 00 00 00 00  FE FF FF 07  
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  

 

'a' in ['a'..'z']  最后转换成的CPU指令应该是:

bt      dword ptr [BitInfoField] , eax ; eax= $61

 

因为 'a'的Ascll码为:$61 根据公式: BIdx = $61 div 32 = 3   , BBit= BitOffset  mod 32 =1 

BitInfoField[BIdx=3 ] = $7FFFFFE 转换为二进制为:00000111111111111111111111111110

BBit = 1

00000111111111111111111111111110

31-------------------------------0

那么 bt      dword ptr [BitInfoField] , $61 就是把下面标志传送到CF标志寄存器


000001111111111111111111111111 [1]0

31-------------------------------0

所以CF =1


这样就完成了'a' in ['a'..'z']  的判断。

根据此原理可以推导出 delphi的 char in ['a'..'z']  和  char in ['a'..'z','1'..'9'] 执行效率是一样的

因为['a'..'z','1'..'9'] 的BitInfoField为:

00 00 00 00   00 00 FE 03   00 00 00 00   FE FF FF 07 

00 00 00 00   00 00 00 00   00 00 00 00   00 00 00 00



当判断任意字符  bt      dword ptr [BitInfoField] , BitOffset 

通过公式:


 1. BIdx= BitOffset div 32 

 2. BBit= BitOffset  mod 32


计算出BIdx 和BBit 只需把相应标志位传送到CF即完成。

总结DELPHI in 操作效率比pos操作效率要高很多,并且效率不被枚举元素个数和多少影响,缺点是只能判断0-255范围的数据。

86 个解决方案

#1


顶。。。。。

#2


不懂汇编,牛人.

#3


in跟pos的应用范围不一样,硬拿他们比较没有意义

#4


不是比较,是弄清楚编译器实现in的本质。

#5


该回复于2010-07-27 17:50:27被版主删除

#6


来句庸俗的."华丽的插入前十!"

#7


 

#8


我是个菜鸟 Delphi是开发语言还是开发环境啊

#9


我只是路过看看的。

#10


当范围小的时候应该会更快,比如如果是 in [0..7],后面那个 set 常量只会占用一个字节

#11


支持一下!

#12


引用 10 楼 iccv9 的回复:
当范围小的时候应该会更快,比如如果是 in [0..7],后面那个 set 常量只会占用一个字节


如果是直接死值那么的确是占用更小
但如果是使用我上面那个函数
function AnsiStrInCharSet(ch: char; CharSet: TSysCharSet): boolean;
begin
  Result := ch in CharSet;
end;
那么
AnsiStrInCharSet(‘’,[0..n])这种方式的话是一样的,那个常量还是32byte

#13


上面说错了,如果是直接死值那么in的实现方式又是另一种,不使用bt指令所以不存在那个常量值

#14


中国很少有用DELPHI

#15


相信Delphi能再造辉煌。顶顶顶顶顶!

#16


这个是什么,好复杂啊

#17


该回复于2010-07-28 10:50:07被版主删除

#18


好帖。

#19


支持一下!

#20


晕,没看懂~

#21


该回复于2010-07-28 14:17:35被版主删除

#22


回帖有分拿、、、

#23


虽然看不懂,还是顶顶,赚分吧

#24


学习学习

#25


该回复于2010-08-03 09:11:20被版主删除

#26


该回复于2010-07-28 14:24:56被版主删除

#27


学习学习  顶顶

#28


顶你!~~!

#29


谢谢~~!

#30


..............

#31


不错 不错 非常不错 哈哈

#32


我相信你是分析完了, 但是你的写法只会让初学的人一头雾水. 建议这样的文章应该写的通俗易懂些, 才能给大家分享

#33


楼上说的很有道理,我就是初学者!

#34


我还能看得懂,就是排版比较凌乱。

#35


啊三是事实是事实是事实是事实是事实是事实是事实是事实收拾收拾

#36


学习!!!

#37


学习学习

#38


[原]深析DELPHI in 操作实现

#39


该回复于2010-12-16 13:54:41被版主删除

#40


讲得太深了

#41


来句庸俗的."华丽的插入前十!"

#42


每天回帖即可获得10分可用分!

#43


每天回帖即可获得10分可用分!

#44


每天回帖即可获得10分可用分!

#45


 其实,我想要5分,下载 CFS 视频破解器。。给我分吧

#46


不懂汇编

#47


学习支持那份

#48


好好。。。。。。。

#49


每天回帖即可获得10分可用分!

#50


是很好的东西!!!1

#1


顶。。。。。

#2


不懂汇编,牛人.

#3


in跟pos的应用范围不一样,硬拿他们比较没有意义

#4


不是比较,是弄清楚编译器实现in的本质。

#5


该回复于2010-07-27 17:50:27被版主删除

#6


来句庸俗的."华丽的插入前十!"

#7


 

#8


我是个菜鸟 Delphi是开发语言还是开发环境啊

#9


我只是路过看看的。

#10


当范围小的时候应该会更快,比如如果是 in [0..7],后面那个 set 常量只会占用一个字节

#11


支持一下!

#12


引用 10 楼 iccv9 的回复:
当范围小的时候应该会更快,比如如果是 in [0..7],后面那个 set 常量只会占用一个字节


如果是直接死值那么的确是占用更小
但如果是使用我上面那个函数
function AnsiStrInCharSet(ch: char; CharSet: TSysCharSet): boolean;
begin
  Result := ch in CharSet;
end;
那么
AnsiStrInCharSet(‘’,[0..n])这种方式的话是一样的,那个常量还是32byte

#13


上面说错了,如果是直接死值那么in的实现方式又是另一种,不使用bt指令所以不存在那个常量值

#14


中国很少有用DELPHI

#15


相信Delphi能再造辉煌。顶顶顶顶顶!

#16


这个是什么,好复杂啊

#17


该回复于2010-07-28 10:50:07被版主删除

#18


好帖。

#19


支持一下!

#20


晕,没看懂~

#21


该回复于2010-07-28 14:17:35被版主删除

#22


回帖有分拿、、、

#23


虽然看不懂,还是顶顶,赚分吧

#24


学习学习

#25


该回复于2010-08-03 09:11:20被版主删除

#26


该回复于2010-07-28 14:24:56被版主删除

#27


学习学习  顶顶

#28


顶你!~~!

#29


谢谢~~!

#30


..............

#31


不错 不错 非常不错 哈哈

#32


我相信你是分析完了, 但是你的写法只会让初学的人一头雾水. 建议这样的文章应该写的通俗易懂些, 才能给大家分享

#33


楼上说的很有道理,我就是初学者!

#34


我还能看得懂,就是排版比较凌乱。

#35


啊三是事实是事实是事实是事实是事实是事实是事实是事实收拾收拾

#36


学习!!!

#37


学习学习

#38


[原]深析DELPHI in 操作实现

#39


该回复于2010-12-16 13:54:41被版主删除

#40


讲得太深了

#41


来句庸俗的."华丽的插入前十!"

#42


每天回帖即可获得10分可用分!

#43


每天回帖即可获得10分可用分!

#44


每天回帖即可获得10分可用分!

#45


 其实,我想要5分,下载 CFS 视频破解器。。给我分吧

#46


不懂汇编

#47


学习支持那份

#48


好好。。。。。。。

#49


每天回帖即可获得10分可用分!

#50


是很好的东西!!!1