2010-10-12 25 views
10

Mam tablicę czterech niepodpisanych znaków. Chcę traktować ją jak 32-bitową liczbę (zakładam, że górne bity char nie obchodzą mnie, tylko dbam o niższe 8 bitów). Następnie chcę cyklicznie przesunąć go o dowolną liczbę miejsc. Mam kilka różnych rozmiarów zmian, wszystkie określone podczas kompilacji.Jak przesunąć okrąg w tablicy 4 znaków?

E.g.

unsigned char a[4] = {0x81, 0x1, 0x1, 0x2}; 
circular_left_shift(a, 1); 
/* a is now { 0x2, 0x2, 0x2, 0x5 } */ 

Edit: Aby wszyscy zastanawiać, dlaczego nie wspomnieć CHAR_BIT = 8, ponieważ jest to standardowy C. I nie określił platformę, więc dlaczego zakładając jeden?

+2

Dlaczego nie przechowywać go w 32-bitowym układzie odniesienia, takich jak int (w zależności od urządzenia i wszystkich)? – JoshD

+0

jeśli char jest 16-bitowy, to twój przykład jest zły, w zasadzie chcesz traktować je jako 8-bitowe znaki, prawda? –

Odpowiedz

5
static void rotate_left(uint8_t *d, uint8_t *s, uint8_t bits) 
{ 
    const uint8_t octetshifts = bits/8; 
    const uint8_t bitshift = bits % 8; 
    const uint8_t bitsleft = (8 - bitshift); 
    const uint8_t lm = (1 << bitshift) - 1; 
    const uint8_t um = ~lm; 
    int i; 

    for (i = 0; i < 4; i++) 
    { 
     d[(i + 4 - octetshifts) % 4] = 
      ((s[i] << bitshift) & um) | 
      ((s[(i + 1) % 4] >> bitsleft) & lm); 
    } 
} 

Oczywiście

+1

To wygląda obiecująco, pozwól mi uruchomić kilka testcases. Jest o wiele czystsza niż moja pierwsza próba. –

+2

Widzę, że założyłeś mały endianin, ale można go łatwo zmodyfikować na dużego endiana ... –

1

, pamiętając zwykły C Najlepszym sposobem jest

inline void circular_left_shift(char *chars, short shift) { 
    __int32 *dword = (__int32 *)chars; 
    *dword = (*dword << shift) | (*dword >> (32 - shift)); 
} 

Uhmm, char wynosi 16 bitów, nie było jasne dla mnie. Przypuszczam, że int nadal jest 32-bitowy.

inline void circular_left_shift(char *chars, short shift) { 
    int i, part; 
    part = chars[0] >> (16 - shift); 
    for (i = 0; i < 3; ++i) 
     chars[i] = (chars[i] << shift) | (chars[i + 1] >> (16 - shift)); 
    chars[3] = (chars[3] << shift) | part; 
} 

Albo można po prostu odprężyć ten cykl.

Możesz dalej przekopywać się do instrukcji asm ror, na x86 jest w stanie wykonać takie przesunięcie do 31 bitów w lewo. Coś jak

MOV CL, 31 
ROR EAX, CL 
+2

Zrobiłbym to, ale CHAR_BIT ma 16, więc aliasing 32-bitowe słowo na wierzchu unsigned char [4] nie działa. Nie mogę polegać na niestandardowych funkcjach C, ale dzięki za odpowiedź. –

+0

Naprawione. Czym jest maszyna docelowa? – Keynslug

+1

Zdarza się, że jest to DSP, gdzie int! = 32-bitowe, ale nie widzę, gdzie by to miało znaczenie w kodzie. Czy jest to ograniczone do przesunięć <= 7? –

-1

Zastosowanie union:

typedef union chr_int{ 
    unsigned int i; 
    unsigned char c[4]; 
}; 

bezpieczniej (ze względu na wskaźnik aliasing) i łatwiej manipulować.

EDYTOWANIE: powinieneś wcześniej wspomnieć, że twój znak nie ma 8 bitów. Jednakże, należy to zrobić trick:

#define ORIG_MASK 0x81010102 
#define LS_CNT 1 

unsigned char a[4] = { 
    ((ORIG_MASK << LS_CNT  ) | (ORIG_MASK >> (32 - LS_CNT))) & 0xff, 
    ((ORIG_MASK << (LS_CNT + 8)) | (ORIG_MASK >> (24 - LS_CNT))) & 0xff, 
    ((ORIG_MASK << LS_CNT + 16)) | (ORIG_MASK >> (16 - LS_CNT))) & 0xff, 
    ((ORIG_MASK << (LS_CNT + 24)) | (ORIG_MASK >> (8 - LS_CNT))) & 0xff 
}; 
+1

+1 dla użycia 'unsigned int', które faktycznie działa z danymi testowymi w pytaniu. Czy jest to niezależne od endianizmu na platformie? –

+2

Zobacz mój komentarz na temat poprzedniej odpowiedzi. –

+0

Widzę, że twoja edycja to tylko tablica ... czy coś mi brakuje? –