2010-10-12 12 views
10

Tengo una matriz de cuatro caracteres sin firmar. Quiero tratarlo como un número de 32 bits (supongamos que a los bits superiores del char no les importa. Solo me importan los 8 bits inferiores). Luego, quiero desplazarlo circularmente por una cantidad arbitraria de lugares. Tengo algunos tamaños de cambio diferentes, todos determinados en tiempo de compilación.¿Cómo circular una matriz de 4 caracteres?

E.g.

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

Editar: A todo el mundo se pregunta por qué no he mencionado CHAR_BIT = 8, porque esto es C. norma que no especificó una plataforma, por lo que ¿por qué suponer una?

+2

Por lo guarde en un dato de 32 bits como un int (dependiendo de la máquina y todo)? – JoshD

+0

si char es de 16 bits, entonces su ejemplo es incorrecto, básicamente QUIERES tratarlos como caracteres de 8 bits, ¿verdad? –

Respuesta

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

Obviamente

+1

Esto parece prometedor, déjame ejecutar algunos testcases. Es mucho más limpio que mi primer intento. –

+2

Veo que asumiste little-endian, pero podría ser fácilmente modificado para big endian ... –

1

Sin olvidar C plano la mejor manera es

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

Uhmm, char es de 16 bits de largo, no estaba claro para mí. Supongo que int sigue siendo de 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; 
} 

O simplemente podría desenrollar este ciclo.

Puede profundizar en la instrucción asm ror, en x86 es capaz de realizar dicho cambio hasta 31 bits restantes. Algo así como una

MOV CL, 31 
ROR EAX, CL 
+2

Haría esto, pero CHAR_BIT es 16, por lo que aliasar una palabra de 32 bits sobre un carácter sin signo [4] no funciona. No puedo confiar en las características C no estándar, pero gracias por su respuesta. –

+0

Apenas arreglado. ¿Cuál es la máquina de destino? – Keynslug

+1

Ocurre que es un DSP de TI donde int! = 32 bits pero de todos modos no veo dónde eso podría importar en tu código. ¿Está restringido a cambios <= 7? –

-1

Uso union:

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

Es más seguro (a causa de aliasing puntero) y más fácil de manipular.

EDITAR: debería haber mencionado anteriormente que su char no es de 8 bits. Sin embargo, esto debe hacer el truco:

#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 para el uso de 'unsigned int', que realmente funciona con datos de prueba en la pregunta. ¿Es esto independiente de endianness en la plataforma? –

+2

Ver mi comentario en la respuesta anterior. –

+0

Veo que su edición es solo una matriz. ¿Me falta algo? –

Cuestiones relacionadas