comment faire une conversion bit-set / byte-array en c

Étant donné un tableau, unsigned char q[32]="1100111..." ,

comment puis-je générer un ensemble de bits de 4 octets, un signe unsigned char p[4] , tel que le bit de cet ensemble de bits soit égal à une valeur dans le tableau, par exemple, le premier octet p [0] = “q [ 0] … q [7] “; 2ème octet p [1] = “q [8] … q [15]”, etc.

et aussi comment le faire à l’inverse, c’est-à-dire, un ensemble de bits donné, générer le tableau?

mon propre essai pour la première partie.

 unsigned char p[4]={0}; for (int j=0; j<N; j++) { if (q[j] == '1') { p [j / 8] |= 1 << (7-(j % 8)); } } 

Le précédent est-il correct? des conditions à vérifier? Y a-t-il une meilleure façon?

EDIT – 1

Je me demande si ci-dessus est un moyen efficace? Comme la taille du tableau pourrait être jusqu’à 4096 ou même plus.

Je ne pense pas que ça va marcher. Vous comparez chaque “bit” à 1 alors qu’il devrait vraiment être '1' . Vous pouvez également le rendre un peu plus efficace en éliminant le if :

 unsigned char p[4]={0}; for (int j=0; j<32; j++) { p [j / 8] |= (q[j] == `1`) << (7-(j % 8)); } 

Aller en arrière est assez simple aussi. Il suffit de masquer pour chaque "bit" que vous avez défini précédemment.

 unsigned char q[32]={0}; for (int j=0; j<32; j++) { q[j] = p[j / 8] & ( 1 << (7-(j % 8)) ) + '0'; } 

Vous remarquerez l'utilisation créative de (boolean) + '0' pour convertir entre 1/0 et "1" / "0".

Commencez par utiliser strtoul pour obtenir une valeur 32 bits. Puis convertissez l’ordre des octets en big-endian avec htonl . Enfin, stockez le résultat dans votre tableau:

 #include  #include  /* ... */ unsigned char q[32] = "1100111..."; unsigned char result[4] = {0}; *(unsigned long*)result = htonl(strtoul(q, NULL, 2)); 

Il y a aussi d’autres moyens.

Mais il me manque !

Ensuite, vous devez connaître l’ordre des octets de votre plate-forme. Si c’est big endian, alors htonl ne fait rien et peut être omis. Si c’est un peu endian, alors htonl est juste:

 unsigned long htonl(unsigned long x) { x = (x & 0xFF00FF00) >> 8) | (x & 0x00FF00FF) << 8); x = (x & 0xFFFF0000) >> 16) | (x & 0x0000FFFF) << 16); return x; } 

Si vous êtes chanceux, votre optimiseur peut voir ce que vous faites et en faire un code efficace. Sinon, au moins tout est implémentable dans les registres et O (log N).

Si vous ne savez pas quel est l'ordre des octets de votre plate-forme, vous devez le détecter:

 typedef union { char c[sizeof(int) / sizeof(char)]; int i; } OrderTest; unsigned long htonl(unsigned long x) { OrderTest test; test.i = 1; if(!test.c[0]) return x; x = (x & 0xFF00FF00) >> 8) | (x & 0x00FF00FF) << 8); x = (x & 0xFFFF0000) >> 16) | (x & 0x0000FFFF) << 16); return x; } 

Peut-être que long est 8 octets!

Eh bien, l'OP implique des entrées de 4 octets avec leur taille de tableau, mais une long 8 octets est faisable:

 #define kCharsPerLong (sizeof(long) / sizeof(char)) unsigned char q[8 * kCharsPerLong] = "1100111..."; unsigned char result[kCharsPerLong] = {0}; *(unsigned long*)result = htonl(strtoul(q, NULL, 2)); unsigned long htonl(unsigned long x) { #if kCharsPerLong == 4 x = (x & 0xFF00FF00UL) >> 8) | (x & 0x00FF00FFUL) << 8); x = (x & 0xFFFF0000UL) >> 16) | (x & 0x0000FFFFUL) << 16); #elif kCharsPerLong == 8 x = (x & 0xFF00FF00FF00FF00UL) >> 8) | (x & 0x00FF00FF00FF00FFUL) << 8); x = (x & 0xFFFF0000FFFF0000UL) >> 16) | (x & 0x0000FFFF0000FFFFUL) << 16); x = (x & 0xFFFFFFFF00000000UL) >> 32) | (x & 0x00000000FFFFFFFFUL) << 32); #else #error Unsupported word size. #endif return x; } 

Pour les caractères qui ne sont pas 8 bits (les DSP aiment bien le faire), vous êtes seul. (C’est la raison pour laquelle c’était un gros problème lorsque la série de DSP de SHARC comportait des octets de 8 bits; cela rendait BEAUCOUP plus facile le portage du code existant car, en réalité, C faisait un travail horrible de support de la portabilité.)

Qu'en est-il des tampons de longueur arbitraires? Pas de typecasts drôles de pointeur, s'il vous plaît.

La principale chose qui peut être améliorée avec la version de l'OP est de repenser les éléments internes de la boucle. Au lieu de considérer les octets de sortie comme un registre de données fixe, considérez-le comme un registre à décalage, où chaque bit successif est décalé vers l'extrémité droite (LSB). Cela vous épargnera de toutes ces divisions et mods (qui, espérons-le, sont optimisés pour éviter les décalages).

Pour des raisons de santé, je uint8_t unsigned char pour uint8_t .

 #include  unsigned SsortingngToBits(const char* inChars, uint8_t* outBytes, size_t numBytes, size_t* bytesRead) /* Converts the ssortingng of '1' and '0' characters in `inChars` to a buffer of * bytes in `outBytes`. `numBytes` is the number of available bytes in the * `outBytes` buffer. On exit, if `bytesRead` is not NULL, the value it points * to is set to the number of bytes read (rounding up to the nearest full * byte). If a multiple of 8 bits is not read, the last byte written will be * padded with 0 bits to reach a multiple of 8 bits. This function returns the * number of padding bits that were added. For example, an input of 11 bits * will result `bytesRead` being set to 2 and the function will return 5. This * means that if a nonzero value is returned, then a partial byte was read, * which may be an error. */ { size_t bytes = 0; unsigned bits = 0; uint8_t x = 0; while(bytes < numBytes) { /* Parse a character. */ switch(*inChars++) { '0': x <<= 1; ++bits; break; '1': x = (x << 1) | 1; ++bits; break; default: numBytes = 0; } /* See if we filled a byte. */ if(bits == 8) { outBytes[bytes++] = x; x = 0; bits = 0; } } /* Padding, if needed. */ if(bits) { bits = 8 - bits; outBytes[bytes++] = x << bits; } /* Finish up. */ if(bytesRead) *bytesRead = bytes; return bits; } 

Il est de votre responsabilité de vous assurer inChars est à terminaison nulle. La fonction retournera sur le premier caractère différent de '0' ou '1' qu'elle verra ou si elle manque de mémoire tampon de sortie. Quelques exemples d'utilisation:

 unsigned char q[32] = "1100111..."; uint8_t buf[4]; size_t bytesRead = 5; if(SsortingngToBits(q, buf, 4, &bytesRead) || bytesRead != 4) { /* Partial read; handle error here. */ } 

Cela lit seulement 4 octets et intercepte l'erreur s'il ne le peut pas.

 unsigned char q[4096] = "1100111..."; uint8_t buf[512]; SsortingngToBits(q, buf, 512, NULL); 

Ceci convertit simplement ce qu'il peut et définit le rest à 0 bits.

Cette fonction pourrait être améliorée si C avait la capacité de sortir de plus d’un niveau de boucle ou de switch ; dans l'état actuel des choses, je devrais append une valeur de drapeau pour obtenir le même effet, qui est un fouillis, ou je devrais append un goto , ce que je refuse tout simplement.

Selon votre exemple, il ne semble pas que vous visiez la lisibilité, et après un rafraîchissement (tardif), ma solution est très similaire à celle de Chriszuma, à l’exception de l’absence de parenthèse due à l’ordre des opérations et à l’ajout de la valeur !! appliquer un 0 ou un 1.

 const size_t N = 32; //N must be a multiple of 8 unsigned char q[N+1] = "11011101001001101001111110000111"; unsigned char p[N/8] = {0}; unsigned char r[N+1] = {0}; //reversed for(size_t i = 0; i < N; ++i) p[i / 8] |= (q[i] == '1') << 7 - i % 8; for(size_t i = 0; i < N; ++i) r[i] = '0' + !!(p[i / 8] & 1 << 7 - i % 8); printf("%x %x %x %x\n", p[0], p[1], p[2], p[3]); printf("%s\n%s\n", q,r); 

Si vous recherchez une efficacité extrême, essayez les techniques suivantes:

Remplacez if par soustraction de '0' (il semble que vous puissiez supposer que vos symboles d’entrée ne peuvent être que 0 ou 1 ). Traitez également les entrées des indices les plus bas aux plus élevées.

 for (int c = 0; c < N; c += 8) { int y = 0; for (int b = 0; b < 8; ++b) y = y * 2 + q[c + b] - '0'; p[c / 8] = y; } 

Remplacez les index de tableau en incrémentant automatiquement des pointeurs

 const char* qptr = q; unsigned char* pptr = p; for (int c = 0; c < N; c += 8) { int y = 0; for (int b = 0; b < 8; ++b) y = y * 2 + *qptr++ - '0'; *pptr++ = y; } 

Déroulez la boucle intérieure:

 const char* qptr = q; unsigned char* pptr = p; for (int c = 0; c < N; c += 8) { *pptr++ = qptr[0] - '0' << 7 | qptr[1] - '0' << 6 | qptr[2] - '0' << 5 | qptr[3] - '0' << 4 | qptr[4] - '0' << 3 | qptr[5] - '0' << 2 | qptr[6] - '0' << 1 | qptr[7] - '0' << 0; qptr += 8; } 

Traitez plusieurs caractères en même temps (avec des instructions bidirectionnelles ou des instructions MMX), ce qui représente un potentiel d’accélération considérable!