2010-10-12 7 views
10

Ho una matrice di quattro caratteri senza segno. Voglio trattarlo come un numero a 32 bit (supponiamo che i bit superiori del char non siano utili. Mi interessa solo degli 8 bit più bassi). Quindi, voglio spostarlo in modo circolare da un numero arbitrario di posti. Ho alcune diverse dimensioni di turno, tutte determinate in fase di compilazione.Come circolare spostare un array di 4 caratteri?

E.g.

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

Edit:! A tutti chiedendo perché non ho menzionato CHAR_BIT = 8, perché questo è C. di serie che non ha specificato una piattaforma, allora perché stai assumendo uno?

+2

Perché non riporlo in un 32 bit dato come un int (a seconda della macchina e tutto)? – JoshD

+0

se il char è 16 bit, il tuo esempio è sbagliato, in pratica VUOI trattarli come caratteri a 8 bit, giusto? –

risposta

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); 
    } 
} 

Ovviamente

+1

Questo sembra promettente, permettimi di eseguire alcuni test di verifica. È molto più pulito del mio primo tentativo. –

+2

Vedo che hai assunto little-endian, ma potrebbe essere facilmente modificato in big endian .. –

1

Tenendo a mente pianura C il modo migliore è

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

Uhmm, char è lungo 16 bit, non era chiaro per me. Presumo che lo int sia ancora a 32 bit.

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; 
} 

Oppure puoi semplicemente srotolare questo ciclo.

È possibile scavare ulteriormente nelle istruzioni asm ror, su x86 è in grado di eseguire tale spostamento fino a 31 bit rimanenti. Qualcosa di simile a un

MOV CL, 31 
ROR EAX, CL 
+2

Lo farei, ma CHAR_BIT è 16, quindi l'aliasing di una parola a 32 bit su un char non firmato [4] non funziona. Non posso fare affidamento su funzionalità C non standard, ma grazie per la tua risposta. –

+0

Appena risolto. Qual è la macchina target? – Keynslug

+1

Succede di essere un DSP TI in cui int! = 32-bit, ma non vedo dove ciò sarebbe importante nel codice in ogni caso. È limitato ai turni <= 7? –

-1

Uso union:

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

E 'più sicuro (a causa del puntatore aliasing) e più facile da manipolare.

EDIT: dovresti menzionare prima che il tuo carattere non è 8 bit. Tuttavia, questo dovrebbe fare il trucco:

#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 per l'uso di 'unsigned int', che funziona effettivamente con i dati di test nella domanda. Questo è indipendente da endianness sulla piattaforma? –

+2

Vedere il mio commento sulla risposta precedente. –

+0

Vedo che la tua modifica è solo un array .. mi manca qualcosa? –

Problemi correlati