如果我不想要调用strcmp所需的开销,我会按照以下代码示例中描述的方式将字符串与短字符串文字进行比较:
#ifdef LITTLE_ENDIAN //little-endian-addressing #define BytesAsDWord_M(a, b, c, d)\ ((ulong) ((a) | ((b) << 8) | ((ulong) (c) << 16) | ((ulong) (d) << 24))) #define BytesAsWord_M(a, b)((ushort) ((a) | ((b) << 8))) #else //LITTLE_ENDIAN //little-endian-addressing #define BytesAsDWord_M(a, b, c, d)\ ((ulong) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24))) #define BytesAsWord_M(a, b) ((ushort) ((b) | ((a) << 8))) #endif //LITTLE_ENDIAN //little-endian-addressing bool AbsCompare(char* chr_p) //compare string with "abs" { if (*((ulong*) &chr_p[1]) == BytesAsDWord_M('a', 'b', 's', '\0')) return true; return false; }
只要我编译时没有启用优化选项,gcc就编译这个例子.启用优化后,我收到警告:
"解除引用类型惩罚指针将破坏严格别名规则"
即使使用-O3进行优化也不会产生有效的代码,如下例所示:
//abstest.c #includetypedef unsigned long ulong; typedef unsigned short ushort; #if BYTE_ORDER == LITTLE_ENDIAN //little-endian-addressing #define BytesAsDWord_M(a, b, c, d)\ ((ulong) ((a) | ((b) << 8) | ((ulong) (c) << 16) | ((ulong) (d) << 24))) #define BytesAsWord_M(a, b)((ushort) ((a) | ((b) << 8))) #else //BYTE_ORDER == LITTLE_ENDIAN //little-endian-addressing #define BytesAsDWord_M(a, b, c, d)\ ((ulong) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24))) #define BytesAsWord_M(a, b) ((ushort) ((b) | ((a) << 8))) #endif //BYTE_ORDER == LITTLE_ENDIAN //little-endian-addressing int AbsCompare1(char* chr_p) { return *(ulong*) chr_p == BytesAsDWord_M('a', 'b', 's', '\0'); } int AbsCompare2(char* chr_p) { return strcmp(chr_p, "abs"); } int main(int argc __attribute__((unused)), char ** argv) { int i; int j; i = AbsCompare1(argv[0]); j = AbsCompare2(argv[0]); return i + j; }
objdump -d -Mintel abstest:
080483d0: 80483d0: 55 push ebp 80483d1: 89 e5 mov ebp,esp 80483d3: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 80483d6: 5d pop ebp 80483d7: 81 38 61 62 73 00 cmp DWORD PTR [eax],0x736261 80483dd: 0f 94 c0 sete al 80483e0: 0f b6 c0 movzx eax,al 80483e3: c3 ret 080483f0 : 80483f0: 55 push ebp 80483f1: 0f b6 0d 5c 85 04 08 movzx ecx,BYTE PTR ds:0x804855c 80483f8: 89 e5 mov ebp,esp 80483fa: 8b 55 08 mov edx,DWORD PTR [ebp+0x8] 80483fd: 0f b6 02 movzx eax,BYTE PTR [edx] 8048400: 29 c8 sub eax,ecx 8048402: 75 2b jne 804842f 8048404: 0f b6 42 01 movzx eax,BYTE PTR [edx+0x1] 8048408: 0f b6 0d 5d 85 04 08 movzx ecx,BYTE PTR ds:0x804855d 804840f: 29 c8 sub eax,ecx 8048411: 75 1c jne 804842f 8048413: 0f b6 42 02 movzx eax,BYTE PTR [edx+0x2] 8048417: 0f b6 0d 5e 85 04 08 movzx ecx,BYTE PTR ds:0x804855e 804841e: 29 c8 sub eax,ecx 8048420: 75 0d jne 804842f 8048422: 0f b6 42 03 movzx eax,BYTE PTR [edx+0x3] 8048426: 0f b6 15 5f 85 04 08 movzx edx,BYTE PTR ds:0x804855f 804842d: 29 d0 sub eax,edx 804842f: 5d pop ebp 8048430: c3 ret
有没有可能直接比较这个简短的文字,而没有将chr_p嵌入到一个联合中,特别是因为我想在任意索引比较chr_p,比如"&chr_p [1]"?
不,那里没有.你知道编译器会使用它的知识strcmp
吗?它将完全按照你想要的方式完成(消除调用开销),而不必在源代码中求助于类型惩罚.在编译器从别名分析中获得所有好处之后,这些代码转换通常在代码生成器中完成.
如果我gcc -O3
用来编译以下程序,则无法strcmp
找到任何调用.
#includeint main(int argc, char ** argv) { return strcmp(argv[0], "abs"); }
例如,我的x86程序集看起来像这样(gcc版本4.3.2(Debian 4.3.2-1.1))(我知道它已经过时了):
main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx movl 4(%ecx), %eax movl (%eax), %edx movzbl (%edx), %eax subl $97, %eax jne .L2 movzbl 1(%edx), %eax subl $98, %eax jne .L2 movzbl 2(%edx), %eax subl $115, %eax jne .L2 movzbl 3(%edx), %eax .L2: popl %ecx popl %ebp leal -4(%ecx), %esp ret
基本上strcmp
已经内联和展开.当然,它在很大程度上取决于您的目标的代码生成器.因此,如果它尚未进化,那么它仍然可能产生一个strcmp
.仍然让你想知道你是否应该为丑陋的代码加重自己,如果代码生成器可能会在以后支持这个...当你仍然坚持你的代码时.