向所有的C语言编程者问好。
在查找类似问题之前,我找不到一个相同的答案。
如何以可移植的方式提取/比较4字节(当然不能使用memcpy / memcmp)?
我从未学过C语言,因此我是一个活生生的例证,没有掌握基础知识,一切都变得一塌糊涂。无论如何,现在写作也不是说“从字母表开始”。
ulHashPattern = *(unsigned long *)(pbPattern);
for (a=0; a < ASIZE; a++) bm_bc[a]=cbPattern;
for (j=0; j < cbPattern-1; j++) bm_bc[pbPattern[j]]=cbPattern-j-1;
i=0;
while (i <= cbTarget-cbPattern) {
if ( *(unsigned long *)&pbTarget[i] == ulHashPattern ) {
上述片段在Windows 32位编译器上按预期工作。我希望所有这样的4对4比较在64位Windows和Linux下也能工作。 很多时候我需要2、4、8字节的传输,在上面的例子中,我需要从某个pbTarget偏移量显式地获取4字节。现在的问题是:我应该使用什么类型来替代unsigned long?(我猜类似于UINT16、UINT32、UINT64的类型就可以了)。换句话说,我需要哪三种类型才能始终独立地表示2、4、8字节? 我认为这个基本问题会引起很多麻烦,所以应该澄清一下。
2012年1月16日补充:
@Richard J. Ross III
我有点困惑!因为我不知道Linux是否使用1]或2],即在Linux中是否定义了_STD_USING, 换句话说,哪一组类型uint8_t,...,uint64_t或_CSTD uint8_t,...,_CSTD uint64_t是可移植的?
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef _ULonglong uint64_t;
[2] 来自 MVS 10.0 的 stdint.h 摘录
#if defined(_STD_USING)
...
using _CSTD uint8_t; using _CSTD uint16_t;
using _CSTD uint32_t; using _CSTD uint64_t;
...
使用 Microsoft C 32 位版本没有问题:
; 3401 : if ( *(_CSTD uint32_t *)&pbTarget[i] == *(_CSTD uint32_t *)(pbPattern) )
01360 8b 04 19 mov eax, DWORD PTR [ecx+ebx]
01363 8b 7c 24 14 mov edi, DWORD PTR _pbPattern$GSCopy$[esp+1080]
01367 3b 07 cmp eax, DWORD PTR [edi]
01369 75 2c jne SHORT $LN80@Railgun_Qu@6
但当目标代码为64位时,就会发生这种情况:
D:\_KAZE_Simplicius_Simplicissimus_Septupleton_r2-_strstr_SHORT-SHOWDOWN_r7>cl /Ox /Tcstrstr_SHORT-SHOWDOWN.c /Fastrstr_SHORT-SHOWDOWN /w /FAcs
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.30729.01 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
strstr_SHORT-SHOWDOWN.c
strstr_SHORT-SHOWDOWN.c(1925) : fatal error C1083: Cannot open include file: 'stdint.h': No such file or directory
D:\_KAZE_Simplicius_Simplicissimus_Septupleton_r2-_strstr_SHORT-SHOWDOWN_r7>
“Linux的stdint.h头文件是否总是存在呢?”
“我没有放弃,注释掉了它:
//#include <stdint.h>
,然后编译通过了。”; 3401 : if ( !memcmp(&pbTarget[i], &ulHashPattern, 4) )
01766 49 63 c4 movsxd rax, r12d
01769 42 39 2c 10 cmp DWORD PTR [rax+r10], ebp
0176d 75 38 jne SHORT $LN1@Railgun_Qu@6
; 3401 : if ( *(unsigned long *)&pbTarget[i] == ulHashPattern )
01766 49 63 c4 movsxd rax, r12d
01769 42 39 2c 10 cmp DWORD PTR [rax+r10], ebp
0176d 75 38 jne SHORT $LN1@Railgun_Qu@6
这个“unsigned long *”让我困扰,因为gcc -m64会获取QWORD而不是DWORD,对吗?
@Mysticial 只想展示Microsoft CL 32位v16完成的三种不同的翻译: 1]
; 3400 : if ( !memcmp(&pbTarget[i], pbPattern, 4) )
01360 8b 04 19 mov eax, DWORD PTR [ecx+ebx]
01363 8b 7c 24 14 mov edi, DWORD PTR _pbPattern$GSCopy$[esp+1080]
01367 3b 07 cmp eax, DWORD PTR [edi]
01369 75 2c jne SHORT $LN84@Railgun_Qu@6
[2]
; 3400 : if ( !memcmp(&pbTarget[i], &ulHashPattern, 4) )
01350 8b 44 24 14 mov eax, DWORD PTR _ulHashPattern$[esp+1076]
01354 39 04 2a cmp DWORD PTR [edx+ebp], eax
01357 75 2e jne SHORT $LN83@Railgun_Qu@6
3]
; 3401 : if ( *(uint32_t *)&pbTarget[i] == ulHashPattern )
01350 8b 44 24 14 mov eax, DWORD PTR _ulHashPattern$[esp+1076]
01354 39 04 2a cmp DWORD PTR [edx+ebp], eax
01357 75 2e jne SHORT $LN79@Railgun_Qu@6
最初的目标是通过单个mov指令(分别使用*(uint32_t *)&pbTarget[i])提取并比较4字节和一个寄存器变量的4字节长度,即在单个指令中进行一次RAM访问和一次比较。 令人不快的是,我只成功将memcmp()在pbPattern上的3次RAM访问(指向4个或更多字节)减少到2次,这要归功于内联。 如果我想在pbPattern的前4个字节上使用memcmp()(如2所示),则ulHashPattern不应该是寄存器类型,而3则不需要这样的限制。
; 3400 : if ( !memcmp(&pbTarget[i], &ulHashPattern, 4) )
上面的行出现了错误(ulHashPattern被定义为:register unsigned long ulHashPattern;):
strstr_SHORT-SHOWDOWN.c(3400) : error C2103: '&' on register variable
是的,你说得对:memcmp()可以解决这个问题(但有局限性)——第2节与第3节我的代码风格完全相同。显然,当我可能手动编码时不使用函数的倾向已经过去了,但我喜欢它。
虽然我还不完全满意编译器,我已将ulHashPattern定义为寄存器变量,但它每次仍从RAM加载?也许我错过了什么,但我认为这行代码(mov eax,DWORD PTR _ulHashPattern$[esp+1076])非常丑陋,会降低性能。