首先声明,我不是来钻牛角尖,抬杠子的,我的目的是不要误导提问的朋友,按你的写法即不省时间,也不提高运行速度,只是带来错误的隐患.
最初由 蓝色光芒 发布
//直接用指针来实现
procedure FillBitmapInfoHeader(pImgData: PBitmapInfoHeader; ImgSize:Integer);
var
i : Integer;
begin
for i := 0 to ImgSize - 1 do
PByte(Pointer(DWORD(pImgData) + i))^ := 255 - PByte(Pointer(DWORD(pImgData) + i))^;
end;
关于指针问题的探讨:
在语句
PByte(Pointer(DWORD(pImgData) + i))^ := 255 - PByte(Pointer(DWORD(pImgData) + i))^;
Pointer 是多余的,可以去掉.
写成如下格式:
PByte(DWORD(pImgData) + i)^ := 255 - PByte(DWORD(pImgData) + i)^;
这两个语句编译后产生的代码完全相同.
表面上看这个语句似乎是正确的,其实这个语句中使用 DWORD 是错误的,应该使用 Integer;
使用 DWORD 编译器会出现如下警告:
[Warning] Unit1.pas(73): Combining signed and unsigned types - widened both operands
粗看似乎只是有符号数和无符号数的定义问题,其实问题远没有这么简单,我们来看看使用 DWORD 后产生的编译代码:
CODE:00401ACC FillBitmapInfoHeader3 proc near ; CODE XREF: _TForm1_Button1Click+3Ap
CODE:00401ACC
CODE:00401ACC var_18 = dword ptr -18h
CODE:00401ACC var_14 = dword ptr -14h
CODE:00401ACC ImgSize1 = dword ptr -10h
CODE:00401ACC i = dword ptr -0Ch
CODE:00401ACC ImgSize = dword ptr -8
CODE:00401ACC pImgData = dword ptr -4
CODE:00401ACC
CODE:00401ACC push ebp
CODE:00401ACD mov ebp, esp
CODE:00401ACF add esp, 0FFFFFFF0h
CODE:00401AD2 mov [ebp+ImgSize], edx
CODE:00401AD5 mov [ebp+pImgData], eax
CODE:00401AD8 mov eax, [ebp+ImgSize]
CODE:00401ADB dec eax
CODE:00401ADC test eax, eax
CODE:00401ADE jl short loc_401B23
CODE:00401AE0 inc eax
CODE:00401AE1 mov [ebp+ImgSize1], eax
CODE:00401AE4 mov [ebp+i], 0
CODE:00401AEB
CODE:00401AEB loc_401AEB: ; CODE XREF: FillBitmapInfoHeader3+55j
CODE:00401AEB mov eax, [ebp+pImgData]
CODE:00401AEE xor edx, edx
;======================= 这里产生错误代码也是垃圾代码 =========================
CODE:00401AF0 push edx
CODE:00401AF1 push eax
CODE:00401AF2 mov eax, [ebp+i]
CODE:00401AF5 cdq
CODE:00401AF6 add eax, [esp+18h+var_18]
CODE:00401AF9 adc edx, [esp+18h+var_14]
CODE:00401AFD add esp, 8
;==========================================================================
CODE:00401B00 mov cl, 0FFh
CODE:00401B02 sub cl, [eax]
CODE:00401B04 mov eax, [ebp+pImgData]
CODE:00401B07 xor edx, edx
;======================= 这里产生错误代码也是垃圾代码 =========================
CODE:00401B09 push edx
CODE:00401B0A push eax
CODE:00401B0B mov eax, [ebp+i]
CODE:00401B0E cdq
CODE:00401B0F add eax, [esp+18h+var_18]
CODE:00401B12 adc edx, [esp+18h+var_14]
CODE:00401B16 add esp, 8
;==========================================================================
CODE:00401B19 mov [eax], cl
CODE:00401B1B inc [ebp+i]
CODE:00401B1E dec [ebp+ImgSize1]
CODE:00401B21 jnz short loc_401AEB
CODE:00401B23
CODE:00401B23 loc_401B23: ; CODE XREF: FillBitmapInfoHeader3+12j
CODE:00401B23 mov esp, ebp
CODE:00401B25 pop ebp
CODE:00401B26 retn
CODE:00401B26 FillBitmapInfoHeader3 endp
编译器认为 pImgData 是 Qwrod 而不是 Dword, 所以对循环变量 i 进行处理, 将其扩展为 Qword, 使用 cdq 指令.但不知为何编译器扩展了 i 为Qword,却没有使用这个扩展的结果,显然你的写法把编译器弄糊涂了.
再看将 Dword 改为 Integer 以后产生的正确的编译代码:
CODE:00401B28 FillBitmapInfoHeader4 proc near ; CODE XREF: _TForm1_Button1Click+45p
CODE:00401B28
CODE:00401B28 ImgSize1 = dword ptr -10h
CODE:00401B28 i = dword ptr -0Ch
CODE:00401B28 ImgSize = dword ptr -8
CODE:00401B28 pImgData = dword ptr -4
CODE:00401B28
CODE:00401B28 push ebp
CODE:00401B29 mov ebp, esp
CODE:00401B2B add esp, 0FFFFFFF0h
CODE:00401B2E mov [ebp+ImgSize], edx
CODE:00401B31 mov [ebp+pImgData], eax
CODE:00401B34 mov eax, [ebp+ImgSize]
CODE:00401B37 dec eax
CODE:00401B38 test eax, eax
CODE:00401B3A jl short loc_401B61
CODE:00401B3C inc eax
CODE:00401B3D mov [ebp+ImgSize1], eax
CODE:00401B40 mov [ebp+i], 0
CODE:00401B47
CODE:00401B47 loc_401B47: ; CODE XREF: FillBitmapInfoHeader4+37j
CODE:00401B47 mov eax, [ebp+pImgData]
CODE:00401B4A add eax, [ebp+i]
CODE:00401B4D mov dl, 0FFh
CODE:00401B4F sub dl, [eax]
CODE:00401B51 mov eax, [ebp+pImgData]
CODE:00401B54 add eax, [ebp+i]
CODE:00401B57 mov [eax], dl
CODE:00401B59 inc [ebp+i]
CODE:00401B5C dec [ebp+ImgSize1]
CODE:00401B5F jnz short loc_401B47
CODE:00401B61
CODE:00401B61 loc_401B61: ; CODE XREF: FillBitmapInfoHeader4+12j
CODE:00401B61 mov esp, ebp
CODE:00401B63 pop ebp
CODE:00401B64 retn
CODE:00401B64 FillBitmapInfoHeader4 endp
再看我原帖的语句:
procedure FillBitmapInfoHeader(pImgData: PBitmapInfoHeader; ImgSize:Integer);
var
lpImgData: PChar;
i : Integer;
begin
lpImgData := PChar(pImgData); //我在这里这样写是为了让提问题的朋友容易理解
for i := 0 to ImgSize - 1 do
PByte(lpImgData + i)^ := 255 - PByte(lpImgData + i)^;
end;
将这句改成如下形式,只是简单的合并同类项:
procedure FillBitmapInfoHeader5(pImgData: PBitmapInfoHeader; ImgSize:Integer);
var
i : Integer;
begin
for i := 0 to ImgSize - 1 do
PByte(PChar(pImgData) + i)^ := 255 - PByte(PChar(pImgData) + i)^;
end;
这段程序编译后和上面使用 Integer 的结果完全相同, 但从阅读和理解显然比使用 Integer 来得好.
最后我们来欣赏汇编程序产生的代码:
CODE:00401A30 FillBitmapInfoHeader proc near ; CODE XREF: _TForm1_Button1Click+19p
CODE:00401A30 pusha
CODE:00401A32 mov esi, eax
CODE:00401A34 mov ecx, edx
CODE:00401A36
CODE:00401A36 loc_401A36: ; CODE XREF: FillBitmapInfoHeader+10j
CODE:00401A36 lodsb
CODE:00401A37 mov ah, 0FFh
CODE:00401A39 sub ah, al
CODE:00401A3B mov [esi-1], ah
CODE:00401A3E dec cx
CODE:00401A40 loop loc_401A36
CODE:00401A42 popa
CODE:00401A44 retn
CODE:00401A44 FillBitmapInfoHeader endp
很明显,这段代码的运行速度最快的.
如果勤快一点, 将压栈语句改成
push esi
push ecx
......
......
pop ecx
pop esi
这样代码看起来长了,但实际运行更快,而且少占堆栈空间.