Portable (в основном для Linux и Windows) 4-байтовое извлечение / сравнение - PullRequest
4 голосов
/ 15 января 2012

Привет всем кодерам 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 ) {

Приведенный выше фрагмент работает так же, как и на 32-битном компиляторе Windows. Я хочу, чтобы все такие сравнения 4vs4 работали и под 64-битными Windows и Linux. Много раз мне нужно передавать 2,4,8 байта, в приведенном выше примере мне нужно явно 4 байта от некоторого смещения pbTarget. Здесь актуальный вопрос: какой тип я должен использовать вместо unsigned long ? (думаю, что-то близкое к UINT16, UINT32, UINT64 подойдет). Другими словами, какие 3 типа мне нужны, чтобы представлять 2,4,8 байта ВСЕГДА независимо от среды.

Я считаю, что этот основной вопрос вызывает много проблем, поэтому его следует уточнить.

Дополнение 2012-Янв-16:

@ Ричард Дж. Росс III
Я в недоумении! Поскольку я не знаю, использует ли Linux 1] или 2], то есть _STD_USING определен в Linux, другими словами, какая группа переносима: типы uint8_t, ..., uint64_t или _CSTD uint8_t, ..., _ CSTD uint64_t?

1] Отрывок из MVS 10.0 stdint.h

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 32bit проблем нет:

; 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 32bit 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 байта, то есть одна оперативная память получает одно сравнение в одной инструкции. К счастью, мне удалось только уменьшить доступ к 3 оперативной памяти memcmp () (применяется к pbPattern, который указывает на 4 или более байтов) до 2, к счастью, для встраивания. Теперь, если я хочу использовать memcmp () на первых 4 байтах pbPattern (как в 2]), ulHashPattern не должен иметь тип register, тогда как 3] не нуждается в таком ограничении.

; 3400 :           if ( !memcmp(&pbTarget[i], &ulHashPattern, 4) )

Строка выше выдает ошибку (ulHashPattern определяется как: зарегистрировать unsigned long ulHashPattern;):

strstr_SHORT-SHOWDOWN.c(3400) : error C2103: '&' on register variable

Да, вы правы: memcmp () сохраняет ситуацию (но с ограничением) - фрагмент 2] идентичен 3] мой грязный стиль. Очевидно, мое стремление не использовать функцию, когда она может быть закодирована вручную, осталось в прошлом, но мне это нравится.

Тем не менее, я не совсем доволен компиляторами, я определил ulHashPattern как переменную регистра, но она загружается каждый раз из ОЗУ ?! Возможно, я что-то упускаю, но эта самая строка (mov eax, DWORD PTR _ulHashPattern $ [esp + 1076]) снижает производительность - на мой взгляд, это ужасный код.

1 Ответ

1 голос
/ 15 января 2012

Чтобы быть строго педантичным, вы можете использовать только тип char.Это потому, что вы нарушаете строгое псевдонимы со следующими типами слов:

*(unsigned long *)(pbPattern);
*(unsigned long *)&pbTarget[i]

char* является единственным исключением из этого правила, поскольку вы можете использовать псевдоним любого типа данных с помощьюchar*.

Если вы включаете свои предупреждения в GCC, вы должны получать строгое псевдоним с вашим фрагментом кода.(AFAIK, MSVC не предупреждает о строгом псевдониме.)


Я не могу точно сказать, что вы пытаетесь сделать в этом фрагменте кода, но идея все еще верна, вы должныне используйте unsigned long или любой другой тип данных для загрузки и сравнения больших порций данных разных типов.

В действительности вам действительно следует использовать memcmp(), так как это простои позволит вам обойти неэффективность приведения всего к char*.

. Есть ли причина, по которой вы не можете использовать memcmp()?


Если вы в порядкес нарушением строгого псевдонима вы можете использовать фиксированные целочисленные типы (например, uint32_t), определенные в <stdint.h>.Тем не менее, имейте в виду, что они фиксируются на количестве # битов, а не # байтах.

...