<?xml version="1.0" encoding="gb2312"?>
<rss version="2.0">
<channel>
<title><![CDATA[他山之石，可以攻玉]]></title>
        <image>
        <title>http://hi.baidu.com</title>
        <link>http://hi.baidu.com</link>
        <url>http://img.baidu.com/img/logo-hi.gif</url>
        </image>
<description><![CDATA[集韵增广 多见多闻 zymill@163.com]]></description>
<link>http://hi.baidu.com/learned</link>
<language>zh-cn</language>
<generator>www.baidu.com</generator>
<ttl>5</ttl>


<item>
        <title><![CDATA[Intel 汇编指令介绍]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/88b0053382a488f21b4cffdf.html]]></link>
        <description><![CDATA[
		
		<p>指令名称 指令形式 机器码 标志位<br>
MOV MOV r/m8,r8 88 /r 不影响标志位 传送指令 MOV [00459AF0],AL <br>
MOV r/m16,r16 89 /r MOV [00459AF0],AX <br>
MOV r/m32,r32 89 /r MOV [00459AF0],EAX <br>
MOV r8,r/m8 8A /r MOV AL,[00459AF0] <br>
MOV r16,r/m16 8B /r MOV AX,[00459AF0] <br>
MOV r32,r/m32 8B /r MOV EAX,[00459AF0] <br>
MOV r/m16,Sreg 8C /r MOV AX,ES <br>
MOV Sreg,r/m16 8E /r MOV ES,AX <br>
MOV AL,moffs8 A0 MOV AL,ES:[459A] <br>
MOV AX,moffs16 A1 MOV AX,ES:[459A] <br>
MOV EAX,moffs32 A1 MOV EAX,ES:[00459AF0] <br>
MOV moffs8,AL A2 MOV ES:[459A],AL <br>
MOV moffs16,AX A3 MOV ES:[459A],AX <br>
MOV moffs32,EAX A3 MOV ES:[00459AF0],EAX <br>
MOV r8,imm8 B0+rb MOV AL,F0 <br>
MOV r16,imm16 B8+rw MOV AX,9AF0 <br>
MOV r32,imm32 B8+rd MOV EAX,00459AF0 <br>
MOV r/m8,imm8 C6 /0 MOV BYTE Ptr [00459AF0],F0 <br>
MOV r/m16,imm16 C7 /0 MOV WORD Ptr [00459AF0],9AF0 <br>
MOV r/m32,imm32 C7 /0 MOV DWORD Ptr [00459AF0],00459AF0 <br>
MOV MOV CR0,r32 0F 22 /r 不影响标志位 控制寄存器传送指令 MOV CR0,EAX <br>
MOV CR2,r32 0F 22 /r MOV CR2,EAX <br>
MOV CR3,r32 0F 22 /r MOV CR3,EAX <br>
MOV CR4,r32 0F 22 /r MOV CR4,EAX <br>
MOV r32，CR0 0F 20 /r MOV EAX，CR0 <br>
MOV r32，CR2 0F 20 /r MOV EAX，CR2 <br>
MOV r32，CR3 0F 20 /r MOV EAX，CR3 <br>
MOV r32，CR4 0F 20 /r MOV EAX，CR4 <br>
MOV MOV r32, DR0-DR7 0F 21 /r 不影响标志位 调试寄存器传送指令 MOV EAX，DR0 <br>
MOV DR0-DR7,r32 0F 23 /r MOV DR0，EAX <br>
MOVD MOVD mm, r/m32 0F 6E /r 不影响标志位 双字传送指令 　 <br>
MOVD r/m32, mm 0F 7E /r 　 <br>
MOVD xmm, r/m32 66 0F 6E /r 　 <br>
MOVD r/m32, xmm 66 0F 7E /r 　 <br>
MOVQ MOVQ mm, r/m64 0F 6F /r 不影响标志位 八字节传送指令 　 <br>
MOVQ mm/m64, mm 0F 7F /r 　 <br>
MOVQ xmm1, xmm2/m64 F3 0F 7E 　 <br>
MOVQ xmm2/m64, xmm1 66 0F D6 　 <br>
MOVS MOVS m8, m8 A4 不影响标志位 字符串传送，每次传送1个字节 MOVS StrING1, StrING2 ；源串DS:(E)SI，目的串：ES:(E)DI <br>
MOVS m16, m16 A5 字符串传送，每次传送1个字 MOVS StrING1, StrING2 ；源串DS:(E)SI，目的串：ES:(E)DI <br>
MOVS m32, m32 A5 字符串传送，每次传送1个双字 MOVS StrING1, StrING2 ；源串DS:(E)SI，目的串：ES:(E)DI (386+) <br>
MOVSB A4 字符串传送，每次传送1个字节 MOVSB ；源串DS:(E)SI，目的串：ES:(E)DI <br>
MOVSW A5 字符串传送，每次传送1个字 MOVSW ；源串DS:(E)SI，目的串：ES:(E)DI <br>
MOVSD A5 字符串传送，每次传送1个双字 MOVSD ；源串DS:(E)SI，目的串：ES:(E)DI (386+) <br>
MOVSX MOVSX r16,r/m8 0F BE /r 不影响标志位 带符号扩展传送指令 MOVSX AX, BL <br>
MOVSX r32,r/m8 0F BE /r MOVSX EAX，BL <br>
MOVSX r32,r/m16 0F BF /r MOVSX EAX，BX <br>
MOVZX MOVZX r16,r/m8 0F B6 /r 不影响标志位 零扩展传送指令 MOVZX AX, BL <br>
MOVZX r32,r/m8 0F B6 /r MOVZX EAX，BL <br>
MOVZX r32,r/m16 0F B7 /r MOVZX EAX，BX <br>
MUL MUL r/m8 F6 /4 设置CF OF<br>
(SF ZF AF PF未定义) 无符号乘法：AX←AL*r/m8 MUL CL <br>
MUL r/m16 F7 /4 无符号乘法：DX:AX←AX*r/m16 MUL CX <br>
MUL r/m32 F7 /4 无符号乘法：EDX:EAX←EAX*r/m32 MUL ECX <br>
NEG NEG r/m8 F6 /3 设置CF OF SF ZF AF PF 取负：r/m8=-r/m8 NEG CL <br>
NEG r/m16 F7 /3 取负：r/m16=-r/m16 NEG CX <br>
NEG r/m32 F7 /3 取负：r/m32=-r/m32 NEG ECX <br>
NOP NOP 90 不影响标志位 空操作 NOP <br>
NOT NOT r/m8 F6 /2 设置CF OF SF ZF AF PF 按位取反：r/m8=NOT r/m8 NOT CL <br>
NOT r/m16 F7 /2 按位取反：r/m16=NOT r/m16 NOT CX <br>
NOT r/m32 F7 /2 按位取反：r/m32=NOT r/m32 NOT ECX <br>
OR OR AL, imm8 0C ib 设置 CF OF PF SF ZF 逻辑或 OR AL, 1F <br>
OR AX, imm16 0D iw OR AX, 4F80 <br>
OR EAX, imm32 0D id OR EAX, 00004F80 <br>
OR r/m8, imm8 80 /1 ib OR BYTE Ptr [006387EA], 39 <br>
OR r/m16,imm16 81 /1 iw OR WORD Ptr [006387EA], 1039 <br>
OR r/m32,imm32 81 /1 id OR DWORD Ptr [006387EA], 00001039 <br>
OR r/m16,imm8 83 /1 ib OR WORD Ptr [006387EA], 39 <br>
OR r/m32,imm8 83 /1 ib OR DWORD Ptr [006387EA], 39 <br>
OR r/m8,r8 08 /r OR [006387EA], AL <br>
OR r/m16,r16 09 /r OR [006387EA], AX <br>
OR r/m32,r32 09 /r OR [006387EA], EAX <br>
OR r8,r/m8 0A /r OR AL, [006387EA] <br>
OR r16,r/m16 0B /r OR AX, [006387EA] <br>
OR r32,r/m32 0B /r OR EAX, [006387EA] <br>
OUT OUT imm8,AL E6 ib 不影响标志位 将AL输出到imm8指定的端口 OUT E0,AL <br>
OUT imm8,AX E7 ib 将AX输出到imm8指定的端口 OUT E0,AX <br>
OUT imm8,EAX E7 ib 将EAX输出到imm8指定的端口 OUT E0,EAX <br>
OUT AL,DX EE 将AL输出到DX指定的端口 OUT DX,AL <br>
OUT AX,DX EF 将AX输出到DX指定的端口 OUT DX,AX <br>
OUT EAX,DX EF 将EAX输出到DX指定的端口 OUT DX,EAX <br>
OUTS OUTS DX,m8 6E 不影响标志位 将DS:(E)SI处的字节输出到DX指定的端口 　 <br>
OUTS DX,m16 6F 将DS:(E)SI处的字输出到DX指定的端口 　 <br>
OUTS DX,m32 6F 将DS:(E)SI处的双字输出到DX指定的端口 　 <br>
OUTSB OUTSB 6E 不影响标志位 将DS:(E)SI处的字节输出到DX指定的端口 OUTSB <br>
OUTSW OUTSW 6F 不影响标志位 将DS:(E)SI处的字输出到DX指定的端口 OUTSW <br>
OUTSD OUTSD 6F 不影响标志位 将DS:(E)SI处的双字输出到DX指定的端口 OUTSD <br>
PACKSSWB PACKSSWB mm1, mm2/m64 0F 63 /r 不影响标志位 紧缩带符号字到字节，溢出取饱和值 　 <br>
PACKSSWB xmm1,xmm2/m128 66 0F 63 /r PACKSSWB XMM0，XMM1 <br>
PACKSSDW PACKSSDW mm1, mm2/m64 0F 6B /r 不影响标志位 紧缩带符号双字到字，溢出取饱和值 　 <br>
PACKSSDW xmm1,xmm2/m128 66 0F 6B /r PACKSSDW XMM0，XMM1 <br>
PACKUSWB PACKUSWB mm1, mm2/m64 0F 67 /r 不影响标志位 紧缩无符号字到字节，溢出取饱和值 　 <br>
PACKUSWB xmm1,xmm2/m128 66 0F 67 /r PACKUSWB XMM0，XMM1 <br>
PADDB PADDB mm, mm/m64 0F FC /r 不影响标志位 紧缩字节相加，溢出则回绕 　 <br>
PADDB xmm1,xmm2/m128 66 0F FC /r PADDB XMM6，XMM7 <br>
PADDW PADDW mm, mm/m64 0F FD /r 不影响标志位 紧缩字相加，溢出则回绕 　 <br>
PADDW xmm1,xmm2/m128 66 0F FD /r PADDW XMM6，XMM7 <br>
PADDD PADDD mm, mm/m64 0F FE /r 不影响标志位 紧缩双字相加，溢出则回绕 　 <br>
PADDD xmm1,xmm2/m128 66 0F FE /r PADDD XMM6，XMM7 <br>
PADDQ PADDQ mm1, mm2/m64 0F D4 /r 不影响标志位 紧缩四字相加，溢出则回绕 　 <br>
PADDQ xmm1,xmm2/m128 66 0F D4 /r PADDQ XMM6，XMM7 <br>
PADDSB PADDSB mm, mm/m64 0F EC /r 不影响标志位 带符号紧缩字节相加，溢出取饱和值 　 <br>
PADDSB xmm1,xmm2/m128 66 0F EC /r PADDSB XMM6，XMM7 <br>
PADDSW PADDSW mm, mm/m64 0F ED /r 不影响标志位 带符号紧缩字相加，溢出取饱和值 　 <br>
PADDSW xmm1,xmm2/m128 66 0F ED /r PADDSW XMM6，XMM7 <br>
PADDUSB PADDUSB mm, mm/m64 0F DC /r 不影响标志位 无符号紧缩字节相加，溢出取饱和值 　 <br>
PADDUSB xmm1,xmm2/m128 66 0F DC /r PADDUSB XMM0，XMM1 <br>
PADDUSW PADDUSW mm, mm/m64 0F DD /r 不影响标志位 无符号紧缩字相加，溢出取饱和值 　 <br>
PADDUSW xmm1,xmm2/m128 66 0F DD /r PADDUSW XMM1，XMM2 <br>
PAND PAND mm, mm/m64 0F DB /r 不影响标志位 逻辑与操作 　 <br>
PAND xmm1,xmm2/m128 66 0F DB /r PAND XMM4，XMM1 <br>
PANDN PANDN mm, mm/m64 0F DF /r 不影响标志位 求反后与操作：DEST ←(NOT DEST) AND SRC 　 <br>
PANDN xmm1,xmm2/m128 66 0F DF /r PANDN XMM4，XMM1 <br>
PAUSE PAUSE F3 90 不影响标志位 改善处理器的&ldquo;spin-wait loop&rdquo;性能 PAUSE <br>
PAVGB PAVGB mm, mm/m64 0F E0 /r 不影响标志位 取紧缩无符号字节的平均值(四舍五入) 　 <br>
PAVGB xmm1,xmm2/m128 0F E0 /r PAVGB XMM4，XMM1 <br>
PAVGW PAVGW mm, mm/m64 0F E0 /r 不影响标志位 取紧缩无符号字的平均值(四舍五入) 　 <br>
PAVGW xmm1,xmm2/m128 0F E0 /r PAVGW XMM4，XMM1 <br>
PCMPEQB PCMPEQB mm, mm/m64 0F 74 /r 不影响标志位 紧缩字节相等比较 　 <br>
PCMPEQB xmm1,xmm2/m128 66 0F 74 /r PCMPEQB XMM4，XMM1 <br>
PCMPEQW PCMPEQW mm, mm/m64 0F 75 /r 不影响标志位 紧缩字相等比较 　 <br>
PCMPEQW xmm1,xmm2/m128 66 0F 75 /r PCMPEQW XMM4，XMM1 <br>
PCMPEQD PCMPEQD mm, mm/m64 0F 76 /r 不影响标志位 紧缩双字相等比较 　 <br>
PCMPEQD xmm1,xmm2/m128 66 0F 76 /r PCMPEQD XMM4，XMM1 <br>
PCMPGTB PCMPGTB mm, mm/m64 0F 64 /r 不影响标志位 带符号紧缩字节大于比较 　 <br>
PCMPGTB xmm1,xmm2/m128 66 0F 64 /r PCMPGTB XMM4，XMM1 <br>
PCMPGTW PCMPGTW mm, mm/m64 0F 65 /r 不影响标志位 带符号紧缩字大于比较 　 <br>
PCMPGTW xmm1,xmm2/m128 66 0F 65 /r PCMPGTW XMM4，XMM1 <br>
PCMPGtd PCMPGtd mm, mm/m64 0F 66 /r 不影响标志位 带符号紧缩双字大于比较 　 <br>
PCMPGtd xmm1,xmm2/m128 66 0F 66 /r PCMPGtd XMM4，XMM1 <br>
PMADDWD PMADDWD mm, mm/m64 0F F5 /r 不影响标志位 紧缩乘和加操作 　 <br>
PMADDWD xmm1,xmm2/m128 66 0F F5 /r PMADDWD XMM4，XMM1 <br>
PMAXSW PMAXSW mm1, mm2/m64 0F EE /r 不影响标志位 有符号字比较，返回最大值 　 <br>
PMAXSW xmm1,xmm2/m128 66 0F EE /r PMAXSW XMM4，XMM1 <br>
PMAXUB PMAXUB mm1, mm2/m64 0F DE /r 不影响标志位 无符号字比较，返回最大值 　 <br>
PMAXUB xmm1,xmm2/m128 66 0F DE /r PMAXUB XMM4，XMM1 <br>
PMINSW PMINSW mm1, mm2/m64 0F EA /r 不影响标志位 有符号字比较，返回最小值 　 <br>
PMINSW xmm1,xmm2/m128 66 0F EA /r PMINSW XMM4，XMM1 <br>
PMINUB PMINUB mm1, mm2/m64 0F DA /r 不影响标志位 无符号字比较，返回最小值 　 <br>
PMINUB xmm1,xmm2/m128 66 0F DA /r PMINUB XMM4，XMM1 <br>
PMULHUW PMULHUW mm1, mm2/m64 0F E4 /r 不影响标志位 紧缩无符号字相乘，存高位 　 <br>
PMULHUW xmm1,xmm2/m128 66 0F E4 /r PMULHUW XMM4，XMM1 <br>
PMULHW PMULHW mm1, mm2/m64 0F E5 /r 不影响标志位 紧缩有符号字相乘，存高位 　 <br>
PMULHW xmm1,xmm2/m128 66 0F E5 /r PMULHW XMM4，XMM1 <br>
PMULLW PMULLW mm1, mm2/m64 0F D5 /r 不影响标志位 紧缩有符号字相乘，存低位 　 <br>
PMULLW xmm1,xmm2/m128 66 0F D5 /r PMULLW XMM4，XMM1 <br>
PMULUDQ PMULUDQ mm1, mm2/m64 0F F4 /r 不影响标志位 无符号双字相乘，存四字 　 <br>
PMULUDQ xmm1,xmm2/m128 66 OF F4 /r PMULUDQ XMM4，XMM1 <br>
POP POP r/m16 8F /0 不影响标志位 16位数据出栈 POP WORD Ptr [006387EA] <br>
POP r/m32 8F /0 32位数据出栈 POP DWORD Ptr [006387EA] <br>
POP r16 58+rw 16位数据出栈到寄存器 POP AX <br>
POP r32 58+rd 32位数据出栈到寄存器 POP EAX <br>
POP DS 1F 数据出栈到寄存器DS POP DS <br>
POP ES 07 数据出栈到寄存器ES POP ES <br>
POP SS 17 数据出栈到寄存器SS POP SS <br>
POP FS 0F A1 数据出栈到寄存器FS POP FS <br>
POP GS 0F A9 32位数据出栈到寄存器GS POP GS <br>
POPA POPA 61 不影响标志位 从堆栈中弹出全部16位通用寄存器：DI, SI, BP, BX, DX, CX, AX POPA <br>
POPAD POPAD 61 不影响标志位 从堆栈中弹出全部32位通用寄存器：EDI, ESI, EBP, EBX, EDX, ECX, EAX POPAD <br>
POPF POPF 9D 设置所有标志位 从堆栈中弹出16位标志寄存器 POPF <br>
POPFD POPFD 9D 设置所有标志位 从堆栈中弹出32位标志寄存器 POPFD <br>
POR POR mm, mm/m64 0F EB /r 不影响标志位 逻辑或操作 　 <br>
POR xmm1,xmm2/m128 66 0F EB /r POR XMM4，XMM1 <br>
PSLLD PSLLD mm, mm/m64 0F F2 /r 不影响标志位 紧缩双字逻辑左移 　 <br>
PSLLD xmm1,xmm2/m128 66 0F F2 /r PSLLD XMM4，XMM1 <br>
PSLLD mm, imm8 0F 72 /6 ib 　 <br>
PSLLD xmm1, imm8 66 0F 72 /6 ib 　 <br>
PSLLQ PSLLQ mm, mm/m64 0F F3 /r 不影响标志位 紧缩四字逻辑左移 　 <br>
PSLLQ xmm1,xmm2/m128 66 0F F3 /r PSLLQ XMM4，XMM1 <br>
PSLLQ mm, imm8 0F 73 /6 ib 　 <br>
PSLLQ xmm1, imm8 66 0F 73 /6 ib 　 <br>
PSLLW PSLLW mm, mm/m64 0F F1 /r 不影响标志位 紧缩字逻辑左移 　 <br>
PSLLW xmm1,xmm2/m128 66 0F F1 /r PSLLW XMM4，XMM1 <br>
PSLLW mm, imm8 0F 71 /6 ib 　 <br>
PSLLW xmm1, imm8 66 0F 71 /6 ib 　 <br>
PSRAD PSRAD mm, mm/m64 0F E2 /r 不影响标志位 紧缩双字算术右移 　 <br>
PSRAD xmm1,xmm2/m128 66 0F E2 /r PSRAD XMM4，XMM1 <br>
PSRAD mm, imm8 0F 72 /4 ib 　 <br>
PSRAD xmm1, imm8 66 0F 72 /4 ib 　 <br>
PSRAW PSRAW mm, mm/m64 0F E1 /r 不影响标志位 紧缩字算术右移 　 <br>
PSRAW xmm1,xmm2/m128 66 0F E1 /r PSRAW XMM4，XMM1 <br>
PSRAW mm, imm8 0F 71 /4 ib 　 <br>
PSRAW xmm1, imm8 66 0F 71 /4 ib 　 <br>
PSRLD PSRLD mm, mm/m64 0F D2 /r 不影响标志位 紧缩双字逻辑右移 　 <br>
PSRLD xmm1,xmm2/m128 66 0F D2 /r PSRLD XMM4，XMM1 <br>
PSRLD mm, imm8 0F 72 /2 ib 　 <br>
PSRLD xmm1, imm8 66 0F 72 /2 ib 　 <br>
PSRLQ PSRLQ mm, mm/m64 0F D3 /r 不影响标志位 紧缩四字逻辑右移 　 <br>
PSRLQ xmm1,xmm2/m128 66 0F D3 /r PSRLQ XMM4，XMM1 <br>
PSRLQ mm, imm8 0F 73 /2 ib 　 <br>
PSRLQ xmm1, imm8 66 0F 73 /2 ib 　 <br>
PSRLW PSRLW mm, mm/m64 0F D1 /r 不影响标志位 紧缩字逻辑右移 　 <br>
PSRLW xmm1,xmm2/m128 66 0F D1 /r PSRLW XMM4，XMM1 <br>
PSRLW mm, imm8 0F 71 /2 ib 　 <br>
PSRLW xmm1, imm8 66 0F 71 /2 ib 　 <br>
PSUBB PSUBB mm, mm/m64 0F F8 /r 不影响标志位 紧缩字节减法，溢出则回绕 　 <br>
PSUBB xmm1,xmm2/m128 66 0F F8 /r PSUBB XMM6，XMM7 <br>
PSUBW PSUBW mm, mm/m64 0F F9 /r 不影响标志位 紧缩字减法，溢出则回绕 　 <br>
PSUBW xmm1,xmm2/m128 66 0F F9 /r PSUBW XMM6，XMM7 <br>
PSUBD PSUBD mm, mm/m64 0F FA /r 不影响标志位 紧缩双字减法，溢出则回绕 　 <br>
PSUBD xmm1,xmm2/m128 66 0F FA /r PSUBD XMM6，XMM7 <br>
PSUBQ PSUBQ mm1, mm2/m64 0F FB /r 不影响标志位 紧缩四字减法，溢出则回绕 　 <br>
PSUBQ xmm1,xmm2/m128 66 0F FB /r PSUBQ XMM6，XMM7 <br>
PSUBSB PSUBSB mm, mm/m64 0F E8 /r 不影响标志位 带符号紧缩字节减法，溢出取饱和值 　 <br>
PSUBSB xmm1,xmm2/m128 66 0F E8 /r PSUBSB XMM6，XMM7 <br>
PSUBSW PSUBSW mm, mm/m64 0F E9 /r 不影响标志位 带符号紧缩字减法，溢出取饱和值 　 <br>
PSUBSW xmm1,xmm2/m128 66 0F E9 /r PSUBSW XMM6，XMM7 <br>
PSUBUSB PSUBUSB mm, mm/m64 0F D8 /r 不影响标志位 无符号紧缩字节减法，溢出取饱和值 　 <br>
PSUBUSB xmm1,xmm2/m128 66 0F D8 /r PSUBUSB XMM6，XMM7 <br>
PSUBUSW PSUBUSW mm, mm/m64 0F D9 /r 不影响标志位 无符号紧缩字减法，溢出取饱和值 　 <br>
PSUBUSW xmm1,xmm2/m128 66 0F D9 /r PSUBUSW XMM6，XMM7 <br>
PUNPCKHBW PUNPCKHBW mm, mm/m64 0F 68 /r 不影响标志位 反紧缩高位，字节到字 　 <br>
PUNPCKHBW xmm1,xmm2/m128 66 0F 68 /r PUNPCKHBW XMM6，XMM7 <br>
PUNPCKHWD PUNPCKHWD mm, mm/m64 0F 69 /r 不影响标志位 反紧缩高位，字到双字 　 <br>
PUNPCKHWD xmm1,xmm2/m128 66 0F 69 /r PUNPCKHWD XMM6，XMM7 <br>
PUNPCKHDQ PUNPCKHDQ mm1, mm2/m64 0F 6A /r 不影响标志位 反紧缩高位，双字到四字 　 <br>
PUNPCKHDQ xmm1,xmm2/m128 66 0F 6A /r PUNPCKHDQ XMM6，XMM7 <br>
PUNPCKHQDQ PUNPCKHQDQ xmm1,xmm2/m128 66 0F 6D /r 不影响标志位 反紧缩高位，四字到八字 　 <br>
PUNPCKLBW PUNPCKLBW mm, mm/m64 0F 60 /r 不影响标志位 反紧缩低位，字节到字 　 <br>
PUNPCKLBW xmm1,xmm2/m128 66 0F 60 /r PUNPCKLBW XMM6，XMM7 <br>
PUNPCKLWD PUNPCKLWD mm, mm/m64 0F 61 /r 不影响标志位 反紧缩低位，字到双字 　 <br>
PUNPCKLWD xmm1,xmm2/m128 66 0F 61 /r PUNPCKLWD XMM6，XMM7 <br>
PUNPCKLDQ PUNPCKLDQ mm1, mm2/m64 0F 62 /r 不影响标志位 反紧缩低位，双字到四字 　 <br>
PUNPCKLDQ xmm1,xmm2/m128 66 0F 62 /r PUNPCKLDQ XMM6，XMM7 <br>
PUNPCKLQDQ PUNPCKLQDQ xmm1, xmm2/m128 66 0F 6C /r 不影响标志位 反紧缩低位，四字到八字 　 <br>
PUSH PUSH r/m16 FF /6 不影响标志位 16位数据压栈 PUSH WORD Ptr [006387EA] <br>
PUSH r/m32 FF /6 32位数据压栈 PUSH DWORD Ptr [006387EA] <br>
PUSH r16 50+rw 16位寄存器数据压栈 PUSH AX <br>
PUSH r32 50+rd 32位寄存器数据压栈 PUSH EAX <br>
PUSH imm8 6A 8位立即数据压栈 PUSH EA <br>
PUSH imm16 68 16位立即数据压栈 PUSH 87EA <br>
PUSH imm32 58 32位立即数据压栈 PUSH 006387EA <br>
PUSH CS 0E 寄存器CS数据压栈 PUSH CS <br>
PUSH SS 16 寄存器SS数据压栈 PUSH SS <br>
PUSH DS 1E 寄存器DS数据压栈 PUSH DS <br>
PUSH ES 06 寄存器ES数据压栈 PUSH ES <br>
PUSH FS 0F A0 寄存器FS数据压栈 PUSH FS <br>
PUSH GS 0F A8 寄存器GS数据压栈 PUSH GS <br>
PUSHA PUSHA 60 不影响标志位 压栈全部16位通用寄存器：AX, CX, DX, BX, SP, BP, SI, DI PUSHA <br>
PUSHAD PUSHAD 60 不影响标志位 压栈全部32位通用寄存器：EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI PUSHAD <br>
PUSHF PUSHF 9C 设置所有标志位 压栈16位标志寄存器 PUSHF <br>
PUSHFD PUSHFD 9C 设置所有标志位 压栈32位标志寄存器 PUSHFD <br>
PXOR PXOR mm, mm/m64 0F EF /r 不影响标志位 逻辑异或操作 　 <br>
PXOR xmm1,xmm2/m128 66 0F EF /r PXOR XMM6，XMM7 <br>
RCL RCL r/m8, 1 D0 /2 CF(或OF)被改变 带进位循环左移1次 RCL AL，1 <br>
RCL r/m8, CL D2 /2 带进位循环左移CL次 RCL AL，CL <br>
RCL r/m8, imm8 C0 /2 ib 带进位循环左移imm8次 RCL AL，03 <br>
RCL r/m16, 1 D1 /2 带进位循环左移1次 RCL AX，1 <br>
RCL r/m16, CL D3 /2 带进位循环左移CL次 RCL AX，CL <br>
RCL r/m16, imm8 C1 /2 ib 带进位循环左移imm8次 RCL AX，03 <br>
　 　 　 　 <br>
　 　 　 　 <br>
RCL r/m32, 1 D1 /2 带进位循环左移1次 RCL EAX，1 <br>
RCL r/m32, CL D3 /2 带进位循环左移CL次 RCL EAX，CL 　 　 <br>
RCL r/m32, imm8 C1 /2 ib 带进位循环左移imm8次 RCL EAX，03 　 　 <br>
RCR RCR r/m8, 1 D0 /3 CF(或OF)被改变 带进位循环右移1次 RCR AL，1 <br>
RCR r/m8, CL D2 /3 带进位循环右移CL次 RCR AL，CL <br>
RCR r/m8, imm8 C0 /3 ib 带进位循环右移imm8次 RCR AL，03 <br>
RCR r/m16, 1 D1 /3 带进位循环右移1次 RCR AX，1 <br>
RCR r/m16, CL D3 /3 带进位循环右移CL次 RCR AX，CL <br>
RCR r/m16, imm8 C1 /3 ib 带进位循环右移imm8次 RCR AX，03 <br>
　 　 　 　 <br>
　 　 　 　 <br>
RCR r/m32, 1 D1 /3 带进位循环右移1次 RCR EAX，1 <br>
RCR r/m32, CL D3 /3 带进位循环右移CL次 RCR EAX，CL 　 　 <br>
RCR r/m32, imm8 C1 /3 ib 带进位循环右移imm8次 RCR EAX，03 　 　 <br>
ROL ROL r/m8, 1 D0 /0 CF(或OF)被改变 循环左移1次 ROL AL，1 <br>
ROL r/m8, CL D2 /0 循环左移CL次 ROL AL，CL <br>
ROL r/m8, imm8 C0 /0 ib 循环左移imm8次 ROL AL，03 <br>
ROL r/m16, 1 D1 /0 循环左移1次 ROL AX，1 <br>
ROL r/m16, CL D3 /0 循环左移CL次 ROL AX，CL <br>
ROL r/m16, imm8 C1 /0 ib 循环左移imm8次 ROL AX，03 <br>
　 　 　 　 <br>
　 　 　 　 <br>
ROL r/m32, 1 D1 /0 循环左移1次 ROL EAX，1 <br>
ROL r/m32, CL D3 /0 循环左移CL次 ROL EAX，CL 　 　 <br>
ROL r/m32, imm8 C1 /0 ib 循环左移imm8次 ROL EAX，03 　 　 <br>
ROR ROR r/m8, 1 D0 /1 CF(或OF)被改变 循环右移1次 ROR AL，1 <br>
ROR r/m8, CL D2 /1 循环右移CL次 ROR AL，CL <br>
ROR r/m8, imm8 C0 /1 ib 循环右移imm8次 ROR AL，03 <br>
ROR r/m16, 1 D1 /1 循环右移1次 ROR AX，1 <br>
ROR r/m16, CL D3 /1 循环右移CL次 ROR AX，CL <br>
ROR r/m16, imm8 C1 /1 ib 循环右移imm8次 ROR AX，03 <br>
　 　 　 　 <br>
　 　 　 　 <br>
ROR r/m32, 1 D1 /1 循环右移1次 ROR EAX，1 <br>
ROR r/m32, CL D3 /1 循环右移CL次 ROR EAX，CL 　 　 <br>
ROR r/m32, imm8 C1 /1 ib 循环右移imm8次 ROR EAX，03 　 　 <br>
RDMSR RDMSR 0F 32 不影响标志位 把ECX指定的模型专用寄存器内容送EDX:EAX RDMSR <br>
RDPMC RDPMC 0F 33 不影响标志位 把ECX指定的性能监测计数器内容送EDX:EAX RDPMC <br>
RDTSC RDTSC 0F 31 不影响标志位 读时间标记计数器到EDX:EAX RDTSC <br>
REP REP INS r/m8, DX F3 6C 不影响标志位 从DX指定的端口读(E)CX个字节(BYTE)到ES:[(E)DI] 　 <br>
REP INS r/m16, DX F3 6D 从DX指定的端口读(E)CX个字(WORD)到ES:[(E)DI] 　 <br>
REP INS r/m32, DX F3 6D 从DX指定的端口读(E)CX个双字(DWORD)到ES:[(E)DI] 　 <br>
REP MOVS m8, m8 F3 A4 连续字符串传送，每次传送1个字节，传送(E)CX次 REP MOVS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REP MOVS m16, m16 F3 A5 连续字符串传送，每次传送1个字，传送(E)CX次 REP MOVS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REP MOVS m32, m32 F3 A5 连续字符串传送，每次传送1个双字，传送(E)CX次 REP MOVS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REP OUTS DX,r/m8 F3 6E 将DS:[(E)SI]处的(E)CX个字节输出到DX指定的端口 　 <br>
REP OUTS DX,r/m16 F3 6F 将DS:[(E)SI]处的(E)CX个字输出到DX指定的端口 　 <br>
REP OUTS DX,r/m32 F3 6F 将DS:[(E)SI]处的(E)CX个双字输出到DX指定的端口 　 <br>
REP LODS AL F3 AC 将地址DS:[(E)SI]处的(E)CX个字节装入AL 　 <br>
REP LODS AX F3 AD 将地址DS:[(E)SI]处的(E)CX个字装入AX 　 <br>
REP LODS EAX F3 AD 将地址DS:[(E)SI]处的(E)CX个双字装入EAX 　 <br>
REP STOS m8 F3 AA 用AL填充地址ES:[(E)DI]处的(E)CX个字节 　 <br>
REP STOS m16 F3 AB 用AX填充地址ES:[(E)DI]处的(E)CX个字 　 <br>
REP STOS m32 F3 AB 用EAX填充地址ES:[(E)DI]处的(E)CX个双字 　 <br>
REPE REPE CMPS m8, m8 F3 A6 设置 AF CF OF PF SF ZF 比较字符串，每次比较1个字节，直到不相等字节停止 REPE CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPE CMPS m16, m16 F3 A7 比较字符串，每次比较1个字，直到不相等字停止 REPE CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPE CMPS m32, m32 F3 A7 比较字符串，每次比较1个双字，直到不相等双字停止 REPE CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPE SCAS m8 F3 AE 扫描字符串ES:[(E)DI]中的AL字节值，遇到非AL值停止 REPE SCAS StrING1 <br>
REPE SCAS m16 F3 AF 扫描字符串ES:[(E)DI]中的AX字值，遇到非AX值停止 REPE CMPS StrING1 <br>
REPE SCAS m32 F3 AF 扫描字符串ES:[(E)DI]中的EAX双字值，遇到非EAX值停止 REPE CMPS StrING1 <br>
REPNE REPNE CMPS m8, m8 F2 A6 设置 AF CF OF PF SF ZF 比较字符串，每次比较1个字节，直到相等字节停止 REPNE CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPNE CMPS m16, m16 F2 A7 比较字符串，每次比较1个字，直到相等字停止 REPNE CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPNE CMPS m32, m32 F2 A7 比较字符串，每次比较1个双字，直到相等双字停止 REPNE CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPNE SCAS m8 F2 AE 扫描字符串ES:[(E)DI]中的AL字节值，遇到AL值停止 REPNE SCAS StrING1 <br>
REPNE SCAS m16 F2 AF 扫描字符串ES:[(E)DI]中的AX字值，遇到AX值停止 REPNE CMPS StrING1 <br>
REPNE SCAS m32 F2 AF 扫描字符串ES:[(E)DI]中的EAX双字值，遇到EAX值停止 REPNE CMPS StrING1 <br>
REPZ REPZ CMPS m8, m8 F3 A6 设置 AF CF OF PF SF ZF 比较字符串，每次比较1个字节，直到不相等字节停止 REPZ CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPZ CMPS m16, m16 F3 A7 比较字符串，每次比较1个字，直到不相等字停止 REPZ CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPZ CMPS m32, m32 F3 A7 比较字符串，每次比较1个双字，直到不相等双字停止 REPZ CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPZ SCAS m8 F3 AE 扫描字符串ES:[(E)DI]中的AL字节值，遇到非AL值停止 REPZ SCAS StrING1 <br>
REPZ SCAS m16 F3 AF 扫描字符串ES:[(E)DI]中的AX字值，遇到非AX值停止 REPZ CMPS StrING1 <br>
REPZ SCAS m32 F3 AF 扫描字符串ES:[(E)DI]中的EAX双字值，遇到非EAX值停止 REPZ CMPS StrING1 <br>
REPNZ REPNZ CMPS m8, m8 F2 A6 设置 AF CF OF PF SF ZF 比较字符串，每次比较1个字节，直到相等字节停止 REPNZ CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPNZ CMPS m16, m16 F2 A7 比较字符串，每次比较1个字，直到相等字停止 REPNZ CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPNZ CMPS m32, m32 F2 A7 比较字符串，每次比较1个双字，直到相等双字停止 REPNZ CMPS StrING1, StrING2 ；源串DS:[(E)SI]，目的串：ES:[(E)DI] <br>
REPNZ SCAS m8 F2 AE 扫描字符串ES:[(E)DI]中的AL字节值，遇到AL值停止 REPNZ SCAS StrING1 <br>
REPNZ SCAS m16 F2 AF 扫描字符串ES:[(E)DI]中的AX字值，遇到AX值停止 REPNZ CMPS StrING1 <br>
REPNZ SCAS m32 F2 AF 扫描字符串ES:[(E)DI]中的EAX双字值，遇到EAX值停止 REPNZ CMPS StrING1 <br>
RET RET C3 恢复压栈的标志位 子过程返回(Near) RET <br>
RET CB 子过程返回(Far) RET <br>
RET imm16 C2 iw 子过程返回(Near)，并从堆栈弹出imm16字节 RET 08 <br>
RET imm16 CA iw 子过程返回(Far)，并从堆栈弹出imm16字节 RET 08 <br>
RSM RSM 0F AA 恢复所有标志位 从系统管理模式返回 RSM <br>
　 　 　 　 　 　 <br>
SAHF SAHF 9E 设置SF ZF AF PF CF 装入AH到标志寄存器，格式：(SF:ZF:0:AF:0:PF:1:CF)←AH SAHF <br>
　 　 　 　 　 　 <br>
SAL SAL r/m8, 1 D0 /4 CF(或OF)被改变 算术左移1次(乘法：r/m8=r/m8*2) SAL AL，1 <br>
SAL r/m8, CL D2 /4 算术左移CL次(乘法：r/m8=r/m8*(2^CL)) SAL AL，CL <br>
SAL r/m8, imm8 C0 /4 ib 算术左移imm8次(乘法：r/m8=r/m8*(2^imm8)) SAL AL，03 <br>
SAL r/m16, 1 D1 /4 算术左移1次(乘法：r/m16=r/m16*2) SAL AX，1 <br>
SAL r/m16, CL D3 /4 算术左移CL次(乘法：r/m16=r/m16*(2^CL)) SAL AX，CL <br>
SAL r/m16, imm8 C1 /4 ib 算术左移imm8次(乘法：r/m16=r/m16*(2^imm8)) SAL AX，03 <br>
　 　 　 　 <br>
　 　 　 　 <br>
SAL r/m32, 1 D1 /4 算术左移1次(乘法：r/m32=r/m32*2) SAL EAX，1 <br>
SAL r/m32, CL D3 /4 算术左移CL次(乘法：r/m32=r/m32*(2^CL)) SAL EAX，CL 　 　 <br>
SAL r/m32, imm8 C1 /4 ib 算术左移imm8次(乘法：r/m32=r/m32*(2^imm8)) SAL EAX，03 　 　 <br>
SAR SAR r/m8, 1 D0 /7 CF(或OF)被改变 算术右移1次(有符号除法：r/m8=r/m8 / 2) SAR AL，1 <br>
SAR r/m8, CL D2 /7 算术右移CL次(有符号除法：r/m8=r/m8 / (2^CL)) SAR AL，CL <br>
SAR r/m8, imm8 C0 /7 ib 算术右移imm8次(有符号除法：r/m8=r/m8 / (2^imm8)) SAR AL，03 <br>
SAR r/m16, 1 D1 /7 算术右移1次(有符号除法：r/m16=r/m16 / 2) SAR AX，1 <br>
SAR r/m16, CL D3 /7 算术右移CL次(有符号除法：r/m16=r/m16 / (2^CL)) SAR AX，CL <br>
SAR r/m16, imm8 C1 /7 ib 算术右移imm8次(有符号除法：r/m16=r/m16 / (2^imm8)) SAR AX，03 <br>
　 　 　 　 <br>
　 　 　 　 <br>
SAR r/m32, 1 D1 /7 算术右移1次(有符号除法：r/m32=r/m32 / 2) SAR EAX，1 <br>
SAR r/m32, CL D3 /7 算术右移CL次(有符号除法：r/m32=r/m32 / (2^CL)) SAR EAX，CL 　 　 <br>
SAR r/m32, imm8 C1 /7 ib 算术右移imm8次(有符号除法：r/m32=r/m32 / (2^imm8)) SAR EAX，03 　 　 <br>
SHL SHL r/m8, 1 D0 /4 CF(或OF)被改变 逻辑左移1次(乘法：r/m8=r/m8*2) SHL AL，1 <br>
SHL r/m8, CL D2 /4 逻辑左移CL次(乘法：r/m8=r/m8*(2^CL)) SHL AL，CL <br>
SHL r/m8, imm8 C0 /4 ib 逻辑左移imm8次(乘法：r/m8=r/m8*(2^imm8)) SHL AL，03 <br>
SHL r/m16, 1 D1 /4 逻辑左移1次(乘法：r/m16=r/m16*2) SHL AX，1 <br>
SHL r/m16, CL D3 /4 逻辑左移CL次(乘法：r/m16=r/m16*(2^CL)) SHL AX，CL <br>
SHL r/m16, imm8 C1 /4 ib 逻辑左移imm8次(乘法：r/m16=r/m16*(2^imm8)) SHL AX，03 <br>
　 　 　 　 <br>
　 　 　 　 <br>
SHL r/m32, 1 D1 /4 逻辑左移1次(乘法：r/m32=r/m32*2) SHL EAX，1 <br>
SHL r/m32, CL D3 /4 逻辑左移CL次(乘法：r/m32=r/m32*(2^CL)) SHL EAX，CL 　 　 <br>
SHL r/m32, imm8 C1 /4 ib 逻辑左移imm8次(乘法：r/m32=r/m32*(2^imm8)) SHL EAX，03 　 　 <br>
SHR SHR r/m8, 1 D0 /5 CF(或OF)被改变 逻辑右移1次(无符号除法：r/m8=r/m8 / 2) SHR AL，1 <br>
SHR r/m8, CL D2 /5 逻辑右移CL次(无符号除法：r/m8=r/m8 / (2^CL)) SHR AL，CL <br>
SHR r/m8, imm8 C0 /5 ib 逻辑右移imm8次(无符号除法：r/m8=r/m8 / (2^imm8)) SHR AL，03 <br>
SHR r/m16, 1 D1 /5 逻辑右移1次(无符号除法：r/m16=r/m16 / 2) SHR AX，1 <br>
SHR r/m16, CL D3 /5 逻辑右移CL次(无符号除法：r/m16=r/m16 / (2^CL)) SHR AX，CL <br>
SHR r/m16, imm8 C1 /5 ib 逻辑右移imm8次(无符号除法：r/m16=r/m16 / (2^imm8)) SHR AX，03 <br>
　 　 　 　 <br>
　 　 　 　 <br>
SHR r/m32, 1 D1 /5 逻辑右移1次(无符号除法：r/m32=r/m32 / 2) SHR EAX，1 <br>
SHR r/m32, CL D3 /5 逻辑右移CL次(无符号除法：r/m32=r/m32 / (2^CL)) SHR EAX，CL 　 　 <br>
SHR r/m32, imm8 C1 /5 ib 逻辑右移imm8次(无符号除法：r/m32=r/m32 / (2^imm8)) SHR EAX，03 　 　 <br>
SBB SBB AL, imm8 1C ib 设置 AF CF OF SF PF ZF 带借位减法 SBB AL, 1F <br>
SBB AX, imm16 1D iw SBB AX, 4F80 <br>
SBB EAX, imm32 1D id SBB EAX, 00004F80 <br>
SBB r/m8, imm8 80 /3 ib SBB BYTE Ptr [006387EA], 39 <br>
SBB r/m16,imm16 81 /3 iw SBB WORD Ptr [006387EA], 1039 <br>
SBB r/m32,imm32 81 /3 id SBB DWORD Ptr [006387EA], 00001039 <br>
SBB r/m16,imm8 83 /3 ib SBB WORD Ptr [006387EA], 39 <br>
SBB r/m32,imm8 83 /3 ib SBB DWORD Ptr [006387EA], 39 <br>
SBB r/m8,r8 18 /r SBB [006387EA], AL <br>
SBB r/m16,r16 19 /r SBB [006387EA], AX <br>
SBB r/m32,r32 19 / r SBB [006387EA], EAX <br>
SBB r8,r/m8 1A /r SBB AL, [006387EA] <br>
SBB r16,r/m16 1B /r SBB AX, [006387EA] <br>
SBB r32,r/m32 1B /r SBB EAX, [006387EA] <br>
SCAS SCAS m8 AE 设置OF SF ZF AF PF CF 用AL中的字节值扫描字符串ES:(E)DI，然后设置标志位 　 <br>
SCAS m16 AF 用AX中的字值扫描字符串ES:(E)DI，然后设置标志位 　 <br>
SCAS m32 AF 用EAX中的双字值扫描字符串ES:(E)DI，然后设置标志位 　 <br>
SCASB SCASB AE 设置OF SF ZF AF PF CF 用AL中的字节值扫描字符串ES:(E)DI，然后设置标志位 SCASB <br>
SCASW SCASW AF 设置OF SF ZF AF PF CF 用AX中的字值扫描字符串ES:(E)DI，然后设置标志位 SCASW <br>
SCASD SCASD AF 设置OF SF ZF AF PF CF 用EAX中的双字值扫描字符串ES:(E)DI，然后设置标志位 SCASD <br>
SETcc SETA r/m8 0F 97 高于(CF=0 and ZF=0) 条件设置指令，如果条件满足则r/m8=1，否则r/m8=0 SETA AL <br>
SETAE r/m8 0F 93 高于等于(CF=0) SETAE AL <br>
SETB r/m8 0F 92 低于(CF=1) SETB AL <br>
SETBE r/m8 0F 96 低于等于(CF=1 or ZF=1) SETBE AL <br>
SETC r/m8 0F 92 有进位(CF=1) SETC AL <br>
SETE r/m8 0F 94 等于(ZF=1) SETE AL <br>
SETG r/m8 0F 9F 大于(ZF=0 and SF=OF) SETG AL <br>
SETGE r/m8 0F 9D 大于等于(SF=OF) SETGE AL <br>
SETL r/m8 0F 9C 小于(SF&lt;&gt;OF) SETL AL <br>
SETLE r/m8 0F 9E 小于等于(ZF=1 or SF&lt;&gt;OF) SETLE AL <br>
SETNA r/m8 0F 96 不高于(CF=1 or ZF=1) SETNA AL <br>
SETNAE r/m8 0F 92 不高等于(CF=1) SETNAE AL <br>
SETNB r/m8 0F 93 不低于(CF=0) SETNB AL <br>
SETNBE r/m8 0F 97 不低等于(CF=0 and ZF=0) SETNBE AL <br>
SETNC r/m8 0F 93 无进位(CF=0) SETNC AL <br>
SETNE r/m8 0F 95 不等于(ZF=0) SETNE AL <br>
SETNG r/m8 0F 9E 不大于(ZF=1 or SF&lt;&gt;OF) SETNG AL <br>
SETNGE r/m8 0F 9C 不大等于(SF&lt;&gt;OF) SETNGE AL <br>
SETNL r/m8 0F 9D 不小于(SF=OF) SETNL AL <br>
SETNLE r/m8 0F 9F 不小等于(ZF=0 and SF=OF) SETNLE AL <br>
SETNO r/m8 0F 91 无溢出(OF=0) SETNO AL <br>
SETNP r/m8 0F 9B 非偶数(PF=0) SETNP AL <br>
SETNS r/m8 0F 99 非负数(SF=0) SETNS AL <br>
SETNZ r/m8 0F 95 非零(ZF=0) SETNZ AL <br>
SETO r/m8 0F 90 溢出(OF=1) SETO AL <br>
SETP r/m8 0F 9A 偶数(PF=1) SETP AL <br>
SETPE r/m8 0F 9A 偶数(PF=1) SETPE AL <br>
SETPO r/m8 0F 9B 奇数(PF=0) SETPO AL <br>
SETS r/m8 0F 98 负数(SF=1) SETS AL <br>
SETZ r/m8 0F 94 为零(ZF=1) SETZ AL <br>
SGDT SGDT m 0F 01 /0 不影响标志位 保存全局描述符表寄存器到内存m处 SGDT [EBP] <br>
SIDT SIDT m 0F 01 /1 不影响标志位 保存中断描述符表寄存器到内存m处 SIDT [ESI] <br>
SLDT SLDT r/m16 0F 00 /0 不影响标志位 保存LDT选择子到r/m16 SLDT SI <br>
SLDT r/m32 保存LDT选择子到r/m32 SLDT ESI <br>
SHLD SHLD r/m16, r16, imm8 0F A4 设置CF SF ZF PF AF OF 双精度左移 SHLD AX，BX，3 <br>
SHLD r/m16, r16, CL 0F A5 SHLD AX，BX，CL <br>
SHLD r/m32, r32, imm8 0F A4 SHLD EAX，EBX，3 <br>
SHLD r/m32, r32, CL 0F A5 SHLD EAX，EBX，CL <br>
SHRD SHRD r/m16, r16, imm8 0F AC 设置CF SF ZF PF AF OF 双精度右移 SHRD AX，BX，3 <br>
SHRD r/m16, r16, CL 0F AD SHRD AX，BX，CL <br>
SHRD r/m32, r32, imm8 0F AC SHRD EAX，EBX，3 <br>
SHRD r/m32, r32, CL 0F AD SHRD EAX，EBX，CL <br>
SMSW SMSW r/m16 0F 01 /4 不影响标志位 保存机器状态字到r/m16 SMSW SI <br>
SMSW r32/m16 保存机器状态字到r32/m16 SMSW ESI <br>
STC STC F9 设置CF 设置CF=1 STC <br>
Std Std FD 设置DF 设置DF=1 Std <br>
STI STI FB 设置IF 设置IF=1，开中断 STI <br>
STOS STOS m8 AA 不影响标志位 将AL保存到地址ES:(E)DI 　 <br>
STOS m16 AB 将AX保存到地址ES:(E)DI 　 <br>
STOS m32 AB 将EAX保存到地址ES:(E)DI 　 <br>
STOSB STOSB AA 不影响标志位 将AL保存到地址ES:(E)DI STOSB <br>
STOSW STOSW AB 不影响标志位 将AX保存到地址ES:(E)DI STOSW <br>
STOSD STOSD AB 不影响标志位 将EAX保存到地址ES:(E)DI STOSD <br>
Str Str r/m16 0F 00 /1 不影响标志位 保存任务寄存器到r/m16 Str AX <br>
SUB SUB AL, imm8 2C ib 设置 AF CF OF SF PF ZF 减法 SUB AL, 1F <br>
SUB AX, imm16 2D iw SUB AX, 4F80 <br>
SUB EAX, imm32 2D id SUB EAX, 00004F80 <br>
SUB r/m8, imm8 80 /5 ib SUB BYTE Ptr [006387EA], 39 <br>
SUB r/m16,imm16 81 /5 iw SUB WORD Ptr [006387EA], 1039 <br>
SUB r/m32,imm32 81 /5 id SUB DWORD Ptr [006387EA], 00001039 <br>
SUB r/m16,imm8 83 /5 ib SUB WORD Ptr [006387EA], 39 <br>
SUB r/m32,imm8 83 /5 ib SUB DWORD Ptr [006387EA], 39 <br>
SUB r/m8,r8 28 /r SUB [006387EA], AL <br>
SUB r/m16,r16 29 /r SUB [006387EA], AX <br>
SUB r/m32,r32 29 / r SUB [006387EA], EAX <br>
SUB r8,r/m8 2A /r SUB AL, [006387EA] <br>
SUB r16,r/m16 2B /r SUB AX, [006387EA] <br>
SUB r32,r/m32 2B /r SUB EAX, [006387EA] <br>
SYSENTER SYSENTER 0F 34 设置VM IF RF 快速调用系统0级过程(用于系统3级应用程序)，配合SYSEXIT使用 　 <br>
SYSEXIT SYSEXIT 0F 35 不影响标志位 快速返回到系统3级用户代码，配合SYSENTER使用 　 <br>
TEST TEST AL, imm8 A8 ib 设置 CF OF PF SF ZF 逻辑与测试，但是不改变目的操作数，只设置相关标志位 TEST AL, 1F <br>
TEST AX, imm16 A9 iw TEST AX, 4F80 <br>
TEST EAX, imm32 A9 id TEST EAX, 00004F80 <br>
TEST r/m8, imm8 F6 /0 ib TEST BYTE Ptr [006387EA], 39 <br>
TEST r/m16,imm16 F7 /0 ib TEST WORD Ptr [006387EA], 1039 <br>
TEST r/m32,imm32 F7 /0 ib TEST DWORD Ptr [006387EA], 00001039 <br>
TEST r/m8,r8 84 /r TEST BYTE Ptr [006387EA], AL <br>
TEST r/m16,r16 85 /r TEST WORD Ptr [006387EA], AX <br>
TEST r/m32,r32 85 /r TEST DWORD Ptr [006387EA], EAX <br>
UD2 UD2 0F 0B 不影响标志位 产生无效代码异常，用于软件测试 UD2 <br>
VERR VERR 0F 00 /4 设置ZF 如果目标选择子对应段可读，则ZF置1 VERR <br>
VERW VERW 0F 00 /5 设置ZF 如果目标选择子对应段可写，则ZF置1 VERW <br>
WAIT WAIT 9B C0，C1, C2, C3 未定义 等待，检查非屏蔽浮点异常 WAIT <br>
WBINVD WBINVD 0F 09 不影响标志位 写回内部CACHE并使之失效 WBINVD <br>
WRMSR WRMSR 0F 30 不影响标志位 把EDX:EAX的内容写入ECX指定的模型专用寄存器 WRMSR <br>
XADD XADD r/m8, r8 0F C0 /r 设置 CF PF AF SF ZF OF 目的操作数和源操作数相加，并使源操作数保存目的操作数的值 XADD AL，BL <br>
XADD r/m16, r16 0F C1 /r XADD AX，BX <br>
XADD r/m32, r32 0F C1 /r XADD EAX，EBX <br>
XCHG XCHG AX, r16 90+rw 不影响标志位 目的操作数和源操作数的值交换 XCHG AX，BX <br>
XCHG r16, AX 90+rw XCHG CX，AX <br>
XCHG EAX, r32 90+rd XCHG EAX，EBX <br>
XCHG r32, EAX 90+rd XCHG ECX，EAX <br>
XCHG r/m8, r8 86 /r XCHG [006387EA]，BL <br>
XCHG r8, r/m8 86 /r XCHG AX，[006387EA] <br>
XCHG r/m16, r16 87 /r XCHG [006387EA]，BX <br>
XCHG r16, r/m16 87 /r XCHG AX，[006387EA] <br>
XCHG r/m32, r32 87 /r XCHG [006387EA]，EBX <br>
XCHG r32, r/m32 87 /r XCHG EAX，[006387EA]</p> <a href="http://hi.baidu.com/learned/blog/item/88b0053382a488f21b4cffdf.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%CB%E3%B7%A8%C9%E8%BC%C6">算法设计</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/88b0053382a488f21b4cffdf.html#comment">查看评论</a>]]></description>
        <pubDate>2009年10月19日 星期一  下午 02:46</pubDate>
        <category><![CDATA[算法设计]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/88b0053382a488f21b4cffdf.html</guid>
</item>

<item>
        <title><![CDATA[MP3文件格式详析]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/725fa9ecf2e505372697919f.html]]></link>
        <description><![CDATA[
		
		<p align="left"><strong>一．概述：</strong> <br>
&nbsp;&nbsp;&nbsp;  MP3 文件是由帧(frame)构成的，帧是MP3 文件最小的组成单位。MP3的全称应为MPEG1 Layer-3 音频<br>
文件，MPEG(Moving Picture Experts Group) 在汉语中译为活动图像专家组，特指活动影音压缩标准，MPEG 音频文件是MPEG1 标准中的声音部分，也叫MPEG 音频层，它根据压缩质量和编码复杂程度划分为三层，即Layer-1、Layer2、Layer3， 且分别对应MP1、MP2、MP3 这三种声音文件，并根据不同的用途，使用不同层次的编码。MPEG 音频编码的层次越高，编码器越复杂，压缩率也越高，MP1 和MP2 的压缩率分别为4：1 和6：1-8：1，而MP3 的压缩率则高达10：1-12：1，也就是说，一分钟CD 音质的音乐，未经压缩需要10MB的存储空间，而经过MP3 压缩编码后只有1MB 左右。不过MP3 对音频信号采用的是有损压缩方式，为了降低声音失真度，MP3采取了&ldquo;感官编码技术&rdquo;，即编码时先对音频文件进行频谱分析，然后用过滤器滤掉噪音电平，接着通过量化的方式将剩下的每一位打散排列，最后形成具有较高压缩比的MP3 文件，并使压缩后的文件在回放时能够达到比较接近原音源的声音效果。</p>
<p align="left"><strong>二．整个MP3 文件结构：</strong> <br>
MP3 文件大体分为三部分：TAG_V2(ID3V2)，Frame, TAG_V1(ID3V1) <br>
<br>
ID3V2 包含了作者，作曲，专辑等信息，长度不固定，扩展了ID3V1 的信息量。 <br>
Frame 一系列的帧，个数由文件大小和帧长决定 <br>
. 每个FRAME 的长度可能不固定，也可能固定，由位率bitrate 决定 <br>
. 每个FRAME 又分为帧头和数据实体两部分 <br>
. 帧头记录了mp3 的位率，采样率，版本等信息，每个帧之间相互独立 <br>
Frame <br>
ID3V1 包含了作者，作曲，专辑等信息，长度为128BYTE。 <br>
<br>
<strong>三．MP3的FRAME 格式： <br>
</strong>每个FRAME 都有一个帧头FRAMEHEADER，长度是4BYTE（32bit）,帧头后面可能有两个字节的CRC 校<br>
验，这两个字节的是否存在决定于FRAMEHEADER 信息的第16bit， 为0 则帧头后面无校验，为1 则有校验,<br>
校验值长度为2 个字节，紧跟在FRAMEHEADER 后面，接着就是帧的实体数据了，格式如下： <br>
FRAMEHEADER <br>
CRC（free） <br>
MAIN_DATA <br>
4 BYTE <br>
0 OR 2 BYTE <br>
长度由帧头计算得出 <br>
<br>
1．帧头FRAMEHEADER 格式如下： <br>
AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM <br>
13 个帧头字符的含义如下： <br>
Sign Length (bits) <br>
Position (bits) <br>
Description <br>
<font color="#0000ff">A 11 (31-21) Frame sync (all bits set with 1)</font> <br>
<br>
<font color="#0000ff">B 2 (20,19) MPEG Audio version</font> <br>
00 - MPEG Version 2.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  01 - reserved&nbsp;&nbsp;&nbsp;&nbsp;  10 - MPEG Version 2&nbsp;&nbsp;&nbsp;&nbsp;  11 - MPEG Version 1 <br>
<font color="#0000ff"><br>
C 2 (18,17) Layer description</font> <br>
00 - reserved&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  01 - Layer III &nbsp;&nbsp;&nbsp;&nbsp;  10 - Layer II&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  11 - Layer I <br>
<font color="#0000ff"><br>
D 1 (16) Protection bit</font> <br>
0 - Protected by CRC (16bit crc follows header) <br>
1 - Not protected <br>
<font color="#0000ff"><br>
E 4 (15,12) Bitrate index</font> <br>
bits&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  V1,L1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  V1,L2&nbsp;&nbsp;&nbsp;  V1,L3&nbsp;&nbsp;&nbsp;&nbsp;  V2,L1&nbsp;&nbsp;&nbsp;  V2,L2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  V2,L3 <br>
0000&nbsp;&nbsp;&nbsp;&nbsp;  free&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  free&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  free&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  free&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  free&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  free <br>
0001&nbsp;&nbsp;&nbsp;&nbsp;  32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  8 (8) <br>
0010&nbsp;&nbsp;&nbsp;&nbsp;  64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  48&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  40&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  48&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  16 (16) <br>
0011&nbsp;&nbsp;&nbsp;&nbsp;  96&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  48&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  96&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  24 (24) <br>
0100&nbsp;&nbsp;  128&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  128&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  32 (32) <br>
0101&nbsp;&nbsp;  160&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  160&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  64 (40) <br>
0110&nbsp;&nbsp;  192&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  96&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  192&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  96&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  80 (48) <br>
0111&nbsp;&nbsp;  224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  112&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  96&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  112&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  56 (56) <br>
1000&nbsp;&nbsp;  256&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  128&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  112&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &nbsp;&nbsp;  256&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  128&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  64 (64) <br>
1001&nbsp;&nbsp;  288&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  160&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  128&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  288&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  160&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  128 (80) <br>
1010&nbsp;&nbsp;  320&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  192&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  160&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  320&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  192&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  160 (96) <br>
1011&nbsp;&nbsp;  352&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  192&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  352&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  112 (112) <br>
1100&nbsp;&nbsp;  384&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  256&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  384&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  256&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  128 (128) <br>
1101&nbsp;&nbsp;  416&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  320&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  256&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  416&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  320&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  256 (144) <br>
1110&nbsp;&nbsp;  448&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  384&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  320&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  448&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  384&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  320 (160) <br>
1111&nbsp;&nbsp;  bad&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  bad&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  bad&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  bad&nbsp;&nbsp;&nbsp;&nbsp;  bad bad <br>
NOTES: All values are in kbps <br>
V1 - MPEG Version 1 <br>
V2 - MPEG Version 2 and Version 2.5 <br>
L1 - Layer I <br>
L2 - Layer II <br>
L3 - Layer III <br>
&quot;free&quot; means variable bitrate. <br>
&quot;bad&quot; means that this is not an allowed value <br>
The values in parentheses are from different sources which <br>
claim that those values are valid for V2,L2 and V2,L3. If <br>
anyone can confirm please let me know. <br>
<br>
<font color="#0000ff">F 2 (11,10) Sampling rate frequency index (values are in Hz)</font> <br>
bits&nbsp;&nbsp;&nbsp;  MPEG1&nbsp;&nbsp;  MPEG2&nbsp;&nbsp;&nbsp;&nbsp;  MPEG2.5 <br>
00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  44100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  22050&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  11025 <br>
01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  48000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  24000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  12000 <br>
10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  32000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  16000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  8000 <br>
11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  reserv.&nbsp;&nbsp;&nbsp;  &nbsp;&nbsp;  reserv.&nbsp;&nbsp;&nbsp;  reserv. <br>
<font color="#0000ff"><br>
G 1 (9) Padding bit</font> <br>
0 - frame is not padded&nbsp;&nbsp;&nbsp;&nbsp;  1 - frame is padded with one extra bit <br>
<font color="#0000ff"><br>
H 1 (8) Private bit (unknown purpose)</font> <br>
<font color="#0000ff"><br>
I 2 (7,6) Channel Mode <br>
</font>00 - Stereo <br>
01 - Joint stereo (Stereo) <br>
10 - Dual channel (Stereo) <br>
11 - Single channel (Mono) <br>
<font color="#0000ff"><br>
J 2 (5,4) Mode extension (Only if Joint stereo)</font> <br>
value Intensity stereo MS stereo <br>
00&nbsp;&nbsp;&nbsp;  off off <br>
01&nbsp;&nbsp;&nbsp;  on off <br>
10&nbsp;&nbsp;&nbsp;  off on <br>
11&nbsp;&nbsp;&nbsp;  on on <br>
<font color="#0000ff"><br>
K 1 (3) Copyright</font> <br>
0 - Audio is not copyrighted <br>
1 - Audio is copyrighted <br>
<font color="#0000ff"><br>
L 1 (2) Original</font> <br>
0 - Copy of original media <br>
1 - Original media <br>
<font color="#0000ff"><br>
M 2 (1,0) Emphasis</font> <br>
00 - none <br>
01 - 50/15 ms <br>
10 - reserved <br>
11 - CCIT J.17</p>
<p align="left"><br>
1）每帧的播放时间：无论帧长是多少，每帧的播放时间都是26ms； <br>
2）数据帧大小: <br>
FrameSize = (((MpegVersion == MPEG1 ? 144 : 72) * Bitrate) / SamplingRate) + PaddingBit <br>
例如: Bitrate ＝ 128000, a SamplingRate ＝44100, and PaddingBit ＝ 1 <br>
FrameSize = (144 * 128000) / 44100 + 1 = 417 bytes <br>
<br>
2．MAIN_DATA： <br>
MAIN_DATA 部分长度是否变化决定于FRAMEHEADER 的bitrate是否变化，一首MP3 歌曲，它有三个版本：96Kbps（96 千比特位每秒）、128Kbps 和192Kbps。Kbps （比特位速率）， 表明了音乐每秒的数据量，Kbps 值越高，音质越好，文件也越大，MP3标准规定，不变的bitrate 的MP3 文件称作CBR，大多数MP3 文件都是CBR 的，而变化的bitrate 的MP3 文件称作VBR， 每个FRAME 的长度都可能是变化的。下面是CBR 和VBR 的不同点： <br>
1）CBR：固定位率的FRAME 的大小也是固定的（公式如上所述），只要知道文件总长度，和帧长即可由播放每帧需26ms 计算得出mp3 播放的总时间，也可通过计数帧的个数控制快进、快退慢放等操作。 <br>
2）VBR：VBR 是XING 公司推出的算法，所以在MP3 的FRAME 里会有&ldquo;XING&quot;这个关键字（现在很多流行的小软件也可以进行VBR 压缩，它们是否遵守这个约定，那就不得而知了），它存放在MP3 文件中的第一个有效FRAME 里，它标识了这个MP3 文件是VBR 的。同时第一个FRAME 里存放了MP3 文件的FRAME 的总个数，这就很容易获得了播放总时间，同时还有100 个字节存放了播放总时间的100 个时间分段的FRAME 的INDEX，假设4 分钟的MP3 歌曲，240S， 分成100 段，每两个相邻INDEX 的时间差就是2.4S， 所以通过这个INDEX，只要前后处理少数的FRAME，就能快速找出我们需要快进的FRAME 头，可参考下文： <br>
This system was created to minimize file lengths and to preserve sound quality. <br>
Higher frequencies generally needs more space for encoding (thats why many codecs cut all <br>
frequencies above cca 16kHz) and lower tones requires less. So if some part of song doesnt consist<br>
of higher tones then using eg. 192kbps is wasting of space. It should be enough to use only eg. <br>
96kbps. <br>
And it is the principle of VBR. Codec looks over frame and then choose bitrate suitable for its<br>
sound quality. <br>
It sounds perfect but it brings some problems:<br>
If you want to jump over 2 minutes in song, it is not a problem with CBR because you are able <br>
simply count amount of Bytes which is necessary to skip. But it is impossible with VBR. Frame<br>
lengths should be arbitrary so you have to either go frame by frame and counts (time consuming <br>
and very unpractical) or use another mechanism for approximate count. <br>
If you want to cut 5 minutes from the middle of VBR file (all we know CDs where last song takes<br>
10 minutes but 5 minutes is a pure silence, HELL!) problems are the same. <br>
Result? VBR files are more difficult for controlling and adjusting. And I dont like feeling that<br>
sound quality changes in every moment. And AFAIK many codecs have problems with creation VBR in good quality. <br>
Personally I cant see any reason why to use VBR -I dont give a **** if size of one CD in MP3 <br>
is 55 MB with CBR or 51 MB with VBR. But everybody has a different taste... some people prefer <br>
VBR. <br>
VBR File Structure is the same as for CBR. But the first frame doesnt contain audio data and it is used for special information about VBR file. <br>
Structure of the first frame(the table as follow):<br>
<br>
Byte&nbsp;&nbsp;&nbsp;  Content<br>
<font color="#0000ff">0-3</font>&nbsp;&nbsp;  Standard audio frame header (as descripted above). Mostly it contains values FF<br>
FB 30 4C, from which you can count FrameLen = 156 Bytes. And thats exactly enough <br>
space for storing VBR info. <br>
This header contains some important information valid for the whole file: <br>
-MPEG (MPEG1 or MPEG2) <br>
-SAMPLING rate frequency index <br>
-CHANNEL (JointStereo etc.) <br>
<font color="#0000ff">4-x</font> Not used till string &quot;Xing&quot; (58 69 6E 67). This string is used as a main VBR file <br>
identifier. If it is not found, file is supposed to be CBR. This string can be placed <br>
at different locations according to values of MPEG and CHANNEL (ya, these from a <br>
few lines upwards): <br>
<font color="#0000ff">36-39</font> &quot;Xing&quot; for MPEG1 and CHANNEL != mono (mostly used) <br>
<font color="#0000ff">21-24</font> &quot;Xing&quot; for MPEG1 and CHANNEL == mono <br>
<font color="#0000ff">21-24</font> &quot;Xing&quot; for MPEG2 and CHANNEL != mono <br>
<font color="#0000ff">13-16</font> &quot;Xing&quot; for MPEG2 and CHANNEL == mono <br>
After &quot;Xing&quot; string there are placed flags, number of frames in file and a size <br>
of file in Bytes. Each of these items has 4 Bytes and it is stored as ''int'' number <br>
in memory. The first is the most significant Byte and the last is the least. <br>
Following schema is for MPEG1 and CHANNEL != mon <br>
<font color="#0000ff">40-43</font> Flags <br>
Value Name Description <br>
00 00 00 01 Frames Flag set if value for number of frames in file is stored <br>
00 00 00 02 Bytes Flag set if value for filesize in Bytes is stored <br>
00 00 00 04 TOC Flag set if values for TOC (see below) are stored <br>
00 00 00 08 VBR Scale Flag set if values for VBR scale are stored <br>
All these values can be stored simultaneously. <br>
<font color="#0000ff">44-47</font> Frames <br>
Number of frames in file (including the first info one) <br>
<font color="#0000ff">48-51</font> Bytes <br>
File length in Bytes <br>
<font color="#0000ff">52-151</font> TOC (Table of Contents) <br>
Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately <br>
solves problem with moving inside file. <br>
Each Byte has a value according this formula: <br>
(TOC[i] / 256) * fileLenInBytes <br>
So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000<br>
000 Bytes length) you can use: <br>
TOC[(60/240)*100] = TOC[25] <br>
and corresponding Byte in file is then approximately at: <br>
(TOC[25]/256) * 5000000 <br>
If you want to trim VBR file you should also reconstruct Frames, Bytes and TOC <br>
properly. <br>
<font color="#0000ff">152-155</font> VBR Scale <br>
I dont know exactly system of storing of this values but this item probably doesnt <br>
have deeper meaning. <br>
<br>
<strong>四．ID3v1</strong> <br>
ID3V1 比较简单，它是存放在MP3 文件的末尾，用16 进制的编辑器打开一个MP3 文件，查看其末尾<br>
的128 个顺序存放字节，数据结构定义如下： <br>
typedef struct tagID3V1 <br>
{ <br>
char Header[3]; /*标签头必须是&quot;TAG&quot;否则认为没有标签*/ <br>
char Title[30]; /*标题*/ <br>
char Artist[30]; /*作者*/ <br>
char Album[30]; /*专集*/ <br>
char Year[4]; /*出品年代*/ <br>
char Comment[28]; /*备注*/ <br>
char reserve; /*保留*/ <br>
char track;; /*音轨*/ <br>
char Genre; /*类型*/ <br>
}ID3V1,*pID3V1; <br>
ID3V1 的各项信息都是顺序存放，没有任何标识将其分开，比如标题信息不足30 个字节，则使用''\0''<br>
补足，否则将造成信息错误。Genre使用原码表示，对照表如下： <br>
/* Standard genres */ <br>
0=&quot;Blues&quot;; <br>
1=&quot;ClassicRock&quot;; <br>
2=&quot;Country&quot;; <br>
3=&quot;Dance&quot;; <br>
4=&quot;Disco&quot;; <br>
5=&quot;Funk&quot;; <br>
6=&quot;Grunge&quot;; <br>
7=&quot;Hip-Hop&quot;; <br>
8=&quot;Jazz&quot;; <br>
9=&quot;Metal&quot;; <br>
10=&quot;NewAge&quot;; <br>
11=&quot;Oldies&quot;; <br>
12=&quot;Other&quot;; <br>
13=&quot;Pop&quot;; <br>
14=&quot;R&amp;B&quot;; <br>
15=&quot;Rap&quot;; <br>
16=&quot;Reggae&quot;; <br>
17=&quot;Rock&quot;; <br>
18=&quot;Techno&quot;; <br>
19=&quot;Industrial&quot;; <br>
20=&quot;Alternative&quot;; <br>
21=&quot;Ska&quot;; <br>
22=&quot;DeathMetal&quot;; <br>
23=&quot;Pranks&quot;; <br>
24=&quot;Soundtrack&quot;; <br>
25=&quot;Euro-Techno&quot;; <br>
26=&quot;Ambient&quot;; <br>
27=&quot;Trip-Hop&quot;; <br>
28=&quot;Vocal&quot;; <br>
29=&quot;Jazz+Funk&quot;; <br>
30=&quot;Fusion&quot;; <br>
31=&quot;Trance&quot;; <br>
32=&quot;Classical&quot;; <br>
33=&quot;Instrumental&quot;; <br>
34=&quot;Acid&quot;; <br>
35=&quot;House&quot;; <br>
36=&quot;Game&quot;; <br>
37=&quot;SoundClip&quot;; <br>
38=&quot;Gospel&quot;; <br>
39=&quot;Noise&quot;; <br>
40=&quot;AlternRock&quot;; <br>
41=&quot;Bass&quot;; <br>
42=&quot;Soul&quot;; <br>
43=&quot;Punk&quot;; <br>
44=&quot;Space&quot;; <br>
45=&quot;Meditative&quot;; <br>
46=&quot;InstrumentalPop&quot;; <br>
47=&quot;InstrumentalRock&quot;; <br>
48=&quot;Ethnic&quot;; <br>
49=&quot;Gothic&quot;; <br>
50=&quot;Darkwave&quot;; <br>
51=&quot;Techno-Industrial&quot;; <br>
52=&quot;Electronic&quot;; <br>
53=&quot;Pop-Folk&quot;; <br>
54=&quot;Eurodance&quot;; <br>
55=&quot;Dream&quot;; <br>
56=&quot;SouthernRock&quot;; <br>
57=&quot;Comedy&quot;; <br>
58=&quot;Cult&quot;; <br>
59=&quot;Gangsta&quot;; <br>
60=&quot;Top40&quot;; <br>
61=&quot;ChristianRap&quot;; <br>
62=&quot;Pop/Funk&quot;; <br>
63=&quot;Jungle&quot;; <br>
64=&quot;NativeAmerican&quot;; <br>
65=&quot;Cabaret&quot;; <br>
66=&quot;NewWave&quot;; <br>
67=&quot;Psychadelic&quot;; <br>
68=&quot;Rave&quot;; <br>
69=&quot;Showtunes&quot;; <br>
70=&quot;Trailer&quot;; <br>
71=&quot;Lo-Fi&quot;; <br>
72=&quot;Tribal&quot;; <br>
73=&quot;AcidPunk&quot;; <br>
74=&quot;AcidJazz&quot;; <br>
75=&quot;Polka&quot;; <br>
76=&quot;Retro&quot;; <br>
77=&quot;Musical&quot;; <br>
78=&quot;Rock&amp;Roll&quot;; <br>
79=&quot;HardRock&quot;; <br>
/* Extended genres */ <br>
80=&quot;Folk&quot;; <br>
81=&quot;Folk-Rock&quot;; <br>
82=&quot;NationalFolk&quot;; <br>
83=&quot;Swing&quot;; <br>
84=&quot;FastFusion&quot;; <br>
85=&quot;Bebob&quot;; <br>
86=&quot;Latin&quot;; <br>
87=&quot;Revival&quot;; <br>
88=&quot;Celtic&quot;; <br>
89=&quot;Bluegrass&quot;; <br>
90=&quot;Avantgarde&quot;; <br>
91=&quot;GothicRock&quot;; <br>
92=&quot;ProgessiveRock&quot;; <br>
93=&quot;PsychedelicRock&quot;; <br>
94=&quot;SymphonicRock&quot;; <br>
95=&quot;SlowRock&quot;; <br>
96=&quot;BigBand&quot;; <br>
97=&quot;Chorus&quot;; <br>
98=&quot;EasyListening&quot;; <br>
99=&quot;Acoustic&quot;; <br>
100=&quot;Humour&quot;; <br>
101=&quot;Speech&quot;; <br>
102=&quot;Chanson&quot;; <br>
103=&quot;Opera&quot;; <br>
104=&quot;ChamberMusic&quot;; <br>
105=&quot;Sonata&quot;; <br>
106=&quot;Symphony&quot;; <br>
107=&quot;BootyBass&quot;; <br>
108=&quot;Primus&quot;; <br>
109=&quot;PornGroove&quot;; <br>
110=&quot;Satire&quot;; <br>
111=&quot;SlowJam&quot;; <br>
112=&quot;Club&quot;; <br>
113=&quot;Tango&quot;; <br>
114=&quot;Samba&quot;; <br>
115=&quot;Folklore&quot;; <br>
116=&quot;Ballad&quot;; <br>
117=&quot;PowerBallad&quot;; <br>
118=&quot;RhythmicSoul&quot;; <br>
119=&quot;Freestyle&quot;; <br>
120=&quot;Duet&quot;; <br>
121=&quot;PunkRock&quot;; <br>
122=&quot;DrumSolo&quot;; <br>
123=&quot;Acapella&quot;; <br>
124=&quot;Euro-House&quot;; <br>
125=&quot;DanceHall&quot;; <br>
126=&quot;Goa&quot;; <br>
127=&quot;Drum&amp;Bass&quot;; <br>
128=&quot;Club-House&quot;; <br>
129=&quot;Hardcore&quot;; <br>
130=&quot;Terror&quot;; <br>
131=&quot;Indie&quot;; <br>
132=&quot;BritPop&quot;; <br>
133=&quot;Negerpunk&quot;; <br>
134=&quot;PolskPunk&quot;; <br>
135=&quot;Beat&quot;; <br>
136=&quot;ChristianGangstaRap&quot;; <br>
137=&quot;HeavyMetal&quot;; <br>
138=&quot;BlackMetal&quot;; <br>
139=&quot;Crossover&quot;; <br>
140=&quot;ContemporaryChristian&quot;; <br>
141=&quot;ChristianRock&quot;; <br>
142=&quot;Merengue&quot;; <br>
143=&quot;Salsa&quot;; <br>
144=&quot;TrashMetal&quot;; <br>
145=&quot;Anime&quot;; <br>
146=&quot;JPop&quot;; <br>
147=&quot;Synthpop&quot;; <br>
<br>
<strong>五．ID3V2</strong> <br>
ID3V2 到现在一共有4 个版本，但流行的播放软件一般只支持第3 版， 既ID3v2.3。由于ID3V1 记录<br>
在MP3 文件的末尾，ID3V2就只好记录在MP3 文件的首部了(如果有一天发布ID3V3，真不知道该记录在哪<br>
里)。也正是由于这个原因，对ID3V2 的操作比ID3V1 要慢。而且ID3V2 结构比ID3V1 的结构要复杂得多，<br>
但比前者全面且可以伸缩和扩展。 <br>
下面就介绍一下ID3V2.3。 <br>
每个ID3V2.3 的标签都一个标签头和若干个标签帧或一个扩展标签头组成。关于曲目的信息如标题、作者<br>
等都存放在不同的标签帧中，扩展标签头和标签帧并不是必要的，但每个标签至少要有一个标签帧。标签<br>
头和标签帧一起顺序存放在MP3 文件的首部。 <br>
1、标签头 <br>
在文件的首部顺序记录10 个字节的ID3V2.3 的头部。数据结构如下： <br>
char Header[3]; /*必须为&quot;ID3&quot;否则认为标签不存在*/ <br>
char Ver; /*版本号ID3V2.3 就记录3*/ <br>
char Revision; /*副版本号此版本记录为0*/ <br>
char Flag; /*存放标志的字节，这个版本只定义了三位，稍后详细解说*/ <br>
char Size[4]; /*标签大小，包括标签头的10 个字节和所有的标签帧的大小*/ <br>
1）.标志字节 <br>
标志字节一般为0，定义如下： <br>
abc00000 <br>
a -- 表示是否使用Unsynchronisation(这个单词不知道是什么意思，字典里也没有找到，一般不设置) <br>
b -- 表示是否有扩展头部，一般没有(至少Winamp 没有记录)，所以一般也不设置 <br>
c -- 表示是否为测试标签(99.99%的标签都不是测试用的啦，所以一般也不设置) <br>
2）.标签大小 <br>
一共四个字节，但每个字节只用7 位，最高位不使用恒为0。所以格式如下 <br>
0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx <br>
计算大小时要将0 去掉，得到一个28 位的二进制数，就是标签大小(不懂为什么要这样做)，计算公式如<br>
下： <br>
int total_size; <br>
total_size = (Size[0]&amp;0x7F)*0x200000 <br>
+(Size[1]&amp;0x7F)*0x400 <br>
+(Size[2]&amp;0x7F)*0x80 <br>
+(Size[3]&amp;0x7F) <br>
2、标签帧 <br>
每个标签帧都有一个10 个字节的帧头和至少一个字节的不固定长度的内容组成。它们也是顺序存放在文件<br>
中，和标签头和其他的标签帧也没有特殊的字符分隔。得到一个完整的帧的内容只有从帧头中的到内容大<br>
小后才能读出，读取时要注意大小，不要将其他帧的内容或帧头读入。 <br>
帧头的定义如下： <br>
char FrameID[4]; /*用四个字符标识一个帧，说明其内容，稍后有常用的标识对照表*/ <br>
char Size[4]; /*帧内容的大小，不包括帧头，不得小于1*/ <br>
char Flags[2]; /*存放标志，只定义了6 位，稍后详细解说*/ <br>
1）.帧标识 <br>
用四个字符标识一个帧，说明一个帧的内容含义，常用的对照如下： <br>
TIT2=标题 表示内容为这首歌的标题，下同 <br>
TPE1=作者 <br>
TALB=专集 <br>
TRCK=音轨 格式：N/M 其中N 为专集中的第N 首，M为专集中共M 首，N和M 为ASCII 码表示的数字 <br>
TYER=年代 是用ASCII 码表示的数字 <br>
TCON=类型 直接用字符串表示 <br>
COMM=备注 格式：&quot;eng\0备注内容&quot;，其中eng 表示备注所使用的自然语言 <br>
2）.大小 <br>
这个可没有标签头的算法那么麻烦，每个字节的8 位全用，格式如下 <br>
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx <br>
算法如下： <br>
int FSize; <br>
FSize = Size[0]*0x100000000 <br>
+Size[1]*0x10000 <br>
+Size[2]*0x100 <br>
+Size[3]; <br>
3）.标志 <br>
只定义了6 位，另外的10 位为0，但大部分的情况下16 位都为0 就可以了。格式如下： <br>
abc00000 ijk00000 <br>
a -- 标签保护标志，设置时认为此帧作废 <br>
b -- 文件保护标志，设置时认为此帧作废 <br>
c -- 只读标志，设置时认为此帧不能修改(但我没有找到一个软件理会这个标志) <br>
i -- 压缩标志，设置时一个字节存放两个BCD 码表示数字 <br>
j -- 加密标志(没有见过哪个MP3 文件的标签用了加密) <br>
k -- 组标志，设置时说明此帧和其他的某帧是一组 <br>
值得一提的是winamp 在保存和读取帧内容的时候会在内容前面加个''\0''，并把这个字节计算在帧内容的<br>
大小中。 <br>
附：帧标识的含义 <br>
4）. Declared ID3v2 frames <br>
The following frames are declared in this draft. <br>
AENC Audio encryption <br>
APIC Attached picture <br>
COMM Comments <br>
COMR Commercial frame <br>
ENCR Encryption method registration <br>
EQUA Equalization <br>
ETCO Event timing codes <br>
GEOB General encapsulated object <br>
GRID Group identification registration <br>
IPLS Involved people list <br>
LINK Linked information <br>
MCDI Music CD identifier <br>
MLLT MPEG location lookup table <br>
OWNE Ownership frame <br>
PRIV Private frame <br>
PCNT Play counter <br>
POPM Popularimeter <br>
POSS Position synchronisation frame <br>
RBUF Recommended buffer size <br>
RVAD Relative volume adjustment <br>
RVRB Reverb <br>
SYLT Synchronized lyric/text <br>
SYTC Synchronized tempo codes <br>
TALB Album/Movie/Show title <br>
TBPM BPM (beats per minute) <br>
TCOM Composer <br>
TCON Content type <br>
TCOP Copyright message <br>
TDAT Date <br>
TDLY Playlist delay <br>
TENC Encoded by <br>
TEXT Lyricist/Text writer <br>
TFLT File type <br>
TIME Time <br>
TIT1 Content group description <br>
TIT2 Title/songname/content description <br>
TIT3 Subtitle/Description refinement <br>
TKEY Initial key <br>
TLAN Language(s) <br>
TLEN Length <br>
TMED Media type <br>
TOAL Original album/movie/show title <br>
TOFN Original filename <br>
TOLY Original lyricist(s)/text writer(s) <br>
TOPE Original artist(s)/performer(s) <br>
TORY Original release year <br>
TOWN File owner/licensee <br>
TPE1 Lead performer(s)/Soloist(s) <br>
TPE2 Band/orchestra/accompaniment <br>
TPE3 Conductor/performer refinement <br>
TPE4 Interpreted, remixed, or otherwise modified by <br>
TPOS Part of a set <br>
TPUB Publisher <br>
TRCK Track number/Position in set <br>
TRDA Recording dates <br>
TRSN Internet radio station name <br>
TRSO Internet radio station owner <br>
TSIZ Size <br>
TSRC ISRC (international standard recording code) <br>
TSSE Software/Hardware and settings used for encoding <br>
TYER Year <br>
TXXX User defined text information frame <br>
UFID Unique file identifier <br>
USER Terms of use <br>
USLT Unsychronized lyric/text transcription <br>
WCOM Commercial information <br>
WCOP Copyright/Legal information <br>
WOAF Official audio file webpage <br>
WOAR Official artist/performer webpage <br>
WOAS Official audio source webpage <br>
WORS Official internet radio station homepage <br>
WPAY Payment <br>
WPUB Publishers official webpage <br>
WXXX User defined URL link frame</p> <a href="http://hi.baidu.com/learned/blog/item/725fa9ecf2e505372697919f.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%D2%F4%CA%D3%C6%B5%BC%BC%CA%F5">音视频技术</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/725fa9ecf2e505372697919f.html#comment">查看评论</a>]]></description>
        <pubDate>2009年10月13日 星期二  下午 07:49</pubDate>
        <category><![CDATA[音视频技术]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/725fa9ecf2e505372697919f.html</guid>
</item>

<item>
        <title><![CDATA[转载：MMX及SSE优化--MMX篇]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/0814be45accaaf2ccefca35b.html]]></link>
        <description><![CDATA[
		
		<p>&nbsp;&nbsp;&nbsp;  MMX和SSE都是INTEL开发的基于SIMD(单指令多数据流)的技术。所谓单指令多数据流是指可以用一条指令可以完成多个数据的操作。虽然64位系统已经推出，但是我们大部分都是使用32位系统，所以如果要完成两个128位的相加运算，用普通32位指令很明显要执行4条相加指令，而基于64位的MMX指令只需要执行两次即可完成，更强大的SSE能一次处理128位，故一次就可以完成操作，所以采用MMX及SSE优化能够大幅度提升程序性能。<br>
&nbsp;&nbsp;&nbsp;  MMX采用处理器的80位的浮点寄存器的低64位作为MMX寄存器，一共有8个，从mm0到mm7,因为是&ldquo;借用&rdquo;浮点寄存器的低64位所以每次在用完MMX指令后一定要用EMMS指令将寄存器清空，MMX主要是针对整数运算进行优化，一个64位的MMX寄存器可以同时存入8个8位或者4个16位的整数，估计一次性就可以完成8次8位运算或者4次16位运算，要注意的MMX指令不能直接对32位数进行2次运算，但可以把32位拆分成两个16位再进行运算。MMX技术还有一个非常有用的特性是饱和运算，比如两个8位数相加：128+129相加后很明显超过了8位的最大值256，但是进行饱和运算相加的结果将是最大值256，饱和运算将运算结果控制在相应位数的范围内。 <br>
下面举一个例子：<br>
在VC 6 SP6中可以直接在内联汇编中使用mmx指令，而且调试时也能查看MMX寄存器<br>
__int16 a[]={1,2,3,4}<br>
__int16 b[]={5,6,7,8}<br>
_asm{<br>
movq mm0,a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //将数组a中的4个数一次存入mm0<br>
movq mm1,b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //将数组b中的4个数一次存入mm0<br>
paddsw mm0,mm1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //16位带符号的饱和相加,结果存在mm0中<br>
movq a,mm0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //再把mm0中的结果存放在数组a中  <br>
emms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //千万别忘了，擦干净<br>
}</p>
<p>也可以不用内联汇编直接用函数,MS的最新的SDK和INTEL的编译器都支持这种方式<br>
#include &lt;xmmintrin.h&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //使用所必须包含的头文件<br>
__m64 a=_mm_set_pi16(1, 2, 3, 4);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //向a中写入4个16位的整数<br>
__m64 b=_mm_set_pi16(5, 6, 7, 8);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //最后一位写入最低位，即8写入最低位<br>
a=_m_paddsw(a,b);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //完成16位加法运算<br>
_m_empty;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //一样不要忘记擦PP，嘿嘿</p>
<p>以上只是最基本最简单的介绍，MMX一共有57条指令，包括基本算术运算指令，比较指令，转换指令，逻辑指令，位移和传输指令，此处就不一一列举，详细的指令介绍请参看INTEL的官方网站。但请记住MMX是针对整数运算的，千万不要用于浮点运算，浮点运算要用更为强大的SSE指令完成，预知SSE详解且待下回分解。</p> <a href="http://hi.baidu.com/learned/blog/item/0814be45accaaf2ccefca35b.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%CB%E3%B7%A8%C9%E8%BC%C6">算法设计</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/0814be45accaaf2ccefca35b.html#comment">查看评论</a>]]></description>
        <pubDate>2009年09月28日 星期一  下午 12:41</pubDate>
        <category><![CDATA[算法设计]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/0814be45accaaf2ccefca35b.html</guid>
</item>

<item>
        <title><![CDATA[SSE2 （English）]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/cbe43d9b455876bfc8eaf45a.html]]></link>
        <description><![CDATA[
		
		<p>What is SSE2?<br>
SSE2 is an extension of assembly language which allows programs to execute one operation on multiple pieces of data at a time. Because SSE2 is assembly however, it only works on processors that support it. If the commands are attempted to be executed on a machine which is not capable of doing so, a general protection fault will be encountered. Luckily there are easy ways to tell if the processor(s) you are running on supports SSE2.</p>
<p>Basic Structure of SSE2<br>
SSE2 works just like any other set of assembly calls. There are registers in which data can be stored and operations that can execute on these registers. Each register is 16 bytes (2 doubles). The 8 registers are named xmm0 through xmm7.</p>
<p>Basics<br>
Some Code<br>
inline void Add(double *x, double *y, double *retval)<br>
{<br>
  asm<br>
  {<br>
&nbsp;&nbsp;&nbsp;  // Copy the first 16 bytes into xmm0, starting at the memory x points to<br>
&nbsp;&nbsp;&nbsp;  movupd xmm0, [x]<br>
&nbsp;&nbsp;&nbsp;  // Copy the first 16 bytes into xmm1, starting at the memory y points to<br>
&nbsp;&nbsp;&nbsp;  movupd xmm1, [y]<br>
&nbsp;&nbsp;&nbsp;  // Add the 2 doubles in xmm1 to the 2 doubles in xmm0, and put the<br>
&nbsp;&nbsp;&nbsp;  // result in xmm0, overwriting the previous data stored there<br>
&nbsp;&nbsp;&nbsp;  addpd xmm0, xmm1<br>
&nbsp;&nbsp;&nbsp;  // Copy the 16 bytes of data in xmm0 to the memory ret points to<br>
&nbsp;&nbsp;&nbsp;  movupd [retval], xmm0  <br>
  }<br>
}<br>
Hopefully my comments before each line were enough to let you know what was going on. In case they weren't, I'll go into a little more detail about each line.</p>
<p>&nbsp;&nbsp;&nbsp;  asm{}<br>
This keyword lets your compiler know that the code you are giving it will be in assembly and that it should compile it as such. It also, conveniently, tells the compiler to inline the code. This means that there is NO overhead for the asm block.</p>
<p>&nbsp;&nbsp;&nbsp;  movupd xmm0, [x]<br>
&nbsp;&nbsp;&nbsp;  movupd xmm1, [y]<br>
This command copies data from the second operand to the first; as always in Intel syntax, the asm is in dest, src order. By putting brackets around the x, we tell the mov command to copy the data that x points to the actual value of the pointer. The square brackets can be thought of as a method of dereferencing a pointer.</p>
<p>&nbsp;&nbsp;&nbsp;  addpd xmm0, xmm1<br>
This is the line that does the actual arithmetic. It takes the value from the 2nd operand, src, and adds it to the 1st operand, dest, and stores the resulting value in the 1st operand, dest.</p>
<p>&nbsp;&nbsp;&nbsp;  movupd [retval], xmm0<br>
Here, we copy the data that is in xmm0 to the memory that retval points to. Again, the square brackets dereference the variable retval in the same way that a '*' does in C/C++.</p>
<p>Some more operations<br>
subpd dest, src  // subtract dest from src, store in dest<br>
mulpd dest, src  // multiply dest and src, store in dest<br>
divpd dest, src  // divide src by dest, store in dest<br>
minpd dest, src  // store the smallest value, either dest or src, in dest<br>
maxpd dest, src  // store the largest value, either dest or src, in dest<br>
sqrtpd dest, src // take the square root of src and put the result in dest<br>
A full list of SSE2 operations and a description of each can be found at HAYES Technologies.</p>
<p>Making the Most Of SSE2<br>
The Faster Move Instruction<br>
Up until now, we have been using movapd to move data to and from our registers. This is much slower than the instruction movapd which does the exact same thing, but assumes that the data is 16 byte aligned. This means that the pointer supplied must be divisible by 16. This becomes a rather large problem if you are compiling your code with gcc or the one supplied with Microsoft Visual C++. One solution to this problem is to use a different compiler such as one that Intel provides. The inherent problem with this is that the Intel compiler is not freeware like gcc. If you have already spent money on some other compiler, you probably do not want to spend more on this new compiler. One hack that I have come up with is the following:</p>
<p>#define AllignData(data) (void *)(((int)data + 15) &amp;~ 0x0F)</p>
<p>//or an inline function if you prefer:</p>
<p>inline void *AllignData(void *data)<br>
{<br>
  return (void *)(((int)data + 15) &amp;~ 0x0F);<br>
}</p>
<p>void main<br>
{<br>
  const int sizeofdata = 512;</p>
<p>  double *lotsofdata;<br>
  double *tempptr;</p>
<p>  tempptr = new double[sizeofdata + 2];<br>
  lotsofdata = AllignData(tempptr);</p>
<p>  asm<br>
  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movapd xmm0, [lotsofdata];<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  // do lots of CPU intensive SSE2 operations on lotsofdata here<br>
  }</p>
<p>  delete [] tempptr;<br>
  lotsofdata = 0;<br>
  // do not delete lotsofdata, just set it to 0, the memory it<br>
  // points to is no longer valid<br>
}<br>
What we need for this instruction to work is memory that has a 16-byte alligned memory address. <br>
 </p>
<p>Memory that is 16byte alligned: <br>
 </p>
<p>When we use the new command in C/C++ to allocate memory, we are given memory that may or may not be 16byte alligned. So, what we do, is instead of using the very first bit of our memory block, we start using it at the first place that is 16byte alligned.<br>
-- yellow is unusbale, green is what we actually use -- <br>
 </p>
<p>By doing this, we waste the memory that comes before the first 16-byte alligned memory address. Normally this is not too big of a problem as the overhead is only once per memory block and if we are allocating large amounts of memory at once, 1 or even 12 bytes will hardly make a difference. The only problem with this is that by not using the beginning of our memory, we end up having a smaller amount of usable memory than we asked for. In the worst case, 15 bytes are not usable: <br>
 </p>
<p>Therefore, to make sure that we get a specific number of usable bytes, we allocate at least 15 extra bytes. In the case of allocating double values, this means we must allocate an extra 2 doubles, giving us 16 extra bytes: <br>
 </p>
<p>If you are not using doubles, just make sure that at least 15 extra bytes are allocated. Just determine the size of the data type and compute how many are needed to give you the needed padding.</p>
<p>Another important thing to notice is that we delete the variable that holds the original pointer to all of our memory. If you try to delete the memory starting in the middle of our memory block, you cause a general protection fault and have a memory leak.</p>
<p>Ordering Your Operations<br>
One thing that is often over looked is the order that registers are used. When an operation is performed, there is a delay while the result is bieng moved to its destination. if the next operation requires this value, it must wait for it to be stored into the register. If however, the next operation does not need this data, it does not need to wait for it to be stored, it can go ahead and execute at the same time that the previous result is getting stored.</p>
<p>For instance, there will be a speed difference between the following two code segments:</p>
<p>#1:</p>
<p>asm<br>
{<br>
  movupd xmm0, [x]&nbsp;&nbsp;  // xmm0 = x<br>
  movupd xmm1, [y]&nbsp;&nbsp;  // xmm1 = y<br>
  movupd xmm2, [z]&nbsp;&nbsp;  // xmm2 = z<br>
  movupd xmm3, [w]&nbsp;&nbsp;  // xmm3 = w</p>
<p>  movapd xmm4, xmm2  // xmm4 = z<br>
  movapd xmm5, xmm1  // xmm5 = y<br>
  movapd xmm6, xmm0  // xmm6 = x<br>
&nbsp;&nbsp;&nbsp;  <br>
  addpd xmm2, xmm3&nbsp;&nbsp;  // xmm2 = z + w<br>
  addpd xmm1, xmm2&nbsp;&nbsp;  // xmm1 = y + z + w<br>
  addpd xmm0, xmm1&nbsp;&nbsp;  // xmm0 = x + y + z + w</p>
<p>  mulpd xmm4, xmm3&nbsp;&nbsp;  // xmm4 = z * w<br>
  mulpd xmm5, xmm4&nbsp;&nbsp;  // xmm5 = y * z * w<br>
  mulpd xmm6, xmm5&nbsp;&nbsp;  // xmm6 = x * y * z * w</p>
<p>  divpd xmm0, xmm6&nbsp;&nbsp;  // xmm0 = (x * y * z * w) / (x + y + z + w)</p>
<p>  movupd [ret], xmm0 // ret = (x * y * z * w) / (x + y + z + w)<br>
}<br>
#2:</p>
<p>asm<br>
{<br>
  movupd xmm0, [x]&nbsp;&nbsp;  // xmm0 = x<br>
  movupd xmm1, [y]&nbsp;&nbsp;  // xmm1 = y<br>
  movupd xmm2, [z]&nbsp;&nbsp;  // xmm2 = z<br>
  movupd xmm3, [w]&nbsp;&nbsp;  // xmm3 = w</p>
<p>  movapd xmm4, xmm2  // xmm4 = z<br>
  movapd xmm5, xmm1  // xmm5 = y<br>
  movapd xmm6, xmm0  // xmm6 = x<br>
&nbsp;&nbsp;&nbsp;  <br>
  addpd xmm2, xmm3&nbsp;&nbsp;  // xmm2 = z + w<br>
  mulpd xmm4, xmm3&nbsp;&nbsp;  // xmm4 = z * w<br>
  addpd xmm1, xmm2&nbsp;&nbsp;  // xmm1 = y + z + w<br>
  mulpd xmm5, xmm4&nbsp;&nbsp;  // xmm5 = y * z * w<br>
  addpd xmm0, xmm1&nbsp;&nbsp;  // xmm0 = x + y + z + w<br>
  mulpd xmm6, xmm5&nbsp;&nbsp;  // xmm6 = x * y * z * w</p>
<p>  divpd xmm0, xmm6&nbsp;&nbsp;  // xmm0 = (x * y * z * w) / (x + y + z + w)</p>
<p>  movupd [ret], xmm0 // ret = (x * y * z * w) / (x + y + z + w)<br>
}<br>
The second piece of code will run faster. This is because in the second case, there are only 2 cases where one instruction relies on the data from the previous one to perform its computations. Because of this, instructions can be executed immediately after the previous one finsihes instead of waiting for it to store its result in the registers.</p>
<p>Don't get carried away<br>
A common mistake made by people new to SSE2 is to convert a lot of their old and future code into SSE2. This can actually result in slower code. The reason for this is the very large overhead for the CPU to copy memory to the registers. If you have an application that is doing a small number of operations on a large data set, you can expect to be less efficient than if you are doing a lot of operations on a small amount of data.</p>
<p>Compiling SSE2 with gcc/g++<br>
The first thing that you need to remember to do when you want to compile SSE2 embedded C/C++ code with gcc/g++, is to throw in the -masm=intel switch during compile. You must also put &quot;.intel_syntax noprefix&quot; in front of your asm code and surround it with quotes like this:</p>
<p>&nbsp;&nbsp;&nbsp;  asm(&quot;.intel_syntax noprefix\n&quot;);<br>
&nbsp;&nbsp;&nbsp;  asm(&quot;&nbsp;&nbsp;&nbsp;  mov eax, x\n&quot;);<br>
&nbsp;&nbsp;&nbsp;  asm(&quot;&nbsp;&nbsp;&nbsp;  movupd xmm0, [eax+0x00]\n&quot;);<br>
&nbsp;&nbsp;&nbsp;  asm(&quot;&nbsp;&nbsp;&nbsp;  movupd xmm1, [eax+0x10]\n&quot;);<br>
&nbsp;&nbsp;&nbsp;  asm(&quot;&nbsp;&nbsp;&nbsp;  addpd xmm0, xmm1\n&quot;);<br>
&nbsp;&nbsp;&nbsp;  asm(&quot;&nbsp;&nbsp;&nbsp;  movupd [eax+0x20], xmm0\n&quot;);</p>
<p>&nbsp;&nbsp;&nbsp;  or</p>
<p>&nbsp;&nbsp;&nbsp;  asm(&quot;.intel_syntax noprefix<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mov eax, x<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movupd xmm0, [eax+0x00]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movupd xmm1, [eax+0x10]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  addpd xmm0, xmm1<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movupd [eax+0x20], xmm0\n&quot;);<br>
Note that the asm block is inside &quot;()&quot; not &quot;{}&quot;. Also, if you want to use a variable declared in your C/C++ code, you must define it publicly. Any variables defined locally, whether inside your main function, inside a for loop, etc, will not be seen by the linker and will be considered an &quot;undefined reference&quot;.</p>
<p><br>
本文来自CSDN博客，转载出处：<a href="http://blog.csdn.net/liu_chulong/articles/833911.aspx">http://blog.csdn.net/liu_chulong/articles/833911.aspx</a></p> <a href="http://hi.baidu.com/learned/blog/item/cbe43d9b455876bfc8eaf45a.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%CB%E3%B7%A8%C9%E8%BC%C6">算法设计</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/cbe43d9b455876bfc8eaf45a.html#comment">查看评论</a>]]></description>
        <pubDate>2009年09月28日 星期一  下午 12:40</pubDate>
        <category><![CDATA[算法设计]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/cbe43d9b455876bfc8eaf45a.html</guid>
</item>

<item>
        <title><![CDATA[SSE技术简介]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/5971c411c5e96a1ab8127b59.html]]></link>
        <description><![CDATA[
		
		<p>　　Intel公司的单指令多数据流式扩展（SSE，Streaming SIMD Extensions）技术能够有效增强CPU浮点运算的能力。Visual Studio .NET 2003提供了对SSE指令集的编程支持，从而允许用户在C++代码中不用编写汇编代码就可直接使用SSE指令的功能。MSDN中有关SSE技术的主题[1]有可能会使不熟悉使用SSE汇编指令编程的初学者感到困惑，但是在阅读MSDN有关文档的同时，参考一下Intel软件说明书（Intel Software manuals）[2]会使你更清楚地理解使用SSE指令编程的要点。</p>
<p>　　SIMD（single-instruction, multiple-data）是一种使用单道指令处理多道数据流的CPU执行模式，即在一个CPU指令执行周期内用一道指令完成处理多个数据的操作。考虑一下下面这个任务：计算一个很长的浮点型数组中每一个元素的平方根。实现这个任务的算法可以这样写：</p>
<p>&nbsp;&nbsp;&nbsp;  for each f in array //对数组中的每一个元素<br>
&nbsp;&nbsp;&nbsp;  f = sqrt(f) //计算它的平方根</p>
<p>为了了解实现的细节，我们把上面的代码这样写：</p>
<p>&nbsp;&nbsp;&nbsp;  for each f in array<br>
&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  把f从内存加载到浮点寄存器<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  计算平方根<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  再把计算结果从寄存器中取出放入内存<br>
&nbsp;&nbsp;&nbsp;  }</p>
<p>具有Intel SSE指令集支持的处理器有8个128位的寄存器，每一个寄存器可以存放4个（32位）单精度的浮点数。SSE同时提供了一个指令集，其中的指令可以允许把浮点数加载到这些128位的寄存器之中，这些数就可以在这些寄存器中进行算术逻辑运算，然后把结果放回内存。采用SSE技术后，算法可以写成下面的样子：</p>
<p>&nbsp;&nbsp;&nbsp;  for each 4 members in array //对数组中的每4个元素<br>
&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  把数组中的这4个数加载到一个128位的SSE寄存器中<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  在一个CPU指令执行周期中完成计算这4个数的平方根的操作<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  把所得的4个结果取出写入内存<br>
&nbsp;&nbsp;&nbsp;  }</p>
<p>C++编程人员在使用SSE指令函数编程时不必关心这些128位的寄存器，你可以使用128位的数据类型&ldquo;__m128&rdquo;和一系列C++函数来实现这些算术和逻辑操作，而决定程序使用哪个SSE寄存器以及代码优化是C++编译器的任务。当需要对很长的浮点数数组中的元素进行处理的时候，SSE技术确实是一种很高效的方法。</p>
<p><br>
SSE程序设计详细介绍</p>
<p>包含的头文件：</p>
<p>所有的SSE指令函数和__m128数据类型都在xmmintrin.h文件中定义：</p>
<p>&nbsp;&nbsp;&nbsp;  #include &lt;xmmintrin.h&gt;</p>
<p>因为程序中用到的SSE处理器指令是由编译器决定，所以它并没有相关的.lib库文件。</p>
<p>　数据分组（Data Alignment）</p>
<p>　　由SSE指令处理的每一个浮点数数组必须把其中需要处理的数每16个字节（128位二进制）分为一组。一个静态数组（static array）可由__declspec(align(16))关键字声明：</p>
<p>&nbsp;&nbsp;&nbsp;  __declspec(align(16)) float m_fArray[ARRAY_SIZE];</p>
<p>动态数组（dynamic array）可由_aligned_malloc函数为其分配空间：</p>
<p>&nbsp;&nbsp;&nbsp;  m_fArray = (float*) _aligned_malloc(ARRAY_SIZE * sizeof(float), 16);</p>
<p>由_aligned_malloc函数分配空间的动态数组可以由_aligned_free函数释放其占用的空间：</p>
<p>&nbsp;&nbsp;&nbsp;  _aligned_free(m_fArray);</p>
<p>　__m128 数据类型</p>
<p>　　该数据类型的变量可用做SSE指令的操作数，它们不能被用户指令直接存取。_m128类型的变量被自动分配为16个字节的字长。</p>
<p>　CPU对SSE指令集的支持</p>
<p>　　如果你的CPU能够具有了SSE指令集，你就可以使用Visual Studio .NET 2003提供的对SSE指令集支持的C++函数库了，你可以查看MSDN中的一个Visual C++ CPUID的例子[4]，它可以帮你检测你的CPU是否支持SSE、MMX指令集或其它的CPU功能。</p>
<p><br>
　编程实例</p>
<p>　　以下讲解了SSE技术在Visual Studio .NET 2003下的应用实例，你可以在<a href="http://www.codeproject.com/cpp/sseintro/SSE_src.zip">http://www.codeproject.com/cpp/sseintro/SSE_src.zip</a>下载示例程序压缩包。该压缩包中含有两个项目，这两个项目是基于微软基本类库（MFC）建立的Visual C++.NET项目，你也可以按照下面的讲解建立这两个项目。</p>
<p>　SSETest 示例项目</p>
<p>SSETest项目是一个基于对话框的应用程序，它用到了三个浮点数组参与运算：</p>
<p>&nbsp;&nbsp;&nbsp;  fResult[i] = sqrt( fSource1[i]*fSource1[i] + fSource2[i]*fSource2[i] ) + 0.5</p>
<p>其中i = 0, 1, 2 ... ARRAY_SIZE-1</p>
<p>其中ARRAY_SIZE被定义为30000。数据源数组（Source数组）通过使用sin和cos函数给它赋值，我们用Kris Jearakul开发的瀑布状图表控件（Waterfall chart control）[3] 来显示参与计算的源数组和结果数组。计算所需的时间(以毫秒ms为单位)在对话框中显示出来。我们使用三种不同的途径来完成计算：</p>
<p>纯C++代码；<br>
使用SSE指令函数的C++代码；<br>
包含SSE汇编指令的代码。</p>
<p><br>
　纯C++代码：</p>
<p>&nbsp;&nbsp;&nbsp;  void CSSETestDlg::ComputeArrayCPlusPlus(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pArray1, // [输入] 源数组1<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pArray2, // [输入] 源数组2<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pResult, // [输出] 用来存放结果的数组<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int nSize) // [输入] 数组的大小<br>
&nbsp;&nbsp;&nbsp;  {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int i;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pSource1 = pArray1;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pSource2 = pArray2;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pDest = pResult;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  for ( i = 0; i &lt; nSize; i++ )<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  *pDest = (float)sqrt((*pSource1) * (*pSource1) + (*pSource2) * (*pSource2)) + 0.5f;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pSource1++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pSource2++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pDest++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;  }</p>
<p><br>
　　下面我们用具有SSE特性的C++代码重写上面这个函数。为了查询使用SSE指令C++函数的方法，我参考了Intel软件说明书（Intel Software manuals）中有关SSE汇编指令的说明，首先我是在第一卷的第九章找到的相关SSE指令，然后在第二卷找到了这些SSE指令的详细说明，这些说明有一部分涉及了与其特性相关的C++函数。然后我通过这些SSE指令对应的C++函数查找了MSDN中与其相关的说明。搜索的结果见下表：<br>
　 <br>
实现的功能 对应的SSE汇编指令 Visual C++.NET中的SSE函数 <br>
将4个32位浮点数放进一个128位的存储单元。 movss 和 shufps _mm_set_ps1  <br>
将4对32位浮点数同时进行相乘操作。这4对32位浮点数来自两个128位的存储单元，再把计算结果（乘积）赋给一个128位的存储单元。 mulps _mm_mul_ps <br>
将4对32位浮点数同时进行相加操作。这4对32位浮点数来自两个128位的存储单元，再把计算结果（相加之和）赋给一个128位的存储单元。 addps _mm_add_ps <br>
对一个128位存储单元中的4个32位浮点数同时进行求平方根操作。 sqrtps _mm_sqrt_ps <br>
 <br>
 </p>
<p><br>
　使用Visual C++.NET的 SSE指令函数的代码：</p>
<p>&nbsp;&nbsp;&nbsp;  void CSSETestDlg::ComputeArrayCPlusPlusSSE(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pArray1, // [输入] 源数组1<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pArray2, // [输入] 源数组2<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pResult, // [输出] 用来存放结果的数组<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int nSize) // [输入] 数组的大小<br>
&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int nLoop = nSize/ 4;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 m1, m2, m3, m4;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128* pSrc1 = (__m128*) pArray1;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128* pSrc2 = (__m128*) pArray2;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128* pDest = (__m128*) pResult;</p>
<p><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 m0_5 = _mm_set_ps1(0.5f); // m0_5[0, 1, 2, 3] = 0.5</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  for ( int i = 0; i &lt; nLoop; i++ )<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m1 = _mm_mul_ps(*pSrc1, *pSrc1); // m1 = *pSrc1 * *pSrc1<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m2 = _mm_mul_ps(*pSrc2, *pSrc2); // m2 = *pSrc2 * *pSrc2<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m3 = _mm_add_ps(m1, m2); // m3 = m1 + m2<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m4 = _mm_sqrt_ps(m3); // m4 = sqrt(m3)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  *pDest = _mm_add_ps(m4, m0_5); // *pDest = m4 + 0.5</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pSrc1++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pSrc2++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pDest++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;  }</p>
<p>　使用SSE汇编指令实现的C++函数代码：</p>
<p>&nbsp;&nbsp;&nbsp;  void CSSETestDlg::ComputeArrayAssemblySSE(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pArray1, // [输入] 源数组1<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pArray2, // [输入] 源数组2<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pResult, // [输出] 用来存放结果的数组<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int nSize) // [输入] 数组的大小<br>
&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int nLoop = nSize/4;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float f = 0.5f;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  _asm<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movss xmm2, f // xmm2[0] = 0.5<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  shufps xmm2, xmm2, 0 // xmm2[1, 2, 3] = xmm2[0]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mov esi, pArray1 // 输入的源数组1的地址送往esi<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mov edx, pArray2 // 输入的源数组2的地址送往edx</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mov edi, pResult // 输出结果数组的地址保存在edi<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mov ecx, nLoop //循环次数送往ecx</p>
<p>start_loop:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movaps xmm0, [esi] // xmm0 = [esi]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mulps xmm0, xmm0 // xmm0 = xmm0 * xmm0</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movaps xmm1, [edx] // xmm1 = [edx]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mulps xmm1, xmm1 // xmm1 = xmm1 * xmm1</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  addps xmm0, xmm1 // xmm0 = xmm0 + xmm1<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  sqrtps xmm0, xmm0 // xmm0 = sqrt(xmm0)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  addps xmm0, xmm2 // xmm0 = xmm1 + xmm2</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movaps [edi], xmm0 // [edi] = xmm0</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  add esi, 16 // esi += 16<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  add edx, 16 // edx += 16<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  add edi, 16 // edi += 16</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  dec ecx // ecx--<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  jnz start_loop //如果不为0则转向start_loop<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;  }</p>
<p>最后，在我的计算机上运行计算测试的结果：</p>
<p>纯C++代码计算所用的时间是26 毫秒 <br>
使用SSE的C++ 函数计算所用的时间是 9 毫秒 <br>
包含SSE汇编指令的C++代码计算所用的时间是 9 毫秒</p>
<p>以上的时间结果是在Release优化编译后执行程序得出的。</p>
<p> </p>
<p>SSESample 示例项目</p>
<p><br>
SSESample项目是一个基于对话框的应用程序，其中它用下面的浮点数数组进行计算：</p>
<p>&nbsp;&nbsp;&nbsp;  fResult[i] = sqrt(fSource[i]*2.8)</p>
<p>其中i = 0, 1, 2 ... ARRAY_SIZE-1</p>
<p>这个程序同时计算了数组中的最大值和最小值。ARRAY_SIZE被定义为100000，数组中的计算结果在列表框中显示出来。其中在我的机子上用下面三种方法计算所需的时间是：<br>
&nbsp;&nbsp;&nbsp;  纯C++代码计算 6 毫秒 <br>
&nbsp;&nbsp;&nbsp;  使用SSE的C++ 函数计算 3 毫秒 <br>
&nbsp;&nbsp;&nbsp;  使用SSE汇编指令计算 2 毫秒</p>
<p>大家看到，使用SSE汇编指令计算的结果会好一些，因为使用了效率增强了的SSX寄存器组。但是在通常情况下，使用SSE的C++ 函数计算会比汇编代码计算的效率更高一些，因为C++编译器的优化后的代码有很高的运算效率，若要使汇编代码比优化后的代码运算效率更高，这通常是很难做到的。</p>
<p>　纯C++代码：</p>
<p>&nbsp;&nbsp;&nbsp;  // 输入: m_fInitialArray<br>
&nbsp;&nbsp;&nbsp;  // 输出: m_fResultArray, m_fMin, m_fMax<br>
&nbsp;&nbsp;&nbsp;  void CSSESampleDlg::OnBnClickedButtonCplusplus()<br>
&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m_fMin = FLT_MAX;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m_fMax = FLT_MIN;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int i;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  for ( i = 0; i &lt; ARRAY_SIZE; i++ )<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m_fResultArray[i] = sqrt(m_fInitialArray[i] * 2.8f);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  if ( m_fResultArray[i] &lt; m_fMin )<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m_fMin = m_fResultArray[i];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  if ( m_fResultArray[i] &gt; m_fMax )<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m_fMax = m_fResultArray[i];<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;  }</p>
<p><br>
　使用Visual C++.NET的 SSE指令函数的代码：</p>
<p>&nbsp;&nbsp;&nbsp;  // 输入: m_fInitialArray<br>
&nbsp;&nbsp;&nbsp;  // 输出: m_fResultArray, m_fMin, m_fMax<br>
&nbsp;&nbsp;&nbsp;  void CSSESampleDlg::OnBnClickedButtonSseC()<br>
&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 coeff = _mm_set_ps1(2.8f); // coeff[0, 1, 2, 3] = 2.8<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 tmp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 min128 = _mm_set_ps1(FLT_MAX); // min128[0, 1, 2, 3] = FLT_MAX<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 max128 = _mm_set_ps1(FLT_MIN); // max128[0, 1, 2, 3] = FLT_MIN</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128* pSource = (__m128*) m_fInitialArray;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128* pDest = (__m128*) m_fResultArray;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  for ( int i = 0; i &lt; ARRAY_SIZE/4; i++ )<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  tmp = _mm_mul_ps(*pSource, coeff); // tmp = *pSource * coeff<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  *pDest = _mm_sqrt_ps(tmp); // *pDest = sqrt(tmp)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  min128 = _mm_min_ps(*pDest, min128);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  max128 = _mm_max_ps(*pDest, max128);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pSource++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pDest++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  // 计算max128的最大值和min128的最小值<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  union u<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 m;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float f[4];<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  } x;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  x.m = min128;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m_fMin = min(x.f[0], min(x.f[1], min(x.f[2], x.f[3])));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  x.m = max128;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m_fMax = max(x.f[0], max(x.f[1], max(x.f[2], x.f[3])));<br>
&nbsp;&nbsp;&nbsp;  }</p>
<p><br>
　使用SSE汇编指令的C++函数代码：</p>
<p>&nbsp;&nbsp;&nbsp;  // 输入: m_fInitialArray<br>
&nbsp;&nbsp;&nbsp;  // 输出: m_fResultArray, m_fMin, m_fMax<br>
&nbsp;&nbsp;&nbsp;  void CSSESampleDlg::OnBnClickedButtonSseAssembly()<br>
&nbsp;&nbsp;&nbsp;  {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pIn = m_fInitialArray;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float* pOut = m_fResultArray;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float f = 2.8f;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float flt_min = FLT_MIN;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float flt_max = FLT_MAX;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 min128;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 max128;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  // 使用以下的附加寄存器:xmm2、xmm3、xmm4:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  // xmm2 &ndash; 相乘系数<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  // xmm3 &ndash; 最小值<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  // xmm4 &ndash; 最大值</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  _asm<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movss xmm2, f // xmm2[0] = 2.8<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  shufps xmm2, xmm2, 0 // xmm2[1, 2, 3] = xmm2[0]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movss xmm3, flt_max // xmm3 = FLT_MAX<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  shufps xmm3, xmm3, 0 // xmm3[1, 2, 3] = xmm3[0]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movss xmm4, flt_min // xmm4 = FLT_MIN<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  shufps xmm4, xmm4, 0 // xmm3[1, 2, 3] = xmm3[0]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mov esi, pIn // 输入数组的地址送往esi<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mov edi, pOut // 输出数组的地址送往edi<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mov ecx, ARRAY_SIZE/4 // 循环计数器初始化</p>
<p>start_loop:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movaps xmm1, [esi] // xmm1 = [esi]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  mulps xmm1, xmm2 // xmm1 = xmm1 * xmm2<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  sqrtps xmm1, xmm1 // xmm1 = sqrt(xmm1)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movaps [edi], xmm1 // [edi] = xmm1</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  minps xmm3, xmm1<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  maxps xmm4, xmm1</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  add esi, 16<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  add edi, 16</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  dec ecx<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  jnz start_loop</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movaps min128, xmm3<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  movaps max128, xmm4<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  union u<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 m;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float f[4];<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  } x;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  x.m = min128;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m_fMin = min(x.f[0], min(x.f[1], min(x.f[2], x.f[3])));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  x.m = max128;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  m_fMax = max(x.f[0], max(x.f[1], max(x.f[2], x.f[3])));<br>
&nbsp;&nbsp;&nbsp;  }</p>
<p><br>
参考文档：</p>
<p>[1]MSDN, SSE技术主题：<br>
<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vcrefstreamingsimdextensions.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vcrefstreamingsimdextensions.asp</a></p>
<p>[2]Intel软件说明书（Intel Software manuals）：<br>
<a href="http://developer.intel.com/design/archives/processors/mmx/index.htm">http://developer.intel.com/design/archives/processors/mmx/index.htm</a></p>
<p>[3] Kris Jearakul的瀑布状图表控件：<a href="http://www.codeguru.com/controls/Waterfall.shtml">http://www.codeguru.com/controls/Waterfall.shtml</a></p>
<p>[4] Microsoft Visual C++ CPUID示例：<br>
<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample/html/vcsamcpuiddeterminecpucapabilities.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample/html/vcsamcpuiddeterminecpucapabilities.asp</a></p>
<p>[5] Matt Pietrek在Microsoft Systems Journal 1998年2月刊上的评论文章：<br>
<a href="http://www.microsoft.com/msj/0298/hood0298.aspx">http://www.microsoft.com/msj/0298/hood0298.aspx</a> 。</p>
<p><br>
本文来自CSDN博客，转载出处：<a href="http://blog.csdn.net/liu_chulong/articles/833915.aspx">http://blog.csdn.net/liu_chulong/articles/833915.aspx</a></p> <a href="http://hi.baidu.com/learned/blog/item/5971c411c5e96a1ab8127b59.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%CB%E3%B7%A8%C9%E8%BC%C6">算法设计</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/5971c411c5e96a1ab8127b59.html#comment">查看评论</a>]]></description>
        <pubDate>2009年09月28日 星期一  下午 12:39</pubDate>
        <category><![CDATA[算法设计]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/5971c411c5e96a1ab8127b59.html</guid>
</item>

<item>
        <title><![CDATA[SSE指令介绍及其C、C++应用]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/d74b0a3b1cd7e4e014cecb59.html]]></link>
        <description><![CDATA[
		
		<p>  SSE是英特尔提出的即MMX之后新一代（当然是几年前了）CPU指令集，最早应用在PIII系列CPU上。现在已经得到了Intel PIII、P4、Celeon、Xeon、AMD Athlon、duron等系列CPU的支持。而更新的SSE2指令集仅得到了P4系列CPU的支持，这也是为什么这篇文章是讲SSE而不是SSE2的原因之一。另一个原因就是SSE和SSE2的指令系统是非常相似的，SSE2比SSE多的仅是少量的额外浮点处理功能、64位浮点数运算支持和64位整数运算支持。 <br>
  SSE为什么会比传统的浮点运算更快呢？因为它使用了128位的存储单元，这对于32位的浮点数来讲，是可以存下4个的，也就是说，SSE中的所有计算都是一次性针对4个浮点数来完成的，这种批处理当然就会带来效率的提升。我们再来回顾一下SSE的全称：Stream SIMD Extentions（流SIMD扩展）。SIMD就是single instruction multiple data，连起来就是&ldquo;数据流单指令多数据扩展&rdquo;，从名字我们就可以更好的理解SSE是如何工作的了。</p>
<p>  虽然SSE从理论上来讲要比传统的浮点运算会快，但是他所受的限制也很多，首先，虽然他执行一次相当于四次，会比传统的浮点运算执行4次的速度要快，但是他执行一次的速度却并没有想象中的那么快，所以要体现SSE的速度，必须有Stream做前提，就是大量的流数据，这样才能发挥SIMD的强大作用。其次，SSE支持的数据类型是4个32位（共计128位）浮点数集合，就是C、C++语言中的float[4]，并且必须是以16位字节边界对齐的（稍后会以代码来进行阐释，关于边界对齐的概念，读者可以参考论坛上的其它文章，都会有很详细的解答，我这里就恕不赘述了）。因此这也给输入和输出带来了不少的麻烦，实际上主要影响SSE发挥性能的就是不停的对数据进行复制以适用应它的数据格式。</p>
<p>  我是一个C++程序员，对汇编并不很熟，但我又想用SSE来优化我的程序，我该怎么做呢？幸好VC++.net为我们提供了很方便的指令C函数级的封装和C格式数据类型，我们只需像平时写C++代码一样定义变量、调用函数就可以很好的应用SSE指令了。</p>
<p>  当然了，我们需要包含一个头文件，这里面包括了我们需要的数据类型和函数的声明：</p>
<p> #include &lt;xmmintrin.h&gt;<br>
 </p>
<p><br>
  SSE运算的标准数据类型只有一个，就是：</p>
<p>__m128，它是这样定义的：</p>
<p> typedef struct __declspec(intrin_type) __declspec(align(16)) __m128 {</p>
<p>&nbsp;&nbsp;  float m128_f32[4];</p>
<p>} __m128;<br>
 </p>
<p><br>
  简化一下，就是：</p>
<p> struct __m128</p>
<p>{</p>
<p>&nbsp;&nbsp;  float m128_f32[4];</p>
<p>};<br>
 </p>
<p><br>
  比如要定义一个__m128变量，并为它赋四个float整数，可以这样写：</p>
<p><br>
 __m128 S1 = { 1.0f, 2.0f, 3,0f, 4,0f };<br>
 </p>
<p> </p>
<p>  要改变其中第2个（基数为0）元素时可以这样写：</p>
<p> S1.m128_f32[2] = 6.0f;<br>
 </p>
<p><br>
  令外我们还会用到几个赋值的指令，它可以让我们更方便的使用这个数据结构：</p>
<p> S1 = _mm_set_ps1( 2.0f );<br>
 </p>
<p><br>
  它会让S1.m128_f32中的四个元素全部赋予2.0f，这样会比你一个一个赋值要快的多。</p>
<p> S1 = _mm_setzero_ps();<br>
 </p>
<p><br>
  这会让S1中的所有4个浮点数都置零。</p>
<p>  还有一些其它的赋值指令，但执行起来还没有自己逐个赋值来的快，只做为一些特殊用途，如果你想了解更多的信息，可以参考MSDN -&gt; VisualC++参考 -&gt; C/C++Language -&gt; C++Language Reference -&gt; Compiler Intrinsics -&gt; MMX, SSE, and SSE2 Intrinsics -&gt; Stream SIMD Extensions(SSE)章节。</p>
<p>  一般来讲，所有SSE指令函数都有3个部分组成，中间用下划线隔开：</p>
<p> _mm_set_ps1<br>
 </p>
<p><br>
  mm表示多媒体扩展指令集</p>
<p>  set表示此函数的含义缩写</p>
<p>  ps1表示该函数对结果变量的影响，由两个字母组成，第一个字母表示对结果变量的影响方式，p表示把结果做为指向一组数据的指针，每一个元素都将参与运算，S表示只将结果变量中的第一个元素参与运算；第二个字母表示参与运算的数据类型。s表示32位浮点数，d表示64位浮点数，i32表示32位定点数，i64表示64位定点数，由于SSE只支持32位浮点数的运算，所以你可能会在这些指令封装函数中找不到包含非s修饰符的，但你可以在MMX和SSE2的指令集中去认识它们。</p>
<p>  接下来我举一个例子来说明SSE的指令函数是如何使用的，必须要说明的是我以下的代码都是在VC7.1的平台上写的，不保证对其它如Dev-C++、Borland C++等开发平台的完全兼容。</p>
<p>  为了方便对比速度，我会用常归方法和SSE优化两种写法写出，并会用一个测试速度的类CTimer来进行计时。</p>
<p>  这个算法是对一组float值进行放大，函数ScaleValue1是使用SSE指令优化的，函数ScaleValue2则没有。我们用10000个元素的float数组数据来测试这两个算法，每个算法运算10000遍，下面是测试程序和结果：</p>
<p> #include &lt;xmmintrin.h&gt;</p>
<p>#include &lt;windows.h&gt;<br>
 </p>
<p> </p>
<p> class CTimer</p>
<p>{</p>
<p>public:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __forceinline CTimer( void )</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  QueryPerformanceFrequency( &amp;m_Frequency );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  QueryPerformanceCounter( &amp;m_StartCount );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __forceinline void Reset( void )</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  QueryPerformanceCounter( &amp;m_StartCount );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __forceinline double End( void )</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  static __int64 nCurCount;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  QueryPerformanceCounter( (PLARGE_INTEGER)&amp;nCurCount );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return double(( nCurCount - ( *(__int64*)&amp;m_StartCount ) ) )/ double( *(__int64*)&amp;m_Frequency );</p>
<p><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</p>
<p>private:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  LARGE_INTEGER m_Frequency;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  LARGE_INTEGER m_StartCount;</p>
<p>};</p>
<p>void ScaleValue1( float *pArray, DWORD dwCount, float fScale )</p>
<p>{<br>
 </p>
<p> </p>
<p><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  DWORD dwGroupCount = dwCount / 4;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  __m128 e_Scale = _mm_set_ps1( fScale );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  for ( DWORD i = 0; i &lt; dwGroupCount; i++ )</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  *(__m128*)( pArray + i * 4 ) = _mm_mul_ps( *(__m128*)( pArray + i * 4 ), e_Scale );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</p>
<p>}</p>
<p>void ScaleValue2( float *pArray, DWORD dwCount, float fScale )</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  for ( DWORD i = 0; i &lt; dwCount; i++ )</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pArray[i] *= fScale;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</p>
<p>}</p>
<p>#define ARRAYCOUNT 10000</p>
<p>int __cdecl main()</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  float __declspec(align(16)) Array[ARRAYCOUNT];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  memset( Array, 0, sizeof(float) * ARRAYCOUNT );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  CTimer t;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  double dTime;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  t.Reset();<br>
 </p>
<p> </p>
<p><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  for ( int i = 0; i &lt; 100000; i++ )</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  ScaleValue1( Array, ARRAYCOUNT, 1000.0f );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  dTime = t.End();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  cout &lt;&lt; &quot;Use SSE：&quot; &lt;&lt; dTime &lt;&lt; &quot;秒&quot; &lt;&lt; endl;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  t.Reset();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  for ( int i = 0; i &lt; 100000; i++ )</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  ScaleValue2( Array, ARRAYCOUNT, 1000.0f );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  dTime = t.End();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  cout &lt;&lt; &quot;Not Use SSE：&quot; &lt;&lt; dTime &lt;&lt; &quot;秒&quot; &lt;&lt; endl;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  system( &quot;pause&quot; );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return 0;</p>
<p>}</p>
<p>Use SSE：0.997817</p>
<p>Not Use SSE：2.84963<br>
 </p>
<p>  这里要注意一下，我使用了__declspec(align(16))做为数组定义的修释符，这表示该数组是以16字节为边界对齐的，因为SSE指令只能支持这种格式的内存数据。</p>
<p><br>
本文来自CSDN博客，转载出处：<a href="http://blog.csdn.net/liu_chulong/articles/833916.aspx">http://blog.csdn.net/liu_chulong/articles/833916.aspx</a></p> <a href="http://hi.baidu.com/learned/blog/item/d74b0a3b1cd7e4e014cecb59.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%CB%E3%B7%A8%C9%E8%BC%C6">算法设计</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/d74b0a3b1cd7e4e014cecb59.html#comment">查看评论</a>]]></description>
        <pubDate>2009年09月28日 星期一  下午 12:38</pubDate>
        <category><![CDATA[算法设计]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/d74b0a3b1cd7e4e014cecb59.html</guid>
</item>

<item>
        <title><![CDATA[使用SSE指令优化的数学函数实例]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/d9f924f5699fb42fbd310958.html]]></link>
        <description><![CDATA[
		
		<p>float _SSE_cos( float x)<br>
{<br>
 float temp;<br>
 __asm<br>
 {<br>
  movss xmm0, x<br>
  movss xmm1, _ps_am_inv_sign_mask<br>
  andps xmm0, xmm1<br>
  addss xmm0, _ps_am_pi_o_2<br>
  mulss xmm0, _ps_am_2_o_pi</p>
<p>  cvttss2si ecx, xmm0<br>
  movss xmm5, _ps_am_1<br>
  mov  edx, ecx<br>
  shl  edx, (31 - 1)<br>
  cvtsi2ss xmm1, ecx<br>
  and  edx, 0x80000000<br>
  and  ecx, 0x1</p>
<p>  subss xmm0, xmm1<br>
  movss xmm6, _sincos_masks[ecx * 4]<br>
  minss xmm0, xmm5</p>
<p>  movss xmm1, _ps_sincos_p3<br>
  subss xmm5, xmm0</p>
<p>  andps xmm5, xmm6<br>
  movss xmm7, _ps_sincos_p2<br>
  andnps xmm6, xmm0<br>
  mov  temp, edx<br>
  orps xmm5, xmm6<br>
  movss xmm0, xmm5</p>
<p>  mulss xmm5, xmm5<br>
  movss xmm4, _ps_sincos_p1<br>
  movss xmm2, xmm5<br>
  mulss xmm5, xmm1<br>
  movss xmm1, _ps_sincos_p0<br>
  addss xmm5, xmm7<br>
  mulss xmm5, xmm2<br>
  movss xmm3, temp<br>
  addss xmm5, xmm4<br>
  mulss xmm5, xmm2<br>
  orps xmm0, xmm3<br>
  addss xmm5, xmm1<br>
  mulss xmm0, xmm5<br>
  <br>
  movss&nbsp;&nbsp;  x,&nbsp;&nbsp;&nbsp;  xmm0</p>
<p> }</p>
<p> return x;<br>
}</p>
<p><br>
float _SSE2_cos(float x)  <br>
{<br>
 __asm<br>
 {<br>
  movss xmm0, x<br>
  movss xmm1, _ps_am_inv_sign_mask<br>
  movss xmm2, _ps_am_pi_o_2<br>
  movss xmm3, _ps_am_2_o_pi<br>
  andps xmm0, xmm1<br>
  addss xmm0, xmm2<br>
  mulss xmm0, xmm3</p>
<p>  pxor xmm3, xmm3<br>
  movd xmm5, _epi32_1<br>
  movss xmm4, _ps_am_1<br>
  cvttps2dq xmm2, xmm0<br>
  pand xmm5, xmm2<br>
  movd xmm1, _epi32_2<br>
  pcmpeqd xmm5, xmm3<br>
  cvtdq2ps xmm6, xmm2<br>
  pand xmm2, xmm1<br>
  pslld xmm2, (31 - 1)</p>
<p>  subss xmm0, xmm6<br>
  movss xmm3, _ps_sincos_p3<br>
  minss xmm0, xmm4<br>
  subss xmm4, xmm0<br>
  andps xmm0, xmm5<br>
  andnps xmm5, xmm4<br>
  orps xmm0, xmm5</p>
<p>  movaps xmm1, xmm0<br>
  movss xmm4, _ps_sincos_p2<br>
  mulss xmm0, xmm0<br>
  movss xmm5, _ps_sincos_p1<br>
  orps xmm1, xmm2<br>
  movaps xmm7, xmm0<br>
  mulss xmm0, xmm3<br>
  movss xmm6, _ps_sincos_p0<br>
  addss xmm0, xmm4<br>
  mulss xmm0, xmm7<br>
  addss xmm0, xmm5<br>
  mulss xmm0, xmm7<br>
  addss xmm0, xmm6<br>
  mulss xmm0, xmm1<br>
  movss&nbsp;&nbsp;  x,&nbsp;&nbsp;&nbsp;  xmm0<br>
 }</p>
<p> return x;<br>
}</p>
<p>float _SSE_Sqrt(float x)<br>
{</p>
<p> float root = 0.f;<br>
 _asm<br>
 {<br>
  sqrtss  xmm0, x<br>
  movss  root, xmm0<br>
 }</p>
<p> return root;<br>
}</p>
<p><br>
本文来自CSDN博客，转载出处：<a href="http://blog.csdn.net/liu_chulong/articles/833856.aspx">http://blog.csdn.net/liu_chulong/articles/833856.aspx</a></p> <a href="http://hi.baidu.com/learned/blog/item/d9f924f5699fb42fbd310958.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%CB%E3%B7%A8%C9%E8%BC%C6">算法设计</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/d9f924f5699fb42fbd310958.html#comment">查看评论</a>]]></description>
        <pubDate>2009年09月28日 星期一  下午 12:36</pubDate>
        <category><![CDATA[算法设计]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/d9f924f5699fb42fbd310958.html</guid>
</item>

<item>
        <title><![CDATA[x86汇编优化介绍：MMX及SSE优化--SSE篇]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/425b8ed6201fb32506088b5f.html]]></link>
        <description><![CDATA[
		
		<p>上回讲到针对整数运算的MMX优化技术，然而真正大运算量的图形和声音处理大都用的是浮点运算，而且现在对浮点运算的要求也是越来越高，在这样一个条件下INTEL终于在Pentium III处理中增加针对浮点运算优化的SSE指令，所以所有用过SSE指令的程序必须在Pentium III或者Althon XP以后的CPU上才来运行。 <br>
 SSE全新定义了8个新的128位寄存器xmm0-xmm7，比MMX的64位还提高了1倍，每个寄存器可以同时装入4个32位的浮点数，因为是全新的寄存器，所以少了MMX寄存器与原浮点寄存器的切换工作，所以有了更高的执行效率。值得注意的SSE还可以对16位和8位的整数进行运算，不过这已经不是SSE应用的主流了。</p>
<p> 在这里随便提一个Intel Compiler 8.0,这个编译器的优化能力的确是强，个人感觉大概比Visual C++ 6.0 SP6快10-20%左右，它可以针对不同的CPU作出优化，如果你是P4系列的cpu,在编译的时候加上参数/fast /QxW /Qip /Qunroll40，会有意想不到的结果，如果你再阅读一个它的用户手册，按照里面的方法稍微修改一下程序将会有更多提高，向所有崇拜极限优化的朋友推荐这款编译器。题外话少说，再次转入SSE正题，下面还是给出一个简单的例子:</p>
<p>在VC中使用内联汇编<br>
float a[]={1.0,2.0,3.0,4.0};<br>
float b[]={5.0,6.0,7.0,8.0};<br>
_asm{<br>
mov ecx,a;<br>
mov edx,b;<br>
movaps xmm0,[ecx]; <br>
movaps xmm1,[edx]; <br>
addps xmm0,xmm1; <br>
movaps [ecx],xmm0;<br>
}</p>
<p>跟MMX一样也可以不用汇编只用包含一个头文件就可以直接在C里面使用了：<br>
#include <br>
__m128 a = _mm_set_ps(1, 2, 3, 4) <br>
__m128 b = _mm_set_ps(5, 6, 7, 8) <br>
a = _mm_add_ps(a, b);<br>
这个时候就感觉使用Intrinsics方便多了，因为它制定了好多合成的伪指令，方便了不少。</p>
<p>下面顺便贴一段SSE的部分指令介绍，更全的可以找Intel下载指令手册。下面这部分是引用至：<br>
<a href="http://dwbclz.myetang.com/articles/piii/sse-ins-ref.html">http://dwbclz.myetang.com/articles/piii/sse-ins-ref.html</a></p>
<p><br>
ADDPS</p>
<p>格式：ADDPS xmm1, xmm2/m128</p>
<p>功能：两组单精度数相加</p>
<p>算法：</p>
<p>DEST[31-0] = DEST[31-0] + SRC/m128[31-0] ;<br>
DEST[63-32] = DEST[63-32] + SRC/m128[63-32] ;<br>
DEST[95-64] = DEST[95-64] + SRC/m128[95-64] ;<br>
DEST[127-96] = DEST[127-96] + SRC/m128[127-96];</p>
<p>ADDSS</p>
<p>格式：ADDSS xmm1, xmm2/m32</p>
<p>功能：低位单精度数相加</p>
<p>算法：</p>
<p>DEST[31-0] = DEST[31-0] + SRC/m32[31-0];<br>
DEST[63-32] = DEST[63-32] ;<br>
DEST[95-64] = DEST[95-64] ;<br>
DEST[127-96] = DEST[127-96];<br>
 <br>
ANDNPS</p>
<p>格式：ANDNPS xmm1, xmm2/m128</p>
<p>功能：xmm1&ldquo;取反&rdquo;再和 xmm2/m128 求&ldquo;与&rdquo;运算</p>
<p>算法：</p>
<p>DEST[127-0] = NOT (DEST[127-0]) AND SRC/m128[127-0];</p>
<p>ANDPS</p>
<p>格式：ANDPS xmm1, xmm2/m128</p>
<p>功能：进行两个寄存器的逻辑&ldquo;与&rdquo;操作</p>
<p>算法：</p>
<p>DEST[127-0] AND= SRC/m128[127-0];</p>
<p>CMPPS</p>
<p>格式：CMPPS xmm1, xmm2/m128, imm8</p>
<p>功能：比较两个寄存器的数值，根据imm8的不同数值采用不同的比较方法</p>
<p>imm8 == 0, ==; imm8 == 1, &lt;; imm8 == 2, &lt;=; imm8 == 3, ;<br>
imm8 == 4, !=; imm8 == 5, !&lt;; imm8 == 6, !&lt;=; imm8 == 7, !;</p>
<p>算法：</p>
<p>IF (imm8 = 0) THEN<br>
 OP = &quot;EQ&quot;;<br>
ELSEIF (imm8 = 1) THEN<br>
 OP = &quot;LT&quot;;<br>
ELSEIF (imm8 = 2) THEN<br>
 OP = &quot;LE&quot;;<br>
ELSEIF (imm8 = 3) THEN<br>
 OP = &quot;UNORD&quot;;<br>
ELSEIF (imm8 = 4) THEN<br>
 OP = &quot;NE&quot;;<br>
ELSEIF (imm8 = 5) THEN<br>
 OP = &quot;NLT&quot;;<br>
ELSEIF (imm8 = 6) THEN<br>
 OP = &quot;NLE&quot;;<br>
ELSEIF (imm8 = 7) THEN<br>
 OP = &quot;ORD&quot;;<br>
FI</p>
<p>CMP0 = DEST[31-0] OP SRC/m128[31-0];<br>
CMP1 = DEST[63-32] OP SRC/m128[63-32];<br>
CMP2 = DEST [95-64] OP SRC/m128[95-64];<br>
CMP3 = DEST[127-96] OP SRC/m128[127-96];</p>
<p>IF (CMP0 = TRUE) THEN<br>
 DEST[31-0] = 0XFFFFFFFF;<br>
ELSE<br>
 DEST[31-0] = 0X00000000;<br>
FI<br>
IF (CMP1 = TRUE) THEN<br>
 DEST[63-32] = 0XFFFFFFFF;<br>
ELSE<br>
 DEST[63-32] = 0X00000000;<br>
FI<br>
IF (CMP2 = TRUE) THEN<br>
 DEST[95-64] = 0XFFFFFFFF;<br>
ELSE<br>
 DEST[95-64] = 0X00000000;<br>
FI<br>
IF (CMP3 = TRUE) THEN<br>
 DEST[127-96] = 0XFFFFFFFF;<br>
ELSE<br>
 DEST[127-96] = 0X00000000;<br>
FI</p>
<p>其它：你可以使用下面的可读性良好的指令</p>
<p>指令 实现<br>
CMPEQPS xmm1, xmm2； CMPPS xmm1,xmm2, 0<br>
CMPLTPS xmm1, xmm2； CMPPS xmm1,xmm2, 1<br>
CMPLEPS xmm1, xmm2； CMPPS xmm1,xmm2, 2<br>
CMPUNORDPS xmm1, xmm2； CMPPS xmm1,xmm2, 3<br>
CMPNEQPS xmm1, xmm2； CMPPS xmm1,xmm2, 4<br>
CMPNLTPS xmm1, xmm2； CMPPS xmm1,xmm2, 5<br>
CMPNLEPS xmm1, xmm2； CMPPS xmm1,xmm2, 6<br>
CMPORDPS xmm1, xmm2； CMPPS xmm1,xmm2, 7</p>
<p>CMPSS</p>
<p>格式：CMPSS xmm1, xmm2/m32, imm8</p>
<p>功能：低位单精度数做比较</p>
<p>算法：算法同CMPPS相似，只不过只是针对DEST[31-0]进行操作。</p>
<p>同样也可以利用可读性更好的指令</p>
<p>指令 实现<br>
CMPEQSS xmm1, xmm2 CMPSS xmm1,xmm2, 0<br>
CMPLTSS xmm1, xmm2 CMPSS xmm1,xmm2, 1<br>
CMPLESS xmm1, xmm2 CMPSS xmm1,xmm2, 2<br>
CMPUNORDSS xmm1, xmm2 CMPSS xmm1,xmm2, 3<br>
CMPNEQSS xmm1, xmm2 CMPSS xmm1,xmm2, 4<br>
CMPNLTSS xmm1, xmm2 CMPSS xmm1,xmm2, 5<br>
CMPNLESS xmm1, xmm2 CMPSS xmm1,xmm2, 6<br>
CMPORDSS xmm1, xmm2 CMPSS xmm1,xmm2, 7</p>
<p>COMISS</p>
<p>格式：COMISS xmm1, xmm2/m32</p>
<p>功能：比较低位数并且设置标识位</p>
<p>算法：</p>
<p>OF = 0;<br>
SF = 0;<br>
AF = 0;<br>
IF ((DEST[31-0] UNORD SRC/m32[31-0]) = TRUE) THEN<br>
 ZF = 1;<br>
 PF = 1;<br>
 CF = 1;<br>
ELSEIF ((DEST[31-0] GTRTHAN SRC/m32[31-0]) = TRUE)THEN<br>
 ZF = 0;<br>
 PF = 0;<br>
 CF = 0;<br>
ELSEIF ((DEST[31-0] LESSTHAN SRC/m32[31-0]) = TRUE THEN<br>
 ZF = 0;<br>
 PF = 0;<br>
 CF = 1;<br>
ELSE<br>
 ZF = 1;<br>
 PF = 0;<br>
 CF = 0;<br>
FI</p>
<p>CVTPI2PS</p>
<p>格式：CVTPI2PS xmm, mm/m64</p>
<p>功能：32位整数转变为浮点数</p>
<p>算法：</p>
<p>DEST[31-0] = (float) (SRC/m64[31-0]) ;<br>
DEST[63-32] = (float) (SRC/m64[63-32]);<br>
DEST[95-64] = DEST[95-64] ;<br>
DEST[127-96] = DEST[127-96];<br>
CVTPS2PI</p>
<p>格式：CVTPS2PI mm, xmm/m64</p>
<p>功能：低位的两个浮点数转变为整数</p>
<p>算法：</p>
<p>DEST[31-0] = (int) (SRC/m64[31-0]);<br>
DEST[63-32]= (int) (SRC/m64[63-32]);</p>
<p>CVTSI2SS</p>
<p>格式：CVTSI2SS xmm, r/m32</p>
<p>功能：32位整数转变为浮点数，存入低位</p>
<p>算法：</p>
<p>DEST[31-0] = (float) (R/m32);<br>
DEST[63-32] = DEST[63-32] ;<br>
DEST[95-64] = DEST[95-64] ;<br>
DEST[127-96] = DEST[127-96];</p>
<p>CVTSS2SI</p>
<p>格式：CVTSS2SI r32, xmm/m32</p>
<p>功能：低位的浮点数转变为32位整数</p>
<p>算法：</p>
<p>r32 = (int) (SRC/m32[31-0]);</p>
<p>CVTTPS2PI</p>
<p>格式：CVTTPS2PI mm, xmm/m64</p>
<p>功能：低位的两个浮点数转变为整数，并且舍位</p>
<p>算法：</p>
<p>DEST[31-0] = (int) (SRC/m64[31-0]) ;<br>
DEST[63-32] = (int) (SRC/m64[63-32]);</p>
<p>CVTTSS2SI</p>
<p>格式：CVTTSS2SI r32, xmm/ m32</p>
<p>功能：将最低位浮点数转换为整数，并舍位。</p>
<p>算法：</p>
<p>r32 = (INT) (SRC/m32[31-0]);</p>
<p>DIVPS</p>
<p>格式：DIVPS xmm1, xmm2/m128</p>
<p>功能：单精度数除法运算</p>
<p>算法：</p>
<p>DEST[31-0] = DEST[31-0] / (SRC/m128[31-0]) ;<br>
DEST[63-32] = DEST[63-32] / (SRC/m128[63-32]) ;<br>
DEST[95-64] = DEST[95-64] / (SRC/m128[95-64]) ;<br>
DEST[127-96] = DEST[127-96] / (SRC/m128[127-96]);</p>
<p>DIVSS</p>
<p>格式：DIVSS xmm1, xmm2/m32</p>
<p>功能：低位单精度数除法</p>
<p>算法：</p>
<p>DEST[31-0] = DEST[31-0] / (SRC/m32[31-0]);<br>
DEST[63-32] = DEST[63-32] ;<br>
DEST[95-64] = DEST[95-64] ;<br>
DEST[127-96] = DEST[127-96];</p>
<p>EMMS</p>
<p>格式：EMMS</p>
<p>功能：将浮点标识字置空</p>
<p>算法：</p>
<p>FPUTagWord &lt;- FFFF</p>
<p>FXRSTOR</p>
<p>格式：FXRSTOR m512byte</p>
<p>功能：从m512byte中装入FP，MMX，以及SSE的状态</p>
<p>算法：</p>
<p>FP and MMX state and Streaming SIMD Extension state = m512byte;</p>
<p>FXSAVE</p>
<p>格式：FXSAVE m512byte</p>
<p>功能：向m512byte中存入FP，MMX，以及SSE的状态</p>
<p>算法：</p>
<p>m512byte = FP and MMX state and Streaming SIMD Extension state;</p>
<p>LDMXCSR</p>
<p>格式：LDMXCSR m32</p>
<p>功能：装入SSE的状态控制字</p>
<p>算法：</p>
<p>MXCSR = m32;</p>
<p>MAXPS</p>
<p>格式：MAXPS xmm1, xmm2/m128</p>
<p>功能：返回最大值</p>
<p>算法：</p>
<p>IF (DEST[31-0]=NaN) THEN<br>
 DEST[31-0] = SRC[31-0];<br>
ELSEIF (SRC[31-0] = NaN) THEN<br>
 DEST[31-0] = SRC[31-0];<br>
ELSEIF (DEST[31-0] &gt; SRC/m128[31-0]) THEN<br>
 DEST[31-0] = DEST[31-0];<br>
ELSE<br>
 DEST[31-0] = SRC/m128[31-0];<br>
FI<br>
IF (DEST[63-32]=NaN) THEN<br>
 DEST[63-32] = SRC[63-32];<br>
ELSEIF (SRC[63-32] = NaN) THEN<br>
 DEST[63-32] = SRC[63-32];<br>
ELSEIF (DEST[63-32] &gt; SRC/m128[63-32]) THEN<br>
 DEST[63-32] = DEST[63-32];<br>
ELSE<br>
 DEST[63-32] = SRC/m128[63-32];<br>
FI<br>
IF (DEST[95-64]=NaN) THEN<br>
 DEST[95-64] = SRC[95-64];<br>
ELSEIF (SRC[95-64] = NaN) THEN<br>
 DEST[95-64] = SRC[95-64];<br>
ELSEIF (DEST[95-64] &gt; SRC/m128[95-64]) THEN<br>
 DEST[95-64] = DEST[95-64];<br>
ELSE<br>
 DEST[95-64] = SRC/m128[95-64];<br>
FI<br>
IF (DEST[127-96]=NaN) THEN<br>
 DEST[127-96] = SRC[127-96];<br>
ELSEIF (SRC[127-96] = NaN) THEN<br>
 DEST[127-96] = SRC[127-96];<br>
ELSEIF (DEST[127-96] &gt; SRC/m128[127-96]) THEN<br>
 DEST[127-96] = DEST[127-96];<br>
ELSE<br>
 DEST[127-96] = SRC/m128[127-96];<br>
FI</p>
<p>MAXSS</p>
<p>格式：MAXSS xmm1, xmm2/m32</p>
<p>功能：返回低位最大值</p>
<p>算法：同上面类似，区别在于只对DEST[31-0]进行操作</p>
<p>MINPS</p>
<p>格式：MINPS xmm1, xmm2/m128</p>
<p>功能：返回最小值</p>
<p>算法：略</p>
<p>MINSS</p>
<p>格式：MINSS xmm1, xmm2/m32</p>
<p>功能：返回低位最小值</p>
<p>算法：略</p>
<p>MOVAPS</p>
<p>格式：MOVAPS xmm1, xmm2/m128 或 MOVAPS xmm2/m128, xmm1</p>
<p>功能：对齐的数据传输指令</p>
<p>算法：</p>
<p>IF (destination = DEST) THEN<br>
 IF (SRC = m128)THEN (* load instruction *)<br>
 DEST[127-0] = m128;<br>
 ELSE(* move instruction *)<br>
 DEST[127=0] = SRC[127-0];<br>
 FI;<br>
ELSE<br>
 IF (destination = m128)THEN (* store instruction *)<br>
 m128 = SRC[127-0];<br>
 ELSE(* move instruction *)<br>
 DEST[127-0] = SRC[127-0];<br>
 FI;<br>
FI;<br>
 <br>
MOVHLPS</p>
<p>格式：MOVHLPS xmm1, xmm2</p>
<p>功能：高位的两个数传向低位</p>
<p>算法：</p>
<p>DEST[127-64] = DEST[127-64];<br>
DEST[63-0] = SRC[127-64] ;</p>
<p>MOVHPS</p>
<p>格式：MOVHPS xmm, m64 或 MOVHPS m64, xmm</p>
<p>功能：高位数据传输指令</p>
<p>算法：</p>
<p>IF (destination = DEST) THEN(* load instruction *)<br>
 DEST[127-64] = m64;<br>
 DEST[31-0] = DEST[31-0];<br>
 DEST[63-32] = DEST[63-32];<br>
 ELSE (* store instruction *)<br>
 m64 = SRC[127-64];<br>
FI;<br>
 <br>
MOVLPS</p>
<p>格式：MOVLPS xmm, m64 或 MOVLPS m64, xmm</p>
<p>功能：低位数据传输指令</p>
<p>算法：</p>
<p>IF (destination = DEST) THEN(* load instruction *)<br>
 DEST[63-0] = m64;<br>
 DEST[95-64] = DEST[95-64];<br>
 DEST[127-96] = DEST[127-96];<br>
ELSE(* store instruction *)<br>
 m64 = DEST[63-0];<br>
FI<br>
 <br>
MOVLHPS</p>
<p>格式：MOVLHPS xmm1, xmm2</p>
<p>功能：低位的两个数传向高位</p>
<p>算法：</p>
<p>DEST[127-64] = SRC[63-0];<br>
DEST[63-0] = DEST[63-0];</p>
<p>MOVMSKPS</p>
<p>格式：MOVMSKPS r32, xmm</p>
<p>功能：掩码移入32位寄存器</p>
<p>算法：</p>
<p>r32[0] = SRC[31] ;<br>
r32[1] = SRC[63] ;<br>
r32[2] = SRC[95] ;<br>
r32[3] = SRC[127];<br>
r32[7-4] = 0X0 ;<br>
r32[15-8] = 0X00 ;<br>
r32[31-16] = 0X0000 ;</p>
<p>MOVNTPS</p>
<p>格式：MOVNTPS m128, xmm</p>
<p>功能：将数据直接存入内存，减小对缓存的压力</p>
<p>算法：</p>
<p>m128 = SRC;</p>
<p>MOVSS</p>
<p>格式：MOVSS xmm1, xmm2/m32 或 MOVSS xmm2/m32, xmm1</p>
<p>功能：最低位数据的传输指令</p>
<p>算法：</p>
<p>IF (destination = DEST) THEN<br>
 IF (SRC == m32) THEN(* load instruction *)<br>
 DEST[31-0] = m32;<br>
 DEST [63-32] = 0X00000000;<br>
 DEST [95-64] = 0X00000000;<br>
 DEST [127-96] = 0X00000000;<br>
 ELSE(* move instruction *)<br>
 DEST [31-0] = SRC[31-0];<br>
 DEST [63-32] = DEST [63-32];<br>
 DEST [95-64] = DEST [95-64];<br>
 DEST [127-96] = DEST [127-96];<br>
 FI<br>
ELSE<br>
 IF (destination = m32) THEN(* store instruction *)<br>
 m32 = SRC[31-0];<br>
 ELSE (* move instruction *)<br>
 DEST [31-0] = SRC[31-0]<br>
 DEST [63-32] = DEST[63-32];<br>
 DEST [95-64] = DEST [95-64];<br>
 DEST [127-96] = DEST [127-96];<br>
 FI<br>
FI</p>
<p>MOVUPS</p>
<p>格式：MOVUPS xmm1, xmm2/m128 或 MOVUPS xmm2/m128, xmm1</p>
<p>功能：非对齐数据的传输指令</p>
<p>算法：</p>
<p>IF (destination = xmm) THEN<br>
 IF (SRC = m128)THEN(* load instruction *)<br>
 DEST[127-0] = m128;<br>
 ELSE (* move instruction *)<br>
 DEST[127-0] = SRC[127-0];<br>
 FI<br>
ELSE<br>
 IF (destination = m128) THEN(* store instruction *)<br>
 m128 = SRC[127-0];<br>
 ELSE (* move instruction *)<br>
 DEST[127-0] = SRC[127-0];<br>
 FI<br>
FI</p>
<p>MULPS</p>
<p>格式：MULPS xmm1, xmm2/m128</p>
<p>功能：单精度数相乘</p>
<p>算法：</p>
<p>DEST[31-0] = DEST[31-0] * SRC/m128[31-0] ;<br>
DEST[63-32] = DEST[63-32] * SRC/m128[63-32] ;<br>
DEST[95-64] = DEST[95-64] * SRC/m128[95-64] ;<br>
DEST[127-96] = DEST[127-96] * SRC/m128[127-96];</p>
<p>MULSS</p>
<p>格式：MULSS xmm1, xmm2/m32</p>
<p>功能：最低位的单精度数相乘</p>
<p>算法：</p>
<p>DEST[31-0] = DEST[31-0] * SRC/m32[31-0];<br>
DEST[63-32] = DEST[63-32] ;<br>
DEST[95-64] = DEST[95-64] ;<br>
DEST[127-96] = DEST[127-96];</p>
<p>ORPS</p>
<p>格式：ORPS xmm1, xmm2/m128</p>
<p>功能：求或运算</p>
<p>算法：</p>
<p>DEST[127-0] |= SRC/m128[127-0];<br>
RCPPS</p>
<p>格式：RCPPS xmm1, xmm2/m128</p>
<p>功能：求倒数的近似值</p>
<p>算法：</p>
<p>DEST[31-0] = APPROX (1.0/(SRC/m128[31-0])) ;<br>
DEST[63-32] = APPROX (1.0/(SRC/m128[63-32])) ;<br>
DEST[95-64] = APPROX (1.0/(SRC/m128[95-64])) ;<br>
DEST[127-96] = APPROX (1.0/(SRC/m128[127-96]));</p>
<p>RCPSS</p>
<p>格式：RCPSS xmm1, xmm2/m32</p>
<p>功能：求最低位的倒数的近似值</p>
<p>算法：</p>
<p>DEST[31-0] = APPROX (1.0/(SRC/m32[31-0]));<br>
DEST[63-32] = DEST[63-32] ;<br>
DEST[95-64] = DEST[95-64] ;<br>
DEST[127-96] = DEST[127-96];</p>
<p>RSQRTPS</p>
<p>格式：RSQRTPS xmm1, xmm2/m128</p>
<p>功能：求倒数平方根的近似值</p>
<p>算法：</p>
<p>DEST[31-0] = APPROX (1.0/SQRT(SRC/m128[31-0])) ;<br>
DEST[63-32] = APPROX (1.0/SQRT(SRC/m128[63-32])) ;<br>
DEST[95-64] = APPROX (1.0/SQRT(SRC/m128[95-64])) ;<br>
DEST[127-96] = APPROX (1.0/SQRT(SRC/m128[127-96]));</p>
<p>RSQRTSS</p>
<p>格式：RSQRTSS xmm1, xmm2/m32</p>
<p>功能：求最低位倒数平方根的近似值</p>
<p>算法：</p>
<p>DEST[31-0] = APPROX (1.0/SQRT(SRC/m32[31-0]));<br>
DEST[63-32] = DEST[63-32] ;<br>
DEST[95-64] = DEST[95-64] ;<br>
DEST[127-96] = DEST[127-96];</p>
<p>SHUFPS</p>
<p>格式：SHUFPS xmm1, xmm2/m128, imm8</p>
<p>功能：打乱顺序</p>
<p>算法：</p>
<p>FP_SELECT = (imm8 &gt;&gt; 0) AND 0X3;<br>
IF (FP_SELECT = 0) THEN<br>
 DEST[31-0] = DEST[31-0];<br>
ELSEIF (FP_SELECT = 1) THEN<br>
 DEST[31-0] = DEST[63-32];<br>
ELSEIF (FP_SELECT = 2) THEN<br>
 DEST[31-0] = DEST[95-64];<br>
ELSE<br>
 DEST[31-0] = DEST[127-96];<br>
FI</p>
<p>FP_SELECT = (imm8 &gt;&gt; 2) AND 0X3;<br>
IF (FP_SELECT = 0) THEN<br>
 DEST[63-32] = DEST[31-0];<br>
ELSEIF (FP_SELECT = 1) THEN<br>
 DEST[63-32] = DEST[63-32];<br>
ELSEIF (FP_SELECT = 2) THEN<br>
 DEST[63-32] = DEST[95-64];<br>
ELSE<br>
 DEST[63-32] = DEST[127-96];<br>
FI</p>
<p>FP_SELECT = (imm8 &gt;&gt; 4) AND 0X3;<br>
IF (FP_SELECT = 0) THEN<br>
 DEST[95-64] = SRC/m128[31-0];<br>
ELSEIF (FP_SELECT = 1) THEN<br>
 DEST[95-64] = SRC/m128 [63-32];<br>
ELSEIF (FP_SELECT = 2) THEN<br>
 DEST[95-64] = SRC/m128 [95-64];<br>
ELSE<br>
 DEST[95-64] = SRC/m128 [127-96];<br>
FI</p>
<p>FP_SELECT = (imm8 &gt;&gt; 6) AND 0X3;<br>
IF (FP_SELECT = 0) THEN<br>
 DEST[127-96] = SRC/m128 [31-0];<br>
ELSEIF (FP_SELECT = 1) THEN<br>
 DEST[127-96] = SRC/m128 [63-32];<br>
ELSEIF (FP_SELECT = 2) THEN<br>
 DEST[127-96] = SRC/m128 [95-64];<br>
ELSE<br>
 DEST[127-96] = SRC/m128 [127-96];<br>
FI</p>
<p>SQRTPS</p>
<p>格式：SQRTPS xmm1, xmm2/m128</p>
<p>功能：求平方根</p>
<p>算法：</p>
<p>DEST[31-0] = SQRT (SRC/m128[31-0] );<br>
DEST[63-32] = SQRT (SRC/m128[63-32]);<br>
DEST[95-64] = SQRT (SRC/m128[95-64]);<br>
DEST[127-96] = SQRT (SRC/m128[127-96]);</p>
<p>SQRTSS</p>
<p>格式：SQRTSS xmm1, xmm2/m32</p>
<p>功能：最低位数求平方根</p>
<p>算法：</p>
<p>DEST[31-0] = SQRT (SRC/m32[31-0]);<br>
DEST[63-32] = DEST[63-32];<br>
DEST[95-64] = DEST[95-64];<br>
DEST[127-96] = DEST[127-96];</p>
<p>STMXCSR</p>
<p>格式：STMXCSR m32</p>
<p>功能：存储SSE控制字</p>
<p>算法：</p>
<p>m32 = MXCSR;</p>
<p>SUBPS</p>
<p>格式：SUBPS xmm1, xmm2/m128</p>
<p>功能：单精度数的减法运算</p>
<p>算法：</p>
<p>DEST[31-0] = DEST[31-0] - SRC/m128[31-0] ;<br>
DEST[63-32] = DEST[63-32] - SRC/m128[63-32];<br>
DEST[95-64] = DEST[95-64] - SRC/m128[95-64];<br>
DEST[127-96] = DEST[127-96] - SRC/m128[127-96];</p>
<p>SUBSS</p>
<p>格式：SUBSS xmm1, xmm2/m32</p>
<p>功能：最低位数相减</p>
<p>算法：</p>
<p>DEST[31-0] = DEST[31-0] - SRC/m32[31-0];<br>
DEST[63-32] = DEST[63-32];<br>
DEST[95-64] = DEST[95-64];<br>
DEST[127-96] = DEST[127-96];</p>
<p>UCOMISS</p>
<p>格式：UCOMISS xmm1, xmm2/m32</p>
<p>功能：比较低位数并且设置标志位</p>
<p>算法：</p>
<p>OF = 0;<br>
SF = 0;<br>
AF = 0;<br>
IF ((DEST[31-0] UNORD SRC/m32[31-0]) = TRUE) THEN<br>
 ZF = 1;<br>
 PF = 1;<br>
 CF = 1;<br>
ELSEIF ((DEST[31-0] GTRTHAN SRC/m32[31-0]) = TRUE)THEN<br>
 ZF = 0;<br>
 PF = 0;<br>
 CF = 0;<br>
ELSEIF ((DEST[31-0] LESSTHAN SRC/m32[31-0]) = TRUE THEN<br>
 ZF = 0;<br>
 PF = 0;<br>
 CF = 1;<br>
ELSE<br>
 ZF = 1;<br>
 PF = 0;<br>
 CF = 0;<br>
FI</p>
<p>UNPCKHPS</p>
<p>格式：UNPCKHPS xmm1, xmm2/m128</p>
<p>功能：高位两数交替传输</p>
<p>算法：</p>
<p>DEST[31-0] = DEST[95-64];<br>
DEST[63-32] = SRC/m128[95-64];<br>
DEST[95-64] = DEST[127-96];<br>
DEST[127-96] = SRC/m128[127-96];</p>
<p>UNPCKLPS</p>
<p>格式：UNPCKLPS xmm1, xmm2/m128</p>
<p>功能：低位两数交替传输</p>
<p>算法：</p>
<p>DEST[31-0] = DEST[31-0];<br>
DEST[63-32] = SRC/m128[31-0];<br>
DEST[95-64] = DEST[63-32];<br>
DEST[127-96] = SRC/m128[63-32];</p>
<p>XORPS</p>
<p>格式：XORPS xmm1, xmm2/m128</p>
<p>功能：异或运算</p>
<p>算法：</p>
<p>DEST[127-0] = DEST/m128[127-0] XOR SRC/m128[127-0]</p>
<p><br>
本文来自CSDN博客，转载出处：<a href="http://blog.csdn.net/liu_chulong/articles/833896.aspx">http://blog.csdn.net/liu_chulong/articles/833896.aspx</a></p> <a href="http://hi.baidu.com/learned/blog/item/425b8ed6201fb32506088b5f.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%CB%E3%B7%A8%C9%E8%BC%C6">算法设计</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/425b8ed6201fb32506088b5f.html#comment">查看评论</a>]]></description>
        <pubDate>2009年09月28日 星期一  下午 12:33</pubDate>
        <category><![CDATA[算法设计]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/425b8ed6201fb32506088b5f.html</guid>
</item>

<item>
        <title><![CDATA[AAC和AMR音频编码标准介绍 (转载)]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/7382912264974bfbd6cae216.html]]></link>
        <description><![CDATA[
		
		<p>&nbsp;&nbsp;&nbsp;  在MP3之后，3GPP又制定了一系列的语音编码(压缩)新标准──AAC和AMR家族。AAC家族包含了AAC、HE-AAC、EAAC+；其竞争对手AMR家族则包含了：AMR、AMR-WB、AMR-WB+。这些新技术足够让人眼花缭乱，但对手机芯片、制造业者和工程师而言，这是一个崭新的机会。 <br>
　　 <br>
<strong>AAC</strong></p>
<p>&nbsp;&nbsp;&nbsp;  AAC(Advanced Audio Coding)也称为MPEG-2 AAC，是一种数据会遗失(lossy)的语音串流压缩标准。AAC是MPEG-2的一部份，是用来取代MP3的，但是AAC和MP3不同，AAC无法向后兼容，MP3可以，例如：MP3可以和MP2兼容。</p>
<p>&nbsp;&nbsp;&nbsp;  AAC最多可以支持48个频道，取样率从8 kHz到96 kHz。AAC的语音分辨率(resolution)比MP3高，一般而言，96 kbps的AAC之语音品质优于或等同于128 kbps的MP3，因此，AAC可以在低速的网络内传输语音串流，而且，不会影响语音的品质。 <br>
　　 <br>
<strong>HE-AAC(AAC+)</strong></p>
<p>&nbsp;&nbsp;&nbsp;  HE(high efficiency) AAC又称为MPEG-4 HE-AAC或简称AAC+，它是MPEG-2 AAC和SBR(Spectral Band Replication)带宽延伸修订版的技术组合。HE-AAC不是要取代AAC，而是要延伸MPEG-4的语音品质，能够以更低的速率传输(32 kbps)。而且，HE-AAC译码器可以对AAC解碼。</p>
<p>&nbsp;&nbsp;&nbsp;  若要产生48 kbps的HE-AAC立体声，HE-AAC编码器会产生两种信号：一个是42 kbps的MPEG AAC信号，另一个是6 kbps的SBR信号。然后，此SBR信号置于MPEG AAC的辅助字段内(该字段是MPEG-4定义的)。最后，构成一个完整48 kbps的MPEG-4 HE-AAC串流。SBR代表高频的成份，而AAC代表低频的成份。HE-AAC译码器使用AAC和SBR信号，产生全频信号；而AAC译码器只使用AAC信号，亦即，只有低频成份被它解碼。</p>
<p>&nbsp;&nbsp;&nbsp;  HE-AAC能传输48 kbps的CD立体声，或128 kbps、5.1声道的&ldquo;环场声&rdquo;(surround sound)。这样的效率，使它适用于Internet传输，或移动数字广播。不过，由于HE-AAC的高延迟特性，使它不适用于双向的通信应用。 <br>
　　 <br>
<strong>EAAC+</strong></p>
<p>&nbsp;&nbsp;&nbsp;  Enhanced AAC+(EAAC+)是在2004年时，纳入3GPP的第6版标准中。根据3GPP，它是由MPEG-4 AAC、MPEG-4 SBR和MPEG-4&ldquo;参数立体声&rdquo;(Parametric Stereo)技术组合的。&ldquo;参数立体声&rdquo;技术能够在低传输率中，进行&ldquo;立体声&rdquo;的编码，其基本原理类似SBR。 <br>
　　 <br>
<strong>AMR</strong></p>
<p>&nbsp;&nbsp;&nbsp;  AMR(Adaptive Multi-Rate)标准是在1998年被提出。它的主要功能是提供移动装置使用的基本语音(baseline speech)。它以可变速率的非立体声(mono)传输，速率在4.75 kbps～12.2 kbps之间，它属于窄频，带宽只有3.5 kHz。它被3GPP当成3G无线电网络系统的基本编译码技术；3G是从GSM、WCDMA、EDGE、GPRS演变而来的，而且，不管是2G、2.5G或3G，AMR都是这些无线电网络标准的最基本编译码技术。</p>
<p>&nbsp;&nbsp;&nbsp;  AMR的基本原理是：当通信干扰增加时，就降低编译码速率，而且还能实现更多的校错(error correction)功能。AMR也可以让不同手机系统的编译码技术之间能够尽量兼容，这是靠ACELP(Algebraic Code Excited Linear Prediction)技术达到的。ACELP是一种语音压缩系统，它可以在低速的网络环境中，提供高品质的语音。 <br>
　　 <br>
<strong>AMR-WB</strong></p>
<p>&nbsp;&nbsp;&nbsp;  AMR-WB(wideband extension)是AMR的升级版，它也是使用ACELP技术。2000年12月时，ETSI/3GPP将AMR-WB标准化，并公布于世。ITU-T在2002年采用它，并另命名为G.722.2。</p>
<p>&nbsp;&nbsp;&nbsp;  由于AMR-WB的语音带宽很宽(50 Hz～7 kHz)，所以它的语音品质很高。它具有9种采样速率(都是非立体声)，分别是：23.85 kbps、23.05 kbps、19.85 kbps、18.25 kbps、15.85 kbps、14.25 kbps、12.65 kbps、8.85 kbps、6.6 kbps。其中，能够保持高的语音品质，并且速率最低者是12.65 kbps。AMR-WB已经被UMTS/IMT-2000无线电网络采用，作为它的编译码基本技术；UMTS也是一种3G新标准。 <br>
　　 <br>
<strong>AMR-WB+</strong></p>
<p>&nbsp;&nbsp;&nbsp;  2004年9月，ETSI/3GPP将AMR-WB+标准化。AMR-WB+是AMR-WB的升级版，它使用ACELP和TCX(Transform Coded Excitation)技术，提供高品质的语音和其它音频内容──这包括：自然声、数字音乐、与音乐相混合的声音(voice-between-music/voice-over-music)。</p>
<p>&nbsp;&nbsp;&nbsp;  AMR-WB+增加了立体声信号和支持更高的采样速率。并且，使用高效率的&ldquo;参数立体声&rdquo;(HE-PS)技术，能够以低速率传输高品质的立体声。TCX转换编码技术则补偿了ACELP的不足。</p>
<p>&nbsp;&nbsp;&nbsp;  AMR-WB+的采样速率是从6 kbps～48 kbps；立体声的采样速率是8 kbps～48 kbps，非立体声的采样速率是6 kbps～36 kbps。这使得它的语音带宽更宽(24 kHz)，接近CD的语音品质。此外，AMR-WB+可以和AMR-WB兼容。 <br>
　　 <br>
<strong>技术比较</strong></p>
<p>&nbsp;&nbsp;&nbsp;  根据欧洲广播联盟(EBU)的人工测试(其方法称为MUSHRA)，除AMR家族尚未测试以外，编译码后的语音品质最好者是AAC+，在采样率为48 kbps时，它的品质与CD一样好。其它技术的语音品质，按优劣顺序分别是：MP3PRO、AAC、Real 8、7 kHz LPF、WMA 8、MP3、Real G2、3.5 kHz LPF。 <br>
按照3GPP的分类，依传输率大小，可区分成两类：</p>
<p>&nbsp;&nbsp;&nbsp;  1. 低于或等于24 kbps者：ARM-WB+、HE-AAC/AAC+、EAAC+。 <br>
&nbsp;&nbsp;&nbsp;  2. 高于24 kbps者：HE-AAC/AAC+、EAAC+。</p>
<p>&nbsp;&nbsp;&nbsp;  根据3GPP的MUSHRA测试结果，ARM-WB+在采样率为48 kbps时的语音品质最高。若以ARM-WB+与EAAC+做比较，在采样率低于24 kbps时，ARM-WB+的立体声优于EAAC+。 <br>
　　 <br>
<strong>结语</strong></p>
<p>&nbsp;&nbsp;&nbsp;  目前，AAC和AMR-WB技术已经被使用于2G和2.5手机中。而AAC+、EAAC+和AMR-WB+则被使用于3G手机中。它们的应用如图1所示。音乐手机比视频手机、电视手机更早被市场接受。但是，MP3播放机的价格却一直下滑，所以，音乐手机或音乐播放机极需要AAC+、EAAC+和AMR-WB+的兴起和普及，以带动另一波的购买热潮。</p>
<p>&nbsp;&nbsp;&nbsp;  更好的语音编译码技术会继续推陈换新，但到2010－2015年，当手机的传输率可达到1 Gbps时，追求效率更佳的编解码技术的趋势可能会逐渐消退，不过，编解码技术的内涵仍然是很迷人的。</p> <a href="http://hi.baidu.com/learned/blog/item/7382912264974bfbd6cae216.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%D2%F4%CA%D3%C6%B5%BC%BC%CA%F5">音视频技术</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/7382912264974bfbd6cae216.html#comment">查看评论</a>]]></description>
        <pubDate>2009年03月29日 星期日  下午 08:51</pubDate>
        <category><![CDATA[音视频技术]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/7382912264974bfbd6cae216.html</guid>
</item>

<item>
        <title><![CDATA[c/c++软件开发的注意事项]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/dad1283fc8fa57e455e723a9.html]]></link>
        <description><![CDATA[
		
		<p><font color="#ff0000">这些天闲着没事，总结了一些c/c++编程的一些经验，提醒自己，温故而知新。</font></p>
<p><font style="font-size: 18px;"><strong><font color="#0000ff">第一部分软件编程的时间分配概况</font></strong></font></p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 高效率的程序员并不是敲键盘的速度比别人快，而是他有着良好的编程习惯，节省了别人浪费的时间。因此，要想提高自己的编程效率，根本在于怎么少浪费时间。只要能把别人浪费的时间节省下来，你的效率就可以快过别人，甚至数倍于别人。要想节省时间，当然首先需要明白编程中耗费时间的分配情况。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 程序员软件开发的几个阶段：</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr>1、分析设计，</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 分析设计的前提是充分理解需求说明文档，然后分析如何实现它们，并形成设计文档。分析设计的目的是明晰软件架构，软件算法和功能的逻辑，得出必要的设计文档，奠定后面编码的基础。合理的设计可以节省后面编码的时间，不合理甚至错误的设计将导致工作的重新开始，没有设计贸然开始编程会导致一些难以预见的逻辑错误。总之，较好的设计事半功倍。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 2、程序编码</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 依照语法、设计文档编码。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 3、程序测试</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 测试的目的是检测程序有没有问题，一旦发现问题，问题的定位越准，效率就越高。因此，程序中需要输出必要的提示信息。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 4、软件调试</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 测试时发现程序有BUG，自然需要调试。显然，遇到的问题越少，调试的时间就越少。没有什么比一次性写好，不用调试更快的方法了。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 时间虽然无法具体分配到各个阶段，但是第一和第二阶段的质量决定了后面两阶段的时间花费，质量越高，测试和调试时间花费越少。反过来，质量越差，测试和调试时间越长。</p>
<p><font color="#0000ff" style="font-size: 18px;"><strong>第二部分 c/c++中常见的错误</strong></font></p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 软件编程的过程中，程序员所犯的并不一定是重大错误，反而一些常见的错误屡见不鲜。这些错误严重影响到编程中测试和调试的时间。这一部分总结一下，时时提醒自己，告诫自己避免这些错误。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 常见的错误有：</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 1、内存泄露</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 在c/c++中，内存管理器不会帮助你自动回收不再使用的内存，不管在什么情况下，采取谨慎的态度，杜绝内存泄露的出现，都是上策。尽管一些工具可以帮助我们检查内存泄露问题，但是编程时还是应该仔细一点，尽早排除这类错误，工具只是用作验证的手段。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 2、内存越界访问</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 1）读越界，即读了不属于自己的数据，如果所读的内存地址是无效的，程度立刻就崩溃了。如果所读内存地址是有效的，虽然读的时候不会出问题，但由于读到的数据是随机的，它会产生不可预料的后果。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 2）写越界，也叫缓冲区溢出，所写入的数据对别人来说是随机的，它也会产生不可预料的后果。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 内存越界访问造成的后果非常严重，是程序稳定性的致命威胁之一。更麻烦的是，它造成的后果是随机的，表现出来的症状和时机也是随机的，让BUG的现象和本质看似没有什么联系，这给BUG的定位带来极大的困难。一些工具可以够帮助检查内存越界访问的问题，但也不能太依赖于工具。内存越界访问通常是动态出现的，即依赖于测试数据，在极端的情况下才会出现，除非精心设计测试数据，工具也无能为力。工具本身也有一些限制，甚至在一些大型项目中，工具变得完全不可用。比较保险的方法还是在编程是就小心，特别是对于外部传入的参数要仔细检查。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> c/c++中，数组中的越界问题尤其常见。例如：</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> ---------------------------------------------------------------------------------------</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> #include &lt;stdio.h&gt;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> #include &lt;stdlib.h&gt;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> #include &lt;string.h&gt;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int main(int argc, char* argv[])<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> char str[10];<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> int array[10] = {0,1,2,3,4,5,6,7,8,9};</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> int data = array[10];// <font color="#ff0000">read error: array[10]<br>
</font> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> array[10] = data;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> if(argc == 2)<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> strcpy(str, argv[1]);// <font color="#ff0000">write error: argv[1]的字符串可能字符个数超过10<br>
</font> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr>}</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return 0;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> ----------------------------------------------------------------------------------------</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 3、野指针</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 野指针是指已经释放了的内存指针。当调用free（p）；p本身没有变化，它指向的内存仍然是有效的。释放掉的内存会被内存管理器重新分配，此时，野指针指向的内存已经被赋予新的意义。对野指针指向内存的访问，无论是有意还是无意的，都为此会付出巨大代价，因为它造成的后果，如同越界访问一样是不可预料的。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 较好的对策是：释放内存后立即把对应指针置为空值，这是避免野指针常用的方法。这个方法简单有效，只是要注意，当然指针是从函数外层传入的时，在函数内把指针置为空值，对外层的指针没有影响。比如，你在析构函数里把this指针置为空值，没有任何效果，这时应该在函数外层把指针置为空值。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 4、访问空指针</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 空指针在C/C++中占有特殊的地址，通常用来判断一个指针的有效性。空指针一般定义为0。现代操作系统都会保留从0开始的一块内存，至于这块内存有多大，视不同的操作系统而定。一旦程序试图访问这块内存，系统就会触发一个异常/信号。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 操作系统为什么要保留一块内存，而不是仅仅保留一个字节的内存呢？原因是：一般内存管理都是按页进行管理的，无法单纯保留一个字节，至少要保留一个页面。保留一块内存也有额外的好处，可以检查诸如p==NULL; p[1]之类的内存错误。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 在一些嵌入式系统(如arm7)中，从0开始的一块内存是用来安装中断向量的，没有MMU的保护，直接访问这块内存好像不会引发异常。不过这块内存是代码段的，不是程序中有效的变量地址，所以用空指针来判断指针的有效性仍然可行。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 5、引用未初始化的变量</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 未初始化变量的内容是随机的(有的编译器会在调试版本中把它们初始化为固定值，如0xcc)，使用这些数据会造成不可预料的后果，调试这样的BUG也是非常困难的。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 较好的习惯是：在声明变量时就对它进行初始化。另外也要重视编译器的警告信息，发现有引用未初始化的变量，立即修改过来。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 6、指针运算</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 依照指针类型进行指针运算，指针偏移值。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 7、结构的成员顺序变化引起的错误</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 如：</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> Struct s<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> int <wbr></wbr> <wbr></wbr> l;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> char* p;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> };</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int main(int argc, char* argv[])<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> struct s s1 = {4, &quot;abcd&quot;};</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return 0;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 以上这种方式是非常危险的，原因在于你对结构的内存布局作了假设。如果这个结构是第三方提供的，他很可能调整结构中成员的相对位置。而这样的调整往往不会在文档中说明，你自然很少去关注。如果调整的两个成员具有相同数据类型，编译时不会有任何警告，而程序的逻辑可能相距十万八千里了。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 正确的初始化方法应该是（当然，一个成员一个成员的初始化也行）：</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> struct s<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> int <wbr></wbr> <wbr></wbr> l;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> char* p;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> };</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int main(int argc, char* argv[])<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> struct s s1 = {.l=4, .p = &quot;abcd&quot;};</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return 0;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 8、结构的大小变化引发的错误</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 我们看看下面这个例子：</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> struct base<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr>  <wbr></wbr>int n;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> };</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> struct s<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> struct base b;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> int m;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> };</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 在OOP中，我们可以认为第二个结构继承了第一结构，这有什么问题吗？当然没有，这是C语言中实现继承的基本手法。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 现在假设第一个结构是第三方提供的，第二个结构是你自己的。第三方提供的库是以DLL方式分发的，DLL最大好处在于可以独立替换。但随着软件的进化，问题可能就来了。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 当第三方在第一个结构中增加了一个新的成员int k;，编译好后把DLL给你，你直接把它给了客户了，让他们替换掉老版本。程序加载时不会有任何问题，在运行逻辑可能完全改变！原因是两个结构的内存布局重叠了。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 解决这类错误的唯一办法就是重新编译全部代码。由此看来，动态库并不见得可以动态替换，如果你想了解更多相关内容，建议你阅读《COM本质论》。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 9、分配/释放不配对</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 大家都知道malloc要和free配对使用，new要和delete/delete[]配对使用，重载了类new操作，应该同时重载类的delete/delete[]操作。这些都是书上反复强调过的，除非当时晕了头，一般不会犯这样的低级错误。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 而有时候我们却被蒙在鼓里，两个代码看起来都是调用的free函数，实际上却调用了不同的实现。比如在Win32下，调试版与发布版，单线程与多线程是不同的运行时库，不同的运行时库使用的是不同的内存管理器。一不小心链接错了库，那你就麻烦了。程序可能动则崩溃，原因在于在一个内存管理器中分配的内存，在另外一个内存管理器中释放时就会出现问题。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 10、返回指向临时变量的指针</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 大家都知道，栈里面的变量都是临时的。当前函数执行完成时，相关的临时变量和参数都被清除了。不能把指向这些临时变量的指针返回给调用者，这样的指针指向的数据是随机的，会给程序造成不可预料的后果。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 下面是个错误的例子：</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> char* get_str(void)<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> char str[] = {&quot;abcd&quot;};</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return str;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int main(int argc, char* argv[])<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> char* p = get_str();</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> printf(&quot;%s\n&quot;, p);</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return 0;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 下面这个例子没有问题，大家知道为什么吗？</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> char* get_str(void)<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> char* str = {&quot;abcd&quot;};</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return str;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int main(int argc, char* argv[])<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> char* p = get_str();</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> printf(&quot;%s\n&quot;, p);</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return 0;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 11、试图修改常量</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 在函数参数前加上const修饰符，只是给编译器做类型检查用的，编译器禁止修改这样的变量。但这并不是强制的，你完全可以用强制类型转换绕过去，一般也不会出什么错。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 而全局常量和字符串，用强制类型转换绕过去，运行时仍然会出错。原因在于它们是放在.rodata里面的，而.rodata内存页面是不能修改的。试图对它们修改，会引发内存错误。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 下面这个程序在运行时会出错：</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> int main(int argc, char* argv[])<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr>  <wbr></wbr> <wbr></wbr> <wbr></wbr> char* p = &quot;abcd&quot;;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr>  <wbr></wbr>*p = '1';</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return 0;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 12、误解传值与传引用(函数参数单向传值问题）</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 在C/C++中，参数默认传递方式是传值的，即在参数入栈时被拷贝一份。在函数里修改这些参数，不会影响外面的调用者。如：</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> #include &lt;stdlib.h&gt;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> #include &lt;stdio.h&gt;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> void get_str(char* p)<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr>  <wbr></wbr>p = malloc(sizeof(&quot;abcd&quot;));</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> strcpy(p, &quot;abcd&quot;);</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int main(int argc, char* argv[])<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr>  <wbr></wbr>char* p = NULL;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> get_str(p);</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> printf(&quot;p=%p\n&quot;, p);</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr>  <wbr></wbr> <wbr></wbr> <wbr></wbr> return 0;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 在main函数里，p的值仍然是空值。当然在函数里修改指针指向的内容是可以的。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 13、重名符号(变量的作用域问题）</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 无论是函数名还是变量名，如果在不同的作用范围内重名，自然没有问题。但如果两个符号的作用域有交集，如全局变量和局部变量，全局变量与全局变量之间，重名的现象一定要坚决避免。gcc有一些隐式规则来决定处理同名变量的方式，编译时可能没有任何警告和错误，但结果通常并非你所期望的。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 下面例子编译时就没有警告：</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> t.c</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> #include &lt;stdlib.h&gt;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> #include &lt;stdio.h&gt;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int count = 0;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int get_count(void)</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return count;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr></p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> main.c</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> #include &lt;stdio.h&gt;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> extern int get_count(void);</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int count;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int main(int argc, char* argv[])<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> count = 10;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> printf(&quot;get_count=%d\n&quot;, get_count());</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return 0;</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------<br>
<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 如果把main.c中的int count;修改为int count = 0;，gcc就会编辑出错，说multiple definition of `count&rsquo;。它的隐式规则比较奇妙吧，所以还是不要依赖它为好。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 14、栈溢出</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 我们在前面关于堆栈的一节讲过，在PC上，普通线程的栈空间也有十几M，通常够用了，定义大一点的临时变量不会有什么问题。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 而在一些嵌入式中，线程的栈空间可能只5K大小，甚至小到只有256个字节。在这样的平台中，栈溢出是最常用的错误之一。在编程时应该清楚自己平台的限制，避免栈溢出的可能。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 15、误用sizeof。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 尽管C/C++通常是按值传递参数，而数组则是例外，在传递数组参数时，数组退化为指针（即按引用传递），用sizeof是无法取得数组的大小的。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 从下面这个例子可以看出：</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> void test(char str[20])<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr>  <wbr></wbr> <wbr></wbr> printf(&quot;%s:size=%d\n&quot;, __func__, sizeof(str));<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> } <wbr></wbr></p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> int main(int argc, char* argv[])<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> char str[20] <wbr></wbr> = {0};</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> test(str);</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> printf(&quot;%s:size=%d\n&quot;, __func__, sizeof(str));</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr> return 0;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> [root@localhost mm]# ./t.exe<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> test:size=4<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> main:size=20</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> -------------------------------------------------------------------------------------</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr>&nbsp;&nbsp;&nbsp;&nbsp;  16、字节对齐</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 字节对齐主要目的是提高内存访问的效率。但在有的平台(如arm7)上，就不光是效率问题了，如果不对齐，得到的数据是错误的。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 所幸的是，大多数情况下，编译会保证全局变量和临时变量按正确的方式对齐。内存管理器会保证动态内存按正确的方式对齐。要注意的是，在不同类型的变量之间转换时要小心，如把char*强制转换为int*时，要格外小心。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 另外，字节对齐也会造成结构大小的变化，在程序内部用sizeof来取得结构的大小，这就足够了。若数据要在不同的机器间传递时，在通信协议中要规定对齐的方式，避免对齐方式不一致引发的问题。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 17、大小端问题（字节顺序）</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 字节顺序历来是设计跨平台软件时头疼的问题。字节顺序是关于数据在物理内存中的布局的问题，最常见的字节顺序有两种：大端模式与小端模式。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 大端模式是高位字节数据存放在低地址处，低位字节数据存放在高地址处。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 小端模式指低位字节数据存放在内存低地址处，高位字节数据存放在内存高地址处；</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 在普通软件中，字节顺序问题并不引人注目。而在开发与网络通信和数据交换有关的软件时，字节顺序问题就要特殊注意了。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 18、多线程共享变量没有用valotile修饰</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 关键字valotile的作用是告诉编译器，不要把变量优化到寄存器里。在开发多线程并发的软件时，如果这些线程共享一些全局变量，这些全局变量最好用valotile修饰。这样可以避免因为编译器优化而引起的错误，这样的错误非常难查。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 19、忘记函数的返回值</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 函数需要返回值，如果你忘记return语句，它仍然会返回一个值，因为在i386上，EAX用来保存返回值，如果没有明确返回，EAX最后的内容被返回，所以EAX的内容是随机的。</p>
<p><font color="#0000ff" style="font-size: 18px;"><strong>第三部分编写程序和检查程序的良好习惯</strong></font></p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 第一阶段分析设计</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 充分理解需求，设计软件框架时数据流，控制流尽可能完整；形成尽可能详尽的流程图；涉及到算法类的设计时，要将涉及到的数学公式或思想深入理解，如果需要采用快速算法的，可以参考经典的算法。参考网上的几篇博客和《高质量》</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 第二阶段编写代码的习惯</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 按照设计文档编，遵照良好的规范书写代码（文献【1】），时时刻刻牢记上面提到的二十种常见错误，争取第一次书写时就避免这样的错误。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 程序完成后，不急着编译，而是先仔细阅读：<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr></p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 1、第一遍阅读：重点关注语法错误、代码排版和命名规则等等问题，只要看不顺眼就修改它们。读完之后，你的代码很少有低级错误，看起来也比较干净清爽。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 2、第二遍阅读：重点关注第二部分的常见编程错误。</p>
<p>&nbsp;&nbsp;<wbr></wbr> <wbr></wbr> <wbr></wbr> 3、模拟计算机执行：常见错误是比较死的东西，按照检查列表一条一条的做就行了。有些逻辑通常不是这么直观的，这时可以自己模拟计算机去执行，假想你自己是计算机，读入这些代码时你会怎么处理。这种方法能有效的完善我们的思路，考虑不同的输入数据，各种边界值，这能帮助我们想到一些没有处理的情况，让程序的逻辑更严谨。<br>
<wbr></wbr></p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> <wbr></wbr>至于读几遍合适，要根据情况而定，通常三遍是最佳的投资。</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> 其他几项良好习惯：<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 1. 一组良好的接口，实现构造和析构函数。<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 2. 隐藏内部细节：内部函数尽量使用static声明，最好结构声明都不要放在头文件里，除非确实需要提供给外部。<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 3. 良好的编程风格：排版，布局，注释，命名需要规范。<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 4. 尽量满足同时供c/c++调用，条件声明成 extern&ldquo;c&rdquo;<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> ---------------------------------------------------------------</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> #if defined(__cplusplus)<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> extern &quot;C&quot; {<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> #endif</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> #if defined(__cplusplus)<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> }<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> #endif</p>
<p><wbr></wbr> <wbr></wbr> <wbr></wbr> ---------------------------------------------------------------<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 5. 需要带有自动测试程序，Demo程序。<br>
<wbr></wbr> <wbr></wbr> <wbr></wbr> 6. 加上#ifndef/#define/#endif：防止重复include相同的头文件</p>
<p><wbr></wbr></p>
<p>===================================================================================</p>
<p>参考文献：</p>
<p>【1】林锐 <wbr></wbr>《高质量C/C++编程指南》</p>
<p>【2】<a href="http://www.limodev.cn/blog"><font color="#7c3f11">http://www.limodev.cn/blog</font></a></p> <a href="http://hi.baidu.com/learned/blog/item/dad1283fc8fa57e455e723a9.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/c%26%2347%3Bc%2B%2B%20asm">c&#47;c++ asm</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/dad1283fc8fa57e455e723a9.html#comment">查看评论</a>]]></description>
        <pubDate>2009年03月18日 星期三  下午 09:33</pubDate>
        <category><![CDATA[c&#47;c++ asm]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/dad1283fc8fa57e455e723a9.html</guid>
</item>

<item>
        <title><![CDATA[小析C++this指针]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/15d37a8d5596101ab21bbadf.html]]></link>
        <description><![CDATA[
		
		<h1>C++this指针</h1>
<div >
<div class="bpctrl"> </div>
　　<br>
<div class="spctrl"> </div>
　　1. this指针的用处: <br>
<div class="spctrl"> </div>
　　一个对象的this指针并不是对象本身的一部分，不会影响sizeof(对象)的结果。this作用域是在类内部，当在类的非静态成员函数中访问类的非静态成员的时候，编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说，即使你没有写上this指针，编译器在编译的时候也是加上this的，它作为非静态成员函数的隐含形参，对各成员的访问均通过this进行。 <br>
<div class="spctrl"> </div>
　　例如，调用date.SetMonth(9) &lt;===&gt; SetMonth(&amp;date, 9)，this帮助完成了这一转换 . <br>
<div class="spctrl"> </div>
　　2. this指针的使用: <br>
<div class="spctrl"> </div>
　　一种情况就是，在类的非静态成员函数中返回类对象本身的时候，直接使用 return *this；另外一种情况是当参数与成员变量名相同时，如this-&gt;n = n （不能写成n = n）。 <br>
<div class="spctrl"> </div>
　　3. this指针程序示例: <br>
<div class="spctrl"> </div>
　　this指针是存在与类的成员函数中,指向被调用函数所在的类实例的地址。 <br>
<div class="spctrl"> </div>
　　根据以下程序来说明this指针 <br>
<div class="spctrl"> </div>
　　#include&lt;iostream.h&gt; <br>
<div class="spctrl"> </div>
　　class Point <br>
<div class="spctrl"> </div>
　　{ <br>
<div class="spctrl"> </div>
　　int x, y; <br>
<div class="spctrl"> </div>
　　public: <br>
<div class="spctrl"> </div>
　　Point(int a, int b) { x=a; y=b;} <br>
<div class="spctrl"> </div>
　　Void MovePoint( int a, int b){ x+=a; y+=b;} <br>
<div class="spctrl"> </div>
　　Void print(){ cout&lt;&lt;&quot;x=&quot;&lt;&lt;x&lt;&lt;&quot;y=&quot;&lt;&lt;y&lt;&lt;endl;} <br>
<div class="spctrl"> </div>
　　}; <br>
<div class="spctrl"> </div>
　　void main( ) <br>
<div class="spctrl"> </div>
　　{ <br>
<div class="spctrl"> </div>
　　Point point1( 10,10); <br>
<div class="spctrl"> </div>
　　point1.MovePoint(2,2); <br>
<div class="spctrl"> </div>
　　point1.print( ); <br>
<div class="spctrl"> </div>
　　} <br>
<div class="spctrl"> </div>
　　当对象point1调用MovePoint(2,2)函数时，即将point1对象的地址传递给了this指针。 <br>
<div class="spctrl"> </div>
　　MovePoint函数的原型应该是 void MovePoint( Point *this, int a, int b);第一个参数是指向该类对象的一个指针，我们在定义成员函数时没看见是因为这个参数在类中是隐含的。这样point1的地址传递给了this，所以在MovePoint函数中便显式的写成： <br>
<div class="spctrl"> </div>
　　void MovePoint(int a, int b) { this-&gt;x +=a; this-&gt; y+= b;} <br>
<div class="spctrl"> </div>
　　即可以知道，point1调用该函数后，也就是point1的数据成员被调用并更新了值。 <br>
<div class="spctrl"> </div>
　　即该函数过程可写成 point1.x+= a; point1. y + = b; <br>
<div class="spctrl"> </div>
　　4. 关于this指针的一个精典回答: <br>
<div class="spctrl"> </div>
　　当你进入一个房子后， <br>
<div class="spctrl"> </div>
　　你可以看见桌子、椅子、地板等， <br>
<div class="spctrl"> </div>
　　但是房子你是看不到全貌了。 <br>
<div class="spctrl"> </div>
　　对于一个类的实例来说， <br>
<div class="spctrl"> </div>
　　你可以看到它的成员函数、成员变量， <br>
<div class="spctrl"> </div>
　　但是实例本身呢？ <br>
<div class="spctrl"> </div>
　　this是一个指针，它时时刻刻指向你这个实例本身</div> <a href="http://hi.baidu.com/learned/blog/item/15d37a8d5596101ab21bbadf.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/c%26%2347%3Bc%2B%2B%20asm">c&#47;c++ asm</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/15d37a8d5596101ab21bbadf.html#comment">查看评论</a>]]></description>
        <pubDate>2009年03月17日 星期二  下午 03:47</pubDate>
        <category><![CDATA[c&#47;c++ asm]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/15d37a8d5596101ab21bbadf.html</guid>
</item>

<item>
        <title><![CDATA[Fedora 10硬盘安装备忘录]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/575f8554074bef173b29351a.html]]></link>
        <description><![CDATA[
		
		<p>通过硬盘安装Fedora的详尽步骤小记（避免下次安装浪费时间）</p>
<p>1. 通过 <a href="http://fedoraproject.org/"><font color="#0000ff">http://fedoraproject.org/</font></a> <wbr></wbr>下载文件 Fedora-10-i386-DVD.iso 放在fat32格式 D盘根目录。</p>
<p><wbr></wbr><wbr></wbr>&nbsp;&nbsp;&nbsp;  通过 <a href="http://download.gna.org/grub4dos/"><font color="#0000ff">http://download.gna.org/grub4dos/</font></a> 下载好引导程序 grub4dos</p>
<p>2. 解压grub4dos, 拷贝 grldr, grub.exe, menu.lst三个文件放在c盘根目录下。</p>
<p>3. 在Fedora-10-i386-DVD.iso中提取（不需要全部解压）文件夹</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr>1）isolinux 将目录中的文件vmlinuz, initrd.img拷贝到c盘根目录</p>
<p><wbr></wbr><wbr></wbr>2) images <wbr></wbr><wbr></wbr><wbr></wbr>将目录放到D盘根目录（注意和文件Fedora-10-i386-DVD.iso要在同一级目录，避免安装过程中找不到ISO文件） <wbr></wbr></p>
<p>4. 编辑c盘根目录的文件 menu.lst，在文件尾加上</p>
<p><wbr></wbr><wbr></wbr>title Install Fedora 10</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr>kernel (hd0,0)/vmlinuz</p>
<p><wbr></wbr><wbr></wbr>initrd (hd0,0)/initrd.img</p>
<p><wbr></wbr><wbr></wbr>编辑系统隐藏文件boot.ini,在文件尾加上：</p>
<p><wbr></wbr><wbr></wbr>c:\grldr=GRUB <wbr></wbr></p>
<p>5. 重启，选择GRUB，再选择Install Fedora 10，只要前面的步骤正确，即可进入安装界面。 <wbr></wbr></p>
<p>6. 安装过程中的注意事项：选择硬盘安装，/dev/sd5(笔者电脑分区，可能因人而异）（D盘） <wbr></wbr></p>
<p>7. 安装完毕后的几个重要配置：</p>
<p><wbr></wbr><wbr></wbr>1）电信ADSL上网配置</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr>系统-&gt;管理-&gt;网络-&gt;新建-&gt;XDSL连接设置</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr>登录名：<a href="mailto:*****@163.gd"><font color="#447077">*****@163.gd</font></a></p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr>口令：**********</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr>设置好后，激活eth0 和 ppp0(默认) <wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr></p>
<p><wbr></wbr><wbr></wbr>2）添加yum源和update系统</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr>添加常用的yum源</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr>$ su</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr># rpm -Uvh <a href="http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-stable.noarch.rpm"><font color="#447077">http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-stable.noarch.rpm</font></a></p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr># rpm -Uvh <a href="http://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-stable.noarch.rpm"><font color="#447077">http://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-stable.noarch.rpm</font></a></p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr># rpm -Uvh <a href="http://livna-dl.reloumirrors.net/fedora/10/i386/livna-release-9-2.noarch.rpm"><font color="#447077">http://livna-dl.reloumirrors.net/fedora/10/i386/livna-release-9-2.noarch.rpm<br>
</font></a><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr># yum update</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr>更新的时间一般较长（视下载速度决定） <wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr></p>
<p><wbr></wbr><wbr></wbr>3）安装显卡驱动（笔者是Geforce4 MX440)</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr>#yum install kmod-nvidia-96xx <wbr></wbr></p>
<p>4)关闭SELinux<br>
<wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr>系统-&gt;管理-&gt;SELinux Management，将系统默认的应用模式设为disable。</p> <a href="http://hi.baidu.com/learned/blog/item/575f8554074bef173b29351a.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/Linux">Linux</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/575f8554074bef173b29351a.html#comment">查看评论</a>]]></description>
        <pubDate>2009年03月13日 星期五  上午 11:10</pubDate>
        <category><![CDATA[Linux]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/575f8554074bef173b29351a.html</guid>
</item>

<item>
        <title><![CDATA[http://gstreamer.freedesktop.org/]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/8dd614953b080c007af48028.html]]></link>
        <description><![CDATA[
		
		<a href="http://gstreamer.freedesktop.org/">http://gstreamer.freedesktop.org/</a> 
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%D2%F4%CA%D3%C6%B5%BC%BC%CA%F5">音视频技术</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/8dd614953b080c007af48028.html#comment">查看评论</a>]]></description>
        <pubDate>2009年03月05日 星期四  下午 11:24</pubDate>
        <category><![CDATA[音视频技术]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/8dd614953b080c007af48028.html</guid>
</item>

<item>
        <title><![CDATA[极限编程(XP)介绍]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/6804ba1cb6f1a18386d6b6ad.html]]></link>
        <description><![CDATA[
		
		<h1><font size="4"><u>极限编程</u></font></h1>
<div>
<div class="bpctrl"> </div>
　　ExtremeProgramming（极限编程，简称XP）是由KentBeck在1996年提出的。KentBeck在九十年代初期与WardCunningham共事时，就一直共同探索着新的软件开发方法，希望能使软件开发更加简单而有效。Kent仔细地观察和分析了各种简化软件开发的前提条件、可能行以及面临的困难。1996年三月，Kent终于在为DaimlerChrysler所做的一个项目中引入了新的软件开发观念&mdash;&mdash;XP。 <br>
<div class="spctrl"> </div>
　　XP是一个轻量级的、灵巧的软件开发方法；同时它也是一个非常严谨和周密的方法。它的基础和价值观是交流、朴素、反馈和勇气；即，任何一个软件项目都可以从四个方面入手进行改善：加强交流；从简单做起；寻求反馈；勇于实事求是。XP是一种近螺旋式的开发方法，它将复杂的开发过程分解为一个个相对比较简单的小周期；通过积极的交流、反馈以及其它一系列的方法，开发人员和客户可以非常清楚开发进度、变化、待解决的问题和潜在的困难等，并根据实际情况及时地调整开发过程。 <br>
<div class="spctrl"> </div>
　　什么是软件开发 <br>
<div class="spctrl"> </div>
　　软件开发的内容是：需求、设计、编程和测试！ <br>
<div class="spctrl"> </div>
　　需求：不仅仅是用户需求，应该是开发中遇到的所有的需求。比如，你首先要知道做这个项目是为了解决什么问题；测试案例中应该输入什么数据……为了清楚地知道这些需求，你经常要和客户、项目经理等交流。 <br>
<div class="spctrl"> </div>
　　设计：编码前，肯定有个计划告诉你要做什么，结构是怎样等等。你一定要按照这个来做，否则可能会一团糟。 <br>
<div class="spctrl"> </div>
　　编程：如果在项目截止日，你的程序不能跑起来或达不到客户的要求，你就拿不到钱。 <br>
<div class="spctrl"> </div>
　　测试：目的是让你知道，什么时候算是完成了。如果你聪明，你就应该先写测试，这样可以及时知道你是否真地完成了。否则，你经常会不知道，到底有哪些功能是真正完成了，离预期目标还差多远。 <br>
<div class="spctrl"> </div>
　　软件开发中，客户和开发人员都有自己的基本权利和义务。 <br>
<div class="spctrl"> </div>
　　客户： <br>
<div class="spctrl"> </div>
　　定义每个用户需求的商业优先级； <br>
<div class="spctrl"> </div>
　　制订总体计划，包括用多少投资、经过多长时间、达到什么目的； <br>
<div class="spctrl"> </div>
　　在项目开发过程中的每个工作周，都能让投资获得最大的收益； <br>
<div class="spctrl"> </div>
　　通过重复运行你所指定的功能测试，准确地掌握项目进展情况； <br>
<div class="spctrl"> </div>
　　能随时改变需求、功能或优先级，同时避免昂贵的再投资；能够根据各种变化及时调整项目计划； <br>
<div class="spctrl"> </div>
　　能够随时取消项目；项目取消时，以前的开发工作不是一堆垃圾，已开发完的功能是合乎要求的，正在进行或未完成的的工作则应该是不难接手的。 <br>
<div class="spctrl"> </div>
　　开发人员： <br>
<div class="spctrl"> </div>
　　知道要做什么，以及要优先做什么； <br>
<div class="spctrl"> </div>
　　工作有效率； <br>
<div class="spctrl"> </div>
　　有问题或困难时，能得到客户、同事、上级的回答或帮助； <br>
<div class="spctrl"> </div>
　　对工作做评估，并根据周围情况的变化及时重新评估； <br>
<div class="spctrl"> </div>
　　积极承担工作，而不是消极接受分配； <br>
<div class="spctrl"> </div>
　　一周40小时工作制，不加班。 <br>
<div class="spctrl"> </div>
　　这就是软件开发，除此之外再还有其它要关心的问题！ <br>
<div class="spctrl"> </div>
　　灵巧的轻量级软件开发方法 <br>
<div class="spctrl"> </div>
　　一套软件开发方法是由一系列与开发相关的规则、规范和惯例。重量级的开发方法严格定义了许多的规则、流程和相关的文档工作。灵巧的轻量级开发方法，其规则和文档相对较少，流程更加灵活，实施起来相对较容易。 <br>
<div class="spctrl"> </div>
　　在软件工程概念出现以前，程序员们按照自己喜欢的方式开发软件。程序的质量很难控制，调试程序很繁琐，程序员之间也很难读懂对方写的代码。1968年，EdsgerDijkstra给CACM写了一封题为GOTOStatementConsideredHarmful的信，软件工程的概念由此诞生。程序员们开始摒弃以前的做法，转而使用更系统、更严格的开发方法。为了使控制软件开发和控制其它产品生产一样严格，人们陆续制定了很多规则和做法，发明了很多软件工程方法，软件质量开始得到大幅度提高。随着遇到的问题更多，规则和流程也越来越精细和复杂。 <br>
<div class="spctrl"> </div>
　　到了今天，在实际开发过程中，很多规则已经难于遵循，很多流程复杂而难于理解，很多项目中文档的制作过程正在失去控制。人们试图提出更全面更好的一揽子方案，或者寄希望于更复杂的、功能更强大的辅助开发工具（CaseTools），但总是不能成功，而且开发规范和流程变得越来越复杂和难以实施。 <br>
<div class="spctrl"> </div>
　　为了赶进度，程序员们经常跳过一些指定的流程，很少人能全面遵循那些重量级开发方法。 <br>
<div class="spctrl"> </div>
　　失败的原因很简单，这个世界没有万能药。因此，一些人提出，将重量级开发方法中的规则和流程进行删减、重整和优化，这样就产生了很多适应不同需要的轻量级流程。在这些流程中，合乎实际需要的规则被保留下来，不必要的复杂化开发的规被抛弃。而且，和传统的开发方法相比，轻量级流程不再象流水生产线，而是更加灵活。 <br>
<div class="spctrl"> </div>
　　ExtremeProgramming（XP）就是这样一种灵巧的轻量级软件开发方法。 <br>
<div class="spctrl"> </div>
　　为什么称为&ldquo;Extreme&rdquo;（极限） <br>
<div class="spctrl"> </div>
　　&ldquo;Extreme&rdquo;（极限）是指，对比传统的项目开发方式，XP强调把它列出的每个方法和思想做到极限、做到最好；其它XP所不提倡的，则一概忽略（如开发前期的整体设计等）。一个严格实施XP的项目，其开发过程应该是平稳的、高效的和快速的，能够做到一周40小时工作制而不拖延项目进度。 <br>
<div class="spctrl"> </div>
　　XP的软件开发是什么样 <br>
<div class="spctrl"> </div>
　　1极限的工作环境 <br>
<div class="spctrl"> </div>
　　为了在软件开发过程中最大程度地实现和满足客户和开发人员的基本权利和义务，XP要求把工作环境也做得最好。每个参加项目开发的人都将担任一个角色（项目经理、项目监督人等等）并履行相应的权利和义务。所有的人都在同一个开放的开发环境中工作，最好是所有人在同一个大房子中工作，还有茶点供应；每周40小时，不提倡加班；每天早晨，所有人一起站着开个短会；墙上有一些大白板，所有的Story卡、CRC卡等都贴在上面，讨论问题的时候可以在上面写写画画；下班后大家可以一起玩电脑游戏……。 <br>
<div class="spctrl"> </div>
　　2极限的需求 <br>
<div class="spctrl"> </div>
　　客户应该是项目开发队伍中的一员，而不是和开发人员分开的；因为从项目的计划到最后验收，客户一直起着很重要的作用。开发人员和客户一起，把各种需求变成一个个小的需求模块（UserStory），例如&ldquo;计算年级的总人数，就是把该年级所有班的人数累加。&rdquo;；这些模块又会根据实际情况被组合在一起或者被分解成更小的模块；它们都被记录在一些小卡片（StoryCard）上，之后分别被程序员们在各个小的周期开发中（Iteration，通常不超过3个星期）实现；客户根据每个模块的商业价值来指定它们的优先级；开发人员要做的是确定每个需求模块的开发风险，风险高的（通常是因为缺乏类似的经验）需求模块将被优先研究、探索和开发；经过开发人员和客户分别从不同的角度评估每个模块后，它们被安排在不同的开发周期里，客户将得到一个尽可能准确的开发计划；客户为每个需求模块指定验收测试（功能测试）。 <br>
<div class="spctrl"> </div>
　　每发布一次开发的软件（经过一个开发周期），用户都能得到一个可以开始使用的系统，这个系统全面实现了相应的计划中的所有需求。而在一些传统的开发模式中，无论什么功能，用户都要等到所有开发完成后才能开始使用。 <br>
<div class="spctrl"> </div>
　　3极限的设计 <br>
<div class="spctrl"> </div>
　　从具体开发的角度来看，XP内层的过程是一个个基于测试驱动的开发（TestDrivenDevelopment）周期，诸如计划和设计等外层的过程都是围绕这些展开的。每个开发周期都有很多相应的单元测试（UnitTest）。刚开始，因为什么都没有实现，所以所有的单元测试都是失败的；随着一个个小的需求模块的完成，通过的单元测试也越来越多。通过这种方式，客户和开发人员都很容易检验，是否履行了对客户的承诺。XP提倡对于简单的设计（SimpleDesign），就是用最简单的方式，使得为每个简单的需求写出来的程序可以通过所有相关的单元测试。XP强调抛弃那种一揽子详细设计方式（BigDesignUpFront），因为这种设计中有很多内容是你现在或最近都根本不需要的。XP还大力提倡设计复核（Review）、代码复核以及重整和优化（Refectory），所有的这些过程其实也是优化设计的过程；在这些过程中不断运行单元测试和功能测试，可以保证经过重整和优化后的系统仍然符合所有需求。 <br>
<div class="spctrl"> </div>
　　4极限的编程 <br>
<div class="spctrl"> </div>
　　既然编程很重要，XP就提倡两个人一起写同一段程序（PairProgramming），而且代码所有权是归于整个开发队伍（CollectiveCodeOwnership）。程序员在写程序和重整优化程序的时候，都要严格遵守编程规范。任何人都可以修改其他人写的程序，修改后要确定新程序能通过单元测试。 <br>
<div class="spctrl"> </div>
　　5极限的测试 <br>
<div class="spctrl"> </div>
　　既然测试很重要，XP就提倡在开始写程序之前先写单元测试。开发人员应该经常把开发好的模块整合到一起（ContinuousIntegration），每次整合后都要运行单元测试；做任何的代码复核和修改，都要运行单元测试；发现了BUG，就要增加相应的测试（因此XP方法不需要BUG数据库）。除了单元测试之外，还有整合测试，功能测试、负荷测试和系统测试等。所有这些测试，是XP开发过程中最重要的文档之一，也是最终交付给用户的内容之一。 <br>
<div class="spctrl"> </div>
　　XP中的重要惯例和规则 <br>
<div class="spctrl"> </div>
　　1项目开发小组（Team） <br>
<div class="spctrl"> </div>
　　在XP中，每个对项目做贡献的人都应该是项目开发小组中的一员。而且，这个小组中必须至少有一个人对用户需求非常清晰，能够提出需求、决定各个需求的商业价值（优先级）、根据需求等的变化调整项目计划等。这个人扮演的是&ldquo;客户&rdquo;这个角色，当然最好就是实际的最终用户，因为整个项目就是围绕最终用户的需求而展开的。程序员是项目开发小组中必不可少的成员。小组中可以有测试员，他们帮助客户制订验收测试；有分析员，帮助客户确定需求；通常还有个Coach（教练），负责跟踪开发进度、解决开发中遇到的一些问题、推动项目进行；还可以又一个项目经理，负责调配资源、协助项目内外的交流沟通等等。项目小组中有这么多角色，但并不是说，每个人做的工作是别人不能插手或干预的，XP鼓励每个人尽可能地为项目多做贡献。平等相处，取长补短；这就是最好的XP开发小组。 <br>
<div class="spctrl"> </div>
　　2计划项目（PlanningGame）、验收测试、小规模发布（SmallReleases） <br>
<div class="spctrl"> </div>
　　XP开发小组使用简单的方式进行项目计划和开发跟踪，并以次预测项目进展情况和决定未来的步骤。根据需求的商业价值，开发小组针对一组组的需求进行一系列的开发和整合，每次开发都会产生一个通过测试的、可以使用的系统。 <br>
<div class="spctrl"> </div>
　　计划项目 <br>
<div class="spctrl"> </div>
　　XP的计划过程主要针对软件开发中的两个问题：预测在交付日期前可以完成多少工作；现在和下一步该做些什么。不断的回答这两个问题，就是直接服务于如何实施及调整开发过程；与此相比，希望一开始就精确定义整个开发过程要做什么事情以及每件事情要花多少时间，则事倍功半。针对这两个问题，XP中又两个主要的相应过程： <br>
<div class="spctrl"> </div>
　　软件发布计划（ReleasePlanning）。客户阐述需求，开发人员估算开发成本和风险。客户根据开发成本、风险和每个需求的重要性，制订一个大致的项目计划。最初的项目计划没有必要（也没有可能）非常准确，因为每个需求的开发成本、风险及其重要性都不是一成不变的。而且，这个计划会在实施过程中被不断地调整以趋精确。 <br>
<div class="spctrl"> </div>
　　周期开发计划（IterationPlanning）。开发过程中，应该有很多阶段计划（比如每三个星期一个计划）。开发人员可能在某个周期对系统进行内部的重整和优化（代码和设计），而在某个周期增加了新功能，或者会在一个周期内同时做两方面的工作。但是，经过每个开发周期，用户都应该能得到一个已经实现了一些功能的系统。而且，每经过一个周期，客户就会再提出确定下一个周期要完成的需求。在每个开发周期中，开发人员会把需求分解成一个个很小的任务，然后估计每个任务的开发成本和风险。这些估算是基于实际开发经验的，项目做得多了，估算自然更加准确和精确；在同一个项目中，每经过一个开发周期，下一次的估算都会有更过的经验、参照和依据，从而更加准确。这些简单的步骤对客户提供了丰富的、足够的信息，使之能灵活有效地调控开发进程。每过两三个星期，客户总能够实实在在地看到开发人员已经完成的需求。在XP里，没有什么&ldquo;快要完成了&rdquo;、&ldquo;完成了90%&rdquo;的模糊说法，要不是完成了，要不就是没完成。这种做法看起来好像有利有弊：好处是客户可以马上知道完成了哪些、做出来的东西是否合用、下面还要做些什么或改进什么等等；坏处是客户看到做出来的东西，可能会很不满意甚至中止合同。实际上，XP的这种做法是为了及早发现问题、解决问题，而不是等到过了几个月，用户终于看到开发完的系统了，然后才告诉你这个不行、那个变了、还要增加 <br>
<div class="spctrl"> </div>
　　哪个内容等等。 <br>
<div class="spctrl"> </div>
　　验收测试 <br>
<div class="spctrl"> </div>
　　客户对每个需求都定义了一些验收测试。通过运行验收测试，开发人员和客户可以知道开发出来的软件是否符合要求。XP开发人员把这些验收测试看得和单元测试一样重要。为了不浪费宝贵的时间，最好能将这些测试过程自动化。 <br>
<div class="spctrl"> </div>
　　频繁地小规模发布软件（SmallReleases） <br>
<div class="spctrl"> </div>
　　每个周期（Iteration）开发的需求都是用户最需要的东西。在XP中，对于每个周期完成时发布的系统，用户都应该可以很容易地进行评估，或者已经能够投入实际使用。这样，软件开发对于客户来说，不再是看不见摸不着的东西，而是实实在在的。XP要求频繁地发布软件，如果有可能，应该每天都发布一个新版本；而且在完成任何一个改动、整合或者新需求后，就应该立即发布一个新版本。这些版本的一致性和可靠性，是靠验收测试和测试驱动的开发来保证的。 <br>
<div class="spctrl"> </div>
　　3简单设计，PairProgramming，测试驱动开发，重整和优化 <br>
<div class="spctrl"> </div>
　　XP程序员不但做为一个开发小组共同工作，还以两个人为一个小开发单元编写同一个程序。开发人员们进行简单的设计，编写单元测试后再编写符合测试要求的代码，并在满足需求的前提下不断地优化设计。 <br>
<div class="spctrl"> </div>
　　简单设计 <br>
<div class="spctrl"> </div>
　　XP中让初学者感到最困惑的就是这点。XP要求用最简单的办法实现每个小需求，前提是按照这些简单设计开发出来的软件必须通过测试。这些设计只要能满足系统和客户在当下的需求就可以了，不需要任何画蛇添足的设计，而且所有这些设计都将在后续的开发过程中就被不断地重整和优化。 <br>
<div class="spctrl"> </div>
　　在XP中，没有那种传统开发模式中一次性的、针对所有需求的总体设计。在XP中，设计过程几乎一直贯穿着整个项目开发：从制订项目的计划，到制订每个开发周期（Iteration）的计划，到针对每个需求模块的简捷设计，到设计的复核，以及一直不间断的设计重整和优化。整个设计过程是个螺旋式的、不断前进和发展的过程。从这个角度看，XP是把设计做到了极致。 <br>
<div class="spctrl"> </div>
　　PairProgramming <br>
<div class="spctrl"> </div>
　　XP中，所有的代码都是由两个程序员在同一台机器上一起写的&mdash;&mdash;这是XP中让人争议最多、也是最难实施的一点。这保证了所有的代码、设计和单元测试至少被另一个人复核过，代码、设计和测试的质量因此得到提高。看起来这样象是在浪费人力资源，但是各种研究表明事实恰恰相反。&mdash;&mdash;这种工作方式极大地提高了工作强度和工作效率。 <br>
<div class="spctrl"> </div>
　　很多程序员一开始是被迫尝试这点的（XP也需要行政命令的支持）。开始时总是不习惯的，而且两个人的效率不会比一个人的效率高。这种做法的效果往往要坚持几个星期或一两个月后才能很显著。据统计，在所有刚开始PairProgramming的程序员中，90%的人在两个月以后都很认为这种工作方式更加高效。 <br>
<div class="spctrl"> </div>
　　项目开发中，每个人会不断地更换合作编程的伙伴。因此，PairProgramming不但提高了软件质量，还增强了相互之间的知识交流和更新，增强了相互之间的沟通和理解。这不但有利于个人，也有利于整个项目、开发队伍和公司。从这点看，PairProgramming不仅仅适用于XP，也适用于所有其它的软件开发方法。 <br>
<div class="spctrl"> </div>
　　测试驱动开发 <br>
<div class="spctrl"> </div>
　　反馈是XP的四个基本的价值观之一&mdash;&mdash;在软件开发中，只有通过充分的测试才能获得充分的反馈。XP中提出的测试，在其它软件开发方法中都可以见到，比如功能测试、单元测试、系统测试和负荷测试等；与众不同的是，XP将测试结合到它独特的螺旋式增量型开发过程中，测试随着项目的进展而不断积累。另外，由于强调整个开发小组拥有代码，测试也是由大家共同维护的。即，任何人在往代码库中放程序（CheckIn）前，都应该运行一遍所有的测试；任何人如果发现了一个BUG，都应该立即为这个BUG增加一个测试，而不是等待写那个程序的人来完成；任何人接手其他人的任务，或者修改其他人的代码和设计，改动完以后如果能通过所有测试，就证明他的工作没有破坏愿系统。这样，测试才能真正起到帮助获得反馈的作用；而且，通过不断地优先编写和累积，测试应该可以基本覆盖全部的客户和开发需求，因此开发人员和客户可以得到尽可能充足的反馈。 <br>
<div class="spctrl"> </div>
　　重构(Refactoring) <br>
<div class="spctrl"> </div>
　　XP强调简单的设计，但简单的设计并不是没有设计的流水账式的程序，也不是没有结构、缺乏重用性的程序设计。开发人员虽然对每个USERSTORY都进行简单设计，但同时也在不断地对设计进行改进，这个过程叫设计的重构（Refactoring）。这个名字最早出现在MartinFowler写的《Refactoring:ImprovingtheDesignofExistingCode》这本书中。 <br>
<div class="spctrl"> </div>
　　Refactoring主要是努力减少程序和设计中重复出现的部分，增强程序和设计的可重用性。Refactoring的概念并不是XP首创的，它已经被提出了近30年了，而且一直被认为是高质量的代码的特点之一。但XP强调，把Refactoring做到极致，应该随时随地、尽可能地进行Refactoring，只要有可能，程序员都不应该心疼以前写的程序，而要毫不留情地改进程序。当然，每次改动后，程序员都应该运行测试程序，保证新系统仍然符合预定的要求。 <br>
<div class="spctrl"> </div>
　　4频繁地整合，集体拥有代码（CollectiveCodeOwnership），编程规范 <br>
<div class="spctrl"> </div>
　　XP开发小组经常整合不同的模块。为了提高软件质量，除了测试驱动开发和PairProgramming以外，XP要求每个人的代码都要遵守编程规范，任何人都可以修改其他人写的代码，而且所有人都应该主动检查其他人写的代码。 <br>
<div class="spctrl"> </div>
　　频繁地整合（Integration） <br>
<div class="spctrl"> </div>
　　在很多项目中，开发人员往往很迟才把各个模块整合在一起。在这些项目中，开发人员经常在整合过程中发现很多问题，但不能肯定到底是谁的程序出了问题；而且，只有整合完成后，开发人员才开始稍稍使用整个系统，然后就马上交付给客户验收。对于客户来说，即使这些系统能够通过终验收测试，因为使用时间短，客户门心里并没有多少把握。 <br>
<div class="spctrl"> </div>
　　为了解决这些问题，XP提出，整个项目过程中，应该频繁地，尽可能地整合已经开发完的USERSTORY（每次整合一个新的USERSTORY）。每次整合，都要运行相应的单元测试和验收测试，保证符合客户和开发的要求。整合后，就发布一个新的应用系统。这样，整个项目开发过程中，几乎每隔一两天，都会发布一个新系统，有时甚至会一天发布好几个版本。通过这个过程，客户能非常清楚地掌握已经完成的功能和开发进度，并基于这些情况和开发人员进行有效地、及时地交流，以确保项目顺利完成。 <br>
<div class="spctrl"> </div>
　　集体拥有代码（CollectiveCodeOwnership） <br>
<div class="spctrl"> </div>
　　在很多项目开发过程中，开发人员只维护自己的代码，而且很多人不喜欢其他人随意修改自己的代码。因此，即使可能有相应的比较详细的开发文档，但一个程序员却很少、也不太愿意去读其他程序员的代码；而且，因为不清楚其他人的程序到底实现了什么功能，一个程序员一般也不敢随便改动其他人的代码。同时，因为是自己维护自己的代码，可能因为时间紧张或技术水平的局限性，某些问题一直不能被发现或得到比较好的解决。针对这点，XP提倡大家共同拥有代码，每个人都有权利和义务阅读其他代码，发现和纠正错误，重整和优化代码。这样，这些代码就不仅仅是一两个人写的，而是由整个项目开发队伍共同完成的，错误会减少很多，重用性会尽可能地得到提高，代码质量是非常好。 <br>
<div class="spctrl"> </div>
　　为了防止修改其他人的代码而引起系统崩溃，每个人在修改后都应该运行测试程序。（从这点，我们可以再次看到，XP的各个惯例和规则是怎样有机地结合在一起的。） <br>
<div class="spctrl"> </div>
　　编程规范 <br>
<div class="spctrl"> </div>
　　XP开发小组中的所有人都遵循一个统一的编程标准，因此，所有的代码看起来好像是一个人写的。因为有了统一的编程规范，每个程序员更加容易读懂其他人写的代码，这是是实现CollectiveCodeOwnership的重要前提之一。 <br>
<div class="spctrl"> </div>
　　5Metaphor（系统比喻），不加班 <br>
<div class="spctrl"> </div>
　　XP过程通过使用一些形象的比喻让所有人对系统有个共同的、简洁的认识。XP认为加班是不正常的，因为这说明关于项目进度的估计和安排有问题。 <br>
<div class="spctrl"> </div>
　　Metaphor（系统比喻） <br>
<div class="spctrl"> </div>
　　为了帮助每个人一致清楚地理解要完成的客户需求、要开发的系统功能，XP开发小组用很多形象的比喻来描述系统或功能模块是怎样工作的。比如，对于一个搜索引擎，它的Metaphor可能就是&ldquo;一大群蜘蛛，在网上四处寻找要捕捉的东西，然后把东西带回巢穴。&rdquo; <br>
<div class="spctrl"> </div>
　　不加班 <br>
<div class="spctrl"> </div>
　　大量的加班意味着原来的计划是不准确的，或者是程序远不清楚自己到底什么时候能完成什么工作。而且，开发管理人员和客户也因此无法准确掌握开发速度；开发人员也因此非常疲劳。XP认为，如果出现大量的加班现象，开发管理人员（比如Coach）应该和客户一起确定加班的原因，并及时调整项目计划、进度和资源。 <br>
<div class="spctrl"> </div>
　　XP中一些基本概念的简介 <br>
<div class="spctrl"> </div>
　　UserStory：开发人员要求客户把所有的需求写成一个个独立的小故事，每个只需要几天时间就可以完成。开发过程中，客户可以随时提出新的UserStory，或者更改以前的UserStory。 <br>
<div class="spctrl"> </div>
　　StoryEstimates和开发速度：开发小组对每个UserStory进行估算，并根据每个开发周期（Iteration）中的实际情况反复计算开发速度。这样，开发人员和客户能知道每个星期到底能开发多少UserStory。 <br>
<div class="spctrl"> </div>
　　ReleasePlan和ReleaseScope：整个开发过程中，开发人员将不断地发布新版本。开发人员和客户一起确定每个发布所包含的UserStory。 <br>
<div class="spctrl"> </div>
　　Iteration（开发周期，或称迭代）和IterationPlan：在一个Release过程中，开发人员要求客户选择最有价值的UserStory作为未来一两个星期的开发内容。 <br>
<div class="spctrl"> </div>
　　TheSeed：第一个迭代（Iteration）完成后，提交给客户的系统。虽然这不是最终的产品，但它已经实现了几个客户认为是最重要的Story，开发人员将逐步在其基础上增加新的模块。 <br>
<div class="spctrl"> </div>
　　ContinuousIntegration（整合）：把开发完的UserStory的模块一个个拼装起来，一步步接近乃至最终完成最终产品。 <br>
<div class="spctrl"> </div>
　　验收测试（功能测试）：对于每个UserStory，客户将定义一些测试案例，开发人员将使运行这些测试案例的过程自动化。 <br>
<div class="spctrl"> </div>
　　UnitTest（单元测试）：在开始写程序前，程序员针对大部分类的方法，先写出相应的测试程序。 <br>
<div class="spctrl"> </div>
　　Refactoring(重构)：去掉代码中的冗余部分，增加代码的可重用性和伸缩性。 <br>
<div class="spctrl"> </div>
　　小结 <br>
<div class="spctrl"> </div>
　　XP的一个成功因素是重视客户的反馈&mdash;&mdash;开发的目的就是为了满足客户的需要。XP方法使开发人员始终都能自信地面对客户需求的变化。XP强调团队合作，经理、客户和开发人员都是开发团队中的一员。团队通过相互之间的充分交流和合作，使用XP这种简单但有效的方式，努力开发出高质量的软件。XP的设计简单而高效；程序员们通过测试获得客户反馈，并根据变化修改代码和设计，他们总是争取尽可能早地将软件交付给客户。XP程序员能够勇于面对需求和技术上的变化。 <br>
<div class="spctrl"> </div>
　　XP很象一个由很多小块拼起来的智力拼图，单独看每一小块都没有什么意义，但拼装好后，一幅美丽的图画就会呈现在你面前。</div> <a href="http://hi.baidu.com/learned/blog/item/6804ba1cb6f1a18386d6b6ad.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%BC%BC%CA%F5%B8%C5%CA%F6">技术概述</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/6804ba1cb6f1a18386d6b6ad.html#comment">查看评论</a>]]></description>
        <pubDate>2009年02月28日 星期六  上午 00:47</pubDate>
        <category><![CDATA[技术概述]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/6804ba1cb6f1a18386d6b6ad.html</guid>
</item>

<item>
        <title><![CDATA[无损音频压缩APE相关知识]]></title>
        <link><![CDATA[http://hi.baidu.com/learned/blog/item/575f8554768220183b293521.html]]></link>
        <description><![CDATA[
		
		<p>　　APE是一种无损压缩音频格式。庞大的WAV音频文件可以通过Monkey's Audio这个软件进行&quot;瘦身&quot;压缩为APE，同样，APE也可以通过Monkey's Audio还原成WAV，再刻录成CD。很多时候它被用做网络音频文件传输，因为被压缩后的APE文件容量要比WAV源文件小一半多，可以节约传输所用的时间。更重要的是，通过Monkey's Audio解压缩还原以后得到的WAV文件可以做到与压缩前的源文件完全一致。所以APE被誉为&quot;无损音频压缩格式&quot;，Monkey's Audio被誉为&quot;无损音频压缩软件&quot;。与采用WinZip或者WinRAR这类专业数据压缩软件来压缩音频文件不同，压缩之后的APE音频文件是可以直接被播放的。</p> <a href="http://hi.baidu.com/learned/blog/item/575f8554768220183b293521.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/learned/blog/category/%D2%F4%CA%D3%C6%B5%BC%BC%CA%F5">音视频技术</a>&nbsp;<a href="http://hi.baidu.com/learned/blog/item/575f8554768220183b293521.html#comment">查看评论</a>]]></description>
        <pubDate>2009年02月06日 星期五  下午 09:18</pubDate>
        <category><![CDATA[音视频技术]]></category>
        <author><![CDATA[ezengyong]]></author>
		<guid>http://hi.baidu.com/learned/blog/item/575f8554768220183b293521.html</guid>
</item>


</channel>
</rss>