如何在c中进行位设置/字节数组转换

给定一个数组, unsigned char q[32]="1100111..."

如何生成一个4字节的位集unsigned char p[4] ,这样,该位集的位等于数组内的值,例如,第一个字节p [0] =“q [ 0] … q [7]“; 第二个字节p [1] =“q [8] … q [15]”等。

以及如何在相反的情况下,即给定位集,生成数组?

我自己的第一部分试用了。

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

以上是对的吗? 有条件要检查吗? 有没有更好的方法?

编辑 – 1

我想知道以上是否是有效的方式? 由于arrays大小可能高达4096甚至更多。

我认为这不会很有效。 当它真的应该为'1'时,你将每个“位”与1进行比较。 您还可以通过删除if提高效率:

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

反过来也很简单。 只需屏蔽您之前设置的每个“位”。

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

你会注意到(boolean) + '0'的创造性使用在1/0和'1'/'0'之间转换。

首先,使用strtoul获取32位值。 然后使用htonl将字节顺序转换为big-endian。 最后,将结果存储在数组中:

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

还有其他方法。

但我缺少

然后你需要知道你的平台是什么字节顺序。 如果它是大端,那么htonl什么都不做,可以省略。 如果它是小端,那么htonl只是:

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

如果你很幸运,你的优化器可能会看到你正在做的事情并将其变成有效的代码。 如果没有,那么,至少它可以在寄存器和O(log N)中实现。

如果您不知道平台的字节顺序,那么您需要检测它:

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

也许多long 8个字节!

那么,OP暗示了4字节输入及其数组大小,但是8字节long是可行的:

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

对于不是8位的char (DSP喜欢这样做),你可以独立完成。 (这就是为什么当SHARC系列DSP具有8位字节时它是一个大交易;它使得移植现有代码变得更容易,因为面对它,C做了可移植性支持的可怕工作。)

任意长度的缓冲区怎么样? 请不要搞笑指针。

OP版本可以改进的主要内容是重新考虑循环的内部结构。 不要将输出字节视为固定数据寄存器,而应将其视为移位寄存器,其中每个连续位移入右(LSB)端。 这将使您免于所有这些分区和模块(希望这些分区和模块可以优化到位移)。

为了理智,我正在为uint8_t抛弃unsigned char

 #include  unsigned StringToBits(const char* inChars, uint8_t* outBytes, size_t numBytes, size_t* bytesRead) /* Converts the string 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; } 

您有责任确保inChars以空值终止。 该函数将返回它看到的第一个非'0''1'字符,或者它是否用完了输出缓冲区。 一些示例用法:

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

这只读取4个字节,如果不能,则捕获错误。

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

这只是转换它可以将其余部分设置为0位。

如果C能够break多个级别的循环或switch ,则可以更好地完成此function; 就目前而言,我必须添加一个标志值以获得相同的效果,这是混乱的,或者我必须添加一个goto ,我只是拒绝。

根据你的例子,看起来你看起来并不是为了可读性,并且在(后期)刷新之后,我的解决方案看起来与Chriszuma非常相似,除了由于操作顺序和添加!!而没有括号。 强制执行0或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); 

如果您正在寻找极高的效率,请尝试使用以下技术:

通过减去'0'替换if (似乎您可以假设您的输入符号只能是01 )。 还处理从较低索引到较高索引的输入。

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

通过自动递增指针替换数组索引:

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

展开内循环:

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

同时处理几个输入字符(使用bit twiddling hacks或MMX指令) - 这具有很大的加速潜力!