gpt4 book ai didi

c - 如何在 C 中进行位集/字节数组转换

转载 作者:行者123 更新时间:2023-12-01 14:01:55 24 4
gpt4 key购买 nike

给定一个数组,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 甚至更多。

最佳答案

首先,使用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;
}

对于 char那不是 8 位(DSP 喜欢这样做),你只能靠自己了。 (这就是为什么当 SHARC 系列 DSP 具有 8 位字节时它是一件大事;它使移植现有代码变得容易得多,因为面对现实,C 在可移植性支持方面做得很糟糕。)

任意长度的缓冲区呢?请不要使用有趣的指针类型转换。

OP 版本可以改进的主要内容是重新考虑循环的内部结构。与其将输出字节视为固定数据寄存器,不如将其视为移位寄存器,其中每个连续的位都移入右 (LSB) 端。这将使您免于所有这些部门和 mod(希望这些部门和 mod 已针对位移进行了优化)。

为了理智,我放弃了 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 ,我只是拒绝。

关于c - 如何在 C 中进行位集/字节数组转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7730472/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com