将字节转换为c

时间:2016-06-03 22:44:24

标签: c algorithm hex decimal

看看这段代码:

#include <stdio.h>
#include <stdlib.h>

int byteToInt(char *bytes) {
    int32_t v = 
        (bytes[0]      ) +
        (bytes[1] << 8 ) +
        (bytes[2] << 16) +
        (bytes[3] << 24);
    return v;
}

int main() {
    char b1[] = {0xec, 0x51, 0x04, 0x00};
    char b2[] = {0x0c, 0x0c, 0x00, 0x00};

    printf("%d\n", byteToInt(b1));
    printf("%d\n", byteToInt(b2));
    printf("%d\n", *(uint32_t *)b1);
    printf("%d\n", *(uint32_t *)b2);

    return 0;
}

{0xec, 0x51, 0x04, 0x00}等于283116,但当我使用byteToInt函数时,由于某种原因,它会返回282860。有一些字节数组会导致类似的麻烦。我意识到这个值总是误以为256.尽管如此,大多数案例都没有任何问题 - 只需看一下b2,它就会被计算为3084,这是正确的。铸造方法在这些情况下很有效,但我想知道所描述的问题是什么。有人可以向我解释一下吗?

2 个答案:

答案 0 :(得分:4)

或许char是签名类型(由实现定义),(int)(char)(0xec)-20,而(int)(unsigned char)(0xec)236

尝试使用unsigned charuint32_t

uint32_t byteToInt(unsigned char *bytes) {
    uint32_t v =
        ((uint32_t)bytes[0]) +
        ((uint32_t)bytes[1] << 8) +
        ((uint32_t)bytes[2] << 16) +
        ((uint32_t)bytes[3] << 24);
    return v;
}

int main() {
    unsigned char b1[] = { 0xec, 0x51, 0x04, 0x00 };
    unsigned char b2[] = { 0x0c, 0x0c, 0x00, 0x00 };

    printf("%u\n", byteToInt(b1));     // 'u' for unsigned
    printf("%u\n", byteToInt(b2));
    //printf("%u\n", *(uint32_t *)b1); // undefined behavior
    //printf("%u\n", *(uint32_t *)b2); // ditto

    return 0;
}

请注意,在最后两个printf中重新解释内存内容是未定义的行为(尽管通常在实践中有效)。

顺便说一句,根据标准,未定义有符号的负值:

  

E1 << E2的结果是E1左移E2位位置; ...   如果E1已签名   类型和非负值,E1 × 2 E2 在结果类型中可表示,那么   结果价值;否则,行为未定义

答案 1 :(得分:0)

此代码存在一些潜在问题。第一个是编译器依赖于char类型是8位,16位还是32位。当您对字符类型执行移位操作时,它可能会丢失位而不是#34;价值。

在移动它们并添加它们之前,首先将值转换为32位类型更安全。例如:

unsigned long v = 
    ((unsigned long)bytes[0]      ) +
    ((unsigned long)bytes[1] << 8 ) +
    ((unsigned long)bytes[2] << 16) +
    ((unsigned long)bytes[3] << 24);

您对int32_t的使用也依赖于编译器。如果内存服务,那就是特定于Windows的int重新分类。 &#34; INT&#34;本身是编译器相关的,较旧的编译器可能将其作为16位值,因为标准表示它应该是您正在处理的机器上的单词大小。使用&#34; long&#34;而不是&#34; int&#34;保证32位值。

另外,我使用&#34; unsigned long&#34;在这个例子中,因为在这种情况下,我不认为你想要处理负数。在二进制表示中,负数具有最高位集(0x8000000)。

如果你想使用负数,那么类型应该是&#34; long&#34;相反,虽然这会在将正值字节添加到负值最大字节时打开不同的蠕虫。在你想要处理负数的情况下,你应该做一个完全不同的转换,剥离高字节的高位,进行加法,然后,如果设置了高位,则使值为负(v = -v;),然后你需要减去1,因为负数的表示(这可能超出了这个问题的范围。)

修改后的代码将是:

#include <stdio.h>
#include <stdlib.h>

unsigned long byteToInt(char *bytes) {
    unsigned long v = 
        ((unsigned long)bytes[0]      ) +
        ((unsigned long)bytes[1] << 8 ) +
        ((unsigned long)bytes[2] << 16) +
        ((unsigned long)bytes[3] << 24);
    return v;
}

int main() {
    char b1[] = {0xec, 0x51, 0x04, 0x00};
    char b2[] = {0x0c, 0x0c, 0x00, 0x00};

    printf("%d\n", byteToInt(b1));
    printf("%d\n", byteToInt(b2));
    printf("%d\n", *(unsigned long *)b1);
    printf("%d\n", *(unsigned long *)b2);

    return 0;
}