2010-10-12 3 views
10

J'ai un tableau de quatre caractères non signés. Je veux le traiter comme un nombre de 32 bits (supposons que les bits supérieurs du caractère ne sont pas importants, je me soucie seulement des 8 bits inférieurs). Ensuite, je veux le déplacer circulairement par un nombre arbitraire de places. J'ai quelques tailles de décalage différentes, toutes déterminées au moment de la compilation.Comment faire un décalage circulaire d'un tableau de 4 caractères?

E.g.

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

Modifier: Pour tout le monde se demandant pourquoi je ne l'ai pas mentionné CHAR_BIT = 8, parce que ce standard C. Je ne précise pas une plate-forme, alors pourquoi en supposant que vous?

+2

Pourquoi ne pas stocker dans une donnée 32 bits comme un int (en fonction de la machine et tout)? – JoshD

+0

si char est 16 bits alors votre exemple est faux, fondamentalement vous voulez les traiter comme des caractères de 8 bits, non? –

Répondre

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

De toute évidence

+1

Cela semble prometteur, laissez-moi courir quelques testcases. C'est beaucoup plus propre que ma première tentative. –

+2

Je vois que vous avez supposé little-endian, mais il pourrait être facilement modifié en big endian .. –

1

Tout en gardant à l'esprit clair C est la meilleure façon

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

Hmm, char est de 16 bits de long, n'a pas été clair pour moi. Je présume que int est toujours 32 bits.

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

Ou vous pouvez simplement dérouler ce cycle.

Vous pouvez creuser plus loin dans l'instruction asm ror, sur x86 il est capable d'effectuer un tel décalage jusqu'à 31 bits restants. Quelque chose comme un

MOV CL, 31 
ROR EAX, CL 
+2

Je le ferais, mais CHAR_BIT est 16, donc aliaser un mot de 32 bits au-dessus d'un char non signé [4] ne fonctionne pas. Je ne peux pas compter sur des fonctionnalités C non standard, mais merci pour votre réponse. –

+0

Juste corrigé. Quelle est la machine cible? – Keynslug

+1

Ca va être un TI DSP où int! = 32-bits mais je ne vois pas où cela aurait de l'importance dans votre code de toute façon. Est-ce limité aux changements <= 7? –

-1

Utilisation union:

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

Il est plus sûr (en raison de l'aliasing pointeur) et plus facile à manipuler.

EDIT: vous devriez avoir mentionné plus tôt que votre char n'est pas 8 bits. Toutefois, cela devrait faire l'affaire:

#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 pour l'utilisation de 'unsigned int', qui fonctionne réellement avec les données de test dans la question. Est-ce indépendant de l'endianness sur la plate-forme? –

+2

Voir mon commentaire sur la réponse précédente. –

+0

Je vois que votre édition est juste un tableau .. ai-je oublié quelque chose? –

Questions connexes