给定阵列,
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
我想知道上述是否有效?因为阵列大小可能高达4096甚至更多。
答案 0 :(得分:3)
首先,使用strtoul
获取32位值。然后使用htonl
将字节顺序转换为big-endian。最后,将结果存储在数组中:
#include <arpa/inet.h>
#include <stdlib.h>
/* ... */
unsigned char q[32] = "1100111...";
unsigned char result[4] = {0};
*(unsigned long*)result = htonl(strtoul(q, NULL, 2));
还有其他方法。
<arpa/inet.h>
!然后你需要知道你的平台是什么字节顺序。如果它是大端,那么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)端。这样可以避免所有这些分区和模块(希望这些分区和模块可以优化到位移)。
为了理智,我放弃了unsigned char
uint8_t
。
#include <stdint.h>
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
,则可以更好地完成此功能。就目前而言,我必须添加一个标志值来获得相同的效果,这是混乱的,或者我必须添加一个goto
,我只是拒绝。
答案 1 :(得分:2)
我认为这不会很有效。您应该将每个“位”与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'之间进行转换。
答案 2 :(得分:1)
根据你的例子,看起来你的可读性看起来不像,并且在(后期)刷新之后,我的解决方案与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);
答案 3 :(得分:1)
如果您正在寻找极高的效率,请尝试使用以下技术:
通过减去if
来替换'0'
(似乎您可以假设您的输入符号只能是0
或1
)。
还处理从较低指数到较高指数的输入。
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指令) - 这有很大的加速潜力!