函数strspn的实现——Ada应用实例之八

 

strspn是C语言中的一个函数,它的功能是计算字符串s1中第一个在字符串s2中没有出现过的字符的位置。一帖子讨论了strspn的如下实现算法(本文修改了参数名称):

int strspn (unsigned char *s1, unsigned char *s2)           
{
    unsigned char map[32];
    int count;
 
    for (count = 0; count < 32; count++)
        map[count] = 0;
    while (*s2)
    {
        map[*s2 >> 3] |= (1 << (*s2 & 7));
        s2++;
    }
    
    count = 0;
    if (*s1)
    {
        while (map[*s1 >> 3] & (1 << (*s1 & 7)))
        {
            count++;
            s1++;
        }
        
        return(count);
    }
    
    return(0);
}

 

该算法的巧妙之处是map的设计和使用。map是一个由32个字节组成的数组,它相当于有256个二进制位,每位映射到一个ASCII码。每个ASCII码(设为c)有8位,把它分为2部分,低3位构成下标j(通过c & 7得到),高5位构成下标i(通过c>>3得到),下标j就是在map[i]中的第j位。

例如,对于字符‘1’,其ASCII码是0x31,>>3得6,&7得1,也就是它在map[6]的第1位,通过1 <<1得1,然后与map[6]相与。

假如使用Ada来实现strspn,有如下代码:

package body strspn is
   type ascii_map_type is array (Character'Range) of Boolean;
   pragma Pack (ascii_map_type);  --用紧凑方式为ascii_map_type分配内存,这样只需256BIT
 
   function strspn (s1 : in String; s2 : in String) return Integer is
      position : Integer;
      map : ascii_map_type;
   begin
      for i in map'Range loop --初始化
         map (i) := False;
      end loop;
 
      for i in s2'Range loop --以s2中每个字符为下标在map中置True
         map (s2 (i)) := True;
      end loop;
 
      for i in s1'Range loop
         if map (s1 (i)) = False then --以s1中每个字符为下标判断它是否在s2中出现过
            position := i;
            exit;
         end if;
      end loop;
 
      return position;
   end strspn;
end strspn;

 

Ada字符串中字符的位置默认从1开始计数,而C是从0开始。因此上述程序的结果比C程序的结果多1。

用Ada编写的函数strspn比C程序易于理解,Ada编译生成的目标码可与C程序媲美。其中对map的置值或判断也是通过>>3和&7实现的。以下是以s2中每个字符为下标在map中置True的目标码:

.text:00401664                     mov     ebx, esi
.text:00401666                     mov     eax, [ebp+var_38]
.text:00401669                     mov     edi, 1
.text:0040166E                     mov     esi, [ebp+var_44]
.text:00401671                     mov     [ebp+var_4C], eax
.text:00401674                     jmp     short loc_40167E
.text:00401676 loc_401676:                        ; CODE XREF: sub_401600+97j
.text:00401676                     mov     ecx, [ebp+var_4C]
.text:00401679                     inc     ebx
.text:0040167A                    inc     ecx
.text:0040167B                     mov     [ebp+var_4C], ecx
.text:0040167E loc_40167E:                       ; CODE XREF: sub_401600+74j
.text:0040167E                     mov     edx, [ebp+var_4C]
.text:00401681                     xor     ecx, ecx
.text:00401683                     mov     cl, [edx]
.text:00401685                     mov     edx, edi
.text:00401687                     mov     eax, ecx
.text:00401689                     and     ecx, 7
.text:0040168C                     shr     eax, 3
.text:0040168F                     shl     edx, cl
.text:00401691                     or      [ebp+eax+var_30], dl
.text:00401695                     cmp     ebx, esi
.text:00401697                     jnz     short loc_401676