• 汇编语言TYPE和SIZEOF运算符:引用结构变量和结构名称

    使用 TYPE 和 SIZEOF 运算符可以引用结构变量和结构名称。例如,现在回到之前的 Employee 结构:

    Employee STRUCT
        IdNum BYTE "000000000"      ; 9
        LastName BYTE 30 DUP(0)     ; 30
        ALIGN WORD                  ; 加 1 字节
        Years WORD 0                ; 2
        ALIGN DWORD                 ; 加 2 字节
        SalaryHistory DWORD 0,0,0,0 ; 16
    Employee ENDS                   ; 共 60 字节

    给定数据定义:

    .data
    worker Employee <>

    则下列所有表达式返回的值都相同:

    TYPE Employee                ; 60
    SIZEOF Employee            ; 60
    SIZEOF worker                ; 60

    TYPE 运算符返回的是标识符存储类型(BYTE、WORD、DWORD 等)的字节数。LENGTHOF 运算符返回的是数组元素的个数。SIZEOF 运算符则为 LENGTHOF 与 TYPE 的乘积。

    1) 引用成员

    引用已命名的结构成员时,需要用结构变量作为限定符。以 Employee 结构为例,在汇编时能生成下述常量表达式:

    TYPE Employee.SalaryHistory                        ; 4
    LENGTHOF Employee.SalaryHistory              ; 4
    SIZEOF Employee.SalaryHistory                     ; 16
    TYPE Employee.Years                                     ; 2

    以下为对 worker(一个 Employee)的运行时引用:

    .data
    worker Employee <>
    .code
    mov dx,worker.Years
    mov worker.SalaryHistory, 20000              ;第一个工资
    mov [worker.SalaryHistory+4 ], 30000         ;第二个工资

    使用 OFFSET 运算符能获得结构变量中一个字段的地址:

    mov edx,OFFSET worker.LastName

    2) 间接和变址操作数

    间接操作数用寄存器(如 ESI)对结构成员寻址。间接寻址具有灵活性,尤其是在向过程传递结构地址或者使用结构数组的情况下。引用间接操作数时需要 PTR 运算符:

    mov esi,OFFSET worker
    mov ax,(Employee PTR [esi]).Years

    下面的语句不能汇编,原因是 Years 自身不能表明它所属的结构:

    mov ax, [esi].Years    ;无效

    变址操作数

    用变址操作数可以访问结构数组。假设 department 是一个包含 5 个 Employee 对象的数组。下述语句访问的是索引位置为 1 的雇员的 Years 字段:

    .data
    department Employee 5 DUP(<>)
    .code
    mov esi, TYPE Employee              ; 索引 = 1
    mov department[esi].Years, 4

    数组循环

    带间接或变址寻址的循环可以用于处理结构数组。下面的程序 (AllPoints.asm)为 AllPoints 数组分配坐标:

    ; 数组循环       (AllPoints.asm)
    INCLUDE Irvine32.inc
    NumPoints = 3
    .data
    ALIGN WORD
    AllPoints COORD NumPoints DUP(<0,0>)
    
    .code
    main PROC
        mov edi,0                    ; 数组索引
        mov ecx,NumPoints            ; 循环计数器
        mov ax,1                     ; 起始 X, Y 的值
    
    L1:    mov (COORD PTR AllPoints[edi]).X,ax
        mov (COORD PTR AllPoints[edi]).Y,ax
        add edi,TYPE COORD
        inc ax
        loop L1
    
        exit
    main ENDP
    END main

    3) 对齐的结构成员的性能

    之前已经断言,处理器访问正确对齐的结构成员时效率更高。那么,非对齐字段会对性能产生多大影响呢?现在使用本章介绍的 Employee 结构的两种不同版本,进行一个简单的测试。测试将对第一个版本进行重命名,以便两种版本能在同一个程序中使用:

    EmployeeBad STRUCT   
        Idnum    BYTE "000000000"
        Lastname BYTE 30 DUP(0)
        Years    WORD 0
        SalaryHistory DWORD 0,0,0,0
    EmployeeBad ENDS               
    
    Employee STRUCT   
        Idnum    BYTE "000000000"
        Lastname BYTE 30 DUP(0)
        ALIGN    WORD
        Years    WORD 0
        ALIGN    DWORD
        SalaryHistory DWORD 0,0,0,0
    Employee ENDS

    下面的代码首先获取系统时间,再执行循环以访问结构字段,最后计算执行花费的时 间。变量 emp 可以声明为 Employee 对象或者 EmployeeBad 对象:

    .data
    ALIGN DWORD
    startTime DWORD ?                ; 对齐 startTime
    emp EmployeeBad <>               ; 或: EmployeeBad
    
    .code
        call    GetMSeconds          ; 获取系统时间
        mov    startTime,eax
       
        mov    ecx,0FFFFFFFFh        ; 循环计数器
    L1:    mov    emp.Years,5
        mov    emp.SalaryHistory,35000
        loop    L1
    
        call    GetMSeconds        ; 获取开始时间
        sub    eax,startTime
        call    WriteDec           ; 显示执行花费的时间

    在这个简单的测试程序中,使用正确对齐的 Employee 结构的执行时间为 6141 毫秒,而使用 EmployeeBad 结构的执行时间为 6203 毫秒。两者相差不大 (62 毫秒),可能是因为处理器的内存 cache 将对齐问题最小化了。

更多...

加载中...