C / C ++包装的符号字符转换成int
题
我需要包装4个符号字节成32位整型。 这是我走过来:
int32_t byte(int8_t c) { return (unsigned char)c; }
int pack(char c0, char c1, ...) {
return byte(c0) | byte(c1) << 8 | ...;
}
这是一个很好的解决方案?它是可移植的(不是在通信意义上的)? 有没有现成的溶液,也许提高?
问题我主要关心的是,从焦炭到int负位的转换时,位顺序。我不知道正确的行为应该是什么。
由于
解决方案
我喜欢乔伊亚当的回答除了它与宏(造成一个真正的痛苦在许多情况下)写的事实,编译器不会给你一个警告,如果“字符”不是1个字节宽。这是我的溶液(基于关乔伊的)。
inline uint32_t PACK(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) {
return (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
}
inline uint32_t PACK(sint8_t c0, sint8_t c1, sint8_t c2, sint8_t c3) {
return PACK((uint8_t)c0, (uint8_t)c1, (uint8_t)c2, (uint8_t)c3);
}
换挡时我省略铸造C0-> C3到uint32_t的,因为编译器应处理此为你,我使用的c-风格转换,因为它们将用于任一C或C ++(标记为两个OP)工作。
其他提示
char
不能保证被符号或无符号(在PowerPC的Linux,炭默认为无符号强>)。宣传!
你需要的是这样的宏:
#include <stdint.h> /* Needed for uint32_t and uint8_t */
#define PACK(c0, c1, c2, c3) \
(((uint32_t)(uint8_t)(c0) << 24) | \
((uint32_t)(uint8_t)(c1) << 16) | \
((uint32_t)(uint8_t)(c2) << 8) | \
((uint32_t)(uint8_t)(c3)))
主要是因为它不使用C的操作顺序发挥好它的丑陋。此外,反斜杠回报有那么该宏不必须是一个大的长行。
此外,我们投铸造uint32_t的前uint8_t的原因是为了防止不必要的符号扩展。
可以避免铸件与隐式转换:
uint32_t pack_helper(uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3) {
return c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);
}
uint32_t pack(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) {
return pack_helper(c0, c1, c2, c3);
}
我们的想法是,你看到“正确地转换所有的参数。按住Shift键并结合他们”,而不是“为每个参数,正确地将其转换,移位和结合起来”。没有多少,虽然。
然后:
template <int N>
uint8_t unpack_u(uint32_t packed) {
// cast to avoid potential warnings for implicit narrowing conversion
return static_cast<uint8_t>(packed >> (N*8));
}
template <int N>
int8_t unpack_s(uint32_t packed) {
uint8_t r = unpack_u<N>(packed);
return (r <= 127 ? r : r - 256); // thanks to caf
}
int main() {
uint32_t x = pack(4,5,6,-7);
std::cout << (int)unpack_u<0>(x) << "\n";
std::cout << (int)unpack_s<1>(x) << "\n";
std::cout << (int)unpack_u<3>(x) << "\n";
std::cout << (int)unpack_s<3>(x) << "\n";
}
输出:
4
5
249
-7
这是作为uint32_t
,uint8_t
和int8_t
类型作为便携式的。他们中没有需要在C99,并且头部stdint.h未在C ++或C89定义。如果存在类型,满足C99的要求,不过,该代码将工作。当然,在C中的解包功能将需要一个函数参数,而不是一个模板参数。你可能更喜欢在C ++太多,如果你想要写短环路拆包。
要解决的事实,类型是可选的,你可以使用uint_least32_t
,这是在C99要求。同样uint_least8_t
和int_least8_t
。你将不得不改变pack_helper和unpack_u的代码:
uint_least32_t mask(uint_least32_t x) { return x & 0xFF; }
uint_least32_t pack_helper(uint_least32_t c0, uint_least32_t c1, uint_least32_t c2, uint_least32_t c3) {
return mask(c0) | (mask(c1) << 8) | (mask(c2) << 16) | (mask(c3) << 24);
}
template <int N>
uint_least8_t unpack_u(uint_least32_t packed) {
// cast to avoid potential warnings for implicit narrowing conversion
return static_cast<uint_least8_t>(mask(packed >> (N*8)));
}
要诚实,这是不太可能是值得的 - 机会是你的应用程序的其余部分上int8_t
等确实存在的假设写的。这是一种罕见的实现,它不具有8位和32位2的补码类型。
<强> “善”强>结果
恕我直言,这是你要获得这个最佳的解决方案。编辑:虽然我会使用static_cast<unsigned int>
代替C风格的演员,我很可能不会使用一个单独的方法来隐藏投....
<强>可移植性:强>结果
这里将是这样做没有可移植的方法,因为没有人说char
必须为8位,并没有说unsigned int
必须是4个字节宽。
此外,您依靠端标记,并因此在一个体系结构数据pack'd将无法使用上一个与对面的端序。
<强>有没有现成的溶液,也许提高吗结果 这不是我所知道。
此是基于格兰特Peters和乔伊亚当斯的答案,延伸到显示如何解压符号值(解包的功能依赖于无符号值C中的模数规则):
(正如史蒂夫杰索普在注释中指出的那样,就没有必要单独pack_s
和pack_u
函数)。
inline uint32_t pack(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3)
{
return ((uint32_t)c0 << 24) | ((uint32_t)c1 << 16) |
((uint32_t)c2 << 8) | (uint32_t)c3;
}
inline uint8_t unpack_c3_u(uint32_t p)
{
return p >> 24;
}
inline uint8_t unpack_c2_u(uint32_t p)
{
return p >> 16;
}
inline uint8_t unpack_c1_u(uint32_t p)
{
return p >> 8;
}
inline uint8_t unpack_c0_u(uint32_t p)
{
return p;
}
inline uint8_t unpack_c3_s(uint32_t p)
{
int t = unpack_c3_u(p);
return t <= 127 ? t : t - 256;
}
inline uint8_t unpack_c2_s(uint32_t p)
{
int t = unpack_c2_u(p);
return t <= 127 ? t : t - 256;
}
inline uint8_t unpack_c1_s(uint32_t p)
{
int t = unpack_c1_u(p);
return t <= 127 ? t : t - 256;
}
inline uint8_t unpack_c0_s(uint32_t p)
{
int t = unpack_c0_u(p);
return t <= 127 ? t : t - 256;
}
(这些都是必要而不是简单地铸造回int8_t
,因为后者可能导致如果该值超过127被升高实现定义信号,所以它不是严格的便携式)。
您也可以让编译器做的工作适合你。
union packedchars {
struct {
char v1,v2,v3,v4;
}
int data;
};
packedchars value;
value.data = 0;
value.v1 = 'a';
value.v2 = 'b;
等