从char*数组中读取“整数”大小的字节。

15

我想从一个 char* 数组中读取 sizeof(int) 个字节。

a) 在哪些情况下需要担心需要检查字节序?

b) 如何读取前4个字节,无论是否考虑字节序。

编辑 : 我已经读取了 sizeof(int) 字节,需要将其与一个整数值进行比较。

如何最好地解决这个问题?


我有一点困惑于你在尝试做什么。 你能写一些伪代码作为示例吗? 你是在尝试从字符数组中解析整数吗? - William Brendel
我正在尝试从char*数组中找到sizeof(int)字节,并尝试将其与整数进行比较。数据的来源是另一台机器。 - kal
9个回答

20

你的意思是像这样吗?

char* a;
int i;
memcpy(&i, a, sizeof(i));

只有当数据来源于不同的平台(比如设备)时,才需要担心字节顺序问题。


还有什么比这更明显的呢? :D - Ярослав Рахматуллин
这是一种不违反类型游戏/别名规则的好方法。对于那些想知道“但它看起来比转换慢!”的人,a)转换是未定义的行为,所以不要去那里b)生成的代码在x86 / x64上没有区别:https://godbolt.org/g/gxtVFZ - Eloff

10

a) 如果数据是由大端机器创建并在小端机器上处理,或者反之,则只需关注“字节顺序”(即字节交换)问题。这种情况有很多种可能性,以下是其中的几个示例:

  1. 您通过套接字在Windows机器上接收数据。Windows采用小端架构,而网络数据“应该”以大端格式存在。
  2. 您处理文件数据,该文件是在具有不同“字节顺序”的系统上创建的。

在任一情况下,您都需要对大于1个字节的所有数字执行字节交换,例如shorts、ints、longs、doubles等。但是,如果您始终处理来自同一平台的数据,则字节顺序问题就不是问题了。

b) 根据您的问题,似乎您有一个char指针,并希望提取前4个字节作为int,然后再处理任何字节顺序问题。要执行提取,请使用以下代码:

int n = *(reinterpret_cast<int *>(myArray)); // where myArray is your data

显然,这里假定myArray不是空指针;否则,它会崩溃,因为它对指针进行了解引用,所以要采用良好的防御性编程策略。

在Windows上交换字节,您可以使用winsock2.h中定义的ntohs()/ntohl()和/或htons()/htonl()函数。或者,您可以编写一些简单的C++程序来执行此操作,例如:

inline unsigned short swap_16bit(unsigned short us)
{
    return (unsigned short)(((us & 0xFF00) >> 8) |
                            ((us & 0x00FF) << 8));
}

inline unsigned long swap_32bit(unsigned long ul)
{
    return (unsigned long)(((ul & 0xFF000000) >> 24) |
                           ((ul & 0x00FF0000) >>  8) |
                           ((ul & 0x0000FF00) <<  8) |
                           ((ul & 0x000000FF) << 24));
}

1
你应该说明第一段代码片段与Daniel的代码存在相同的问题:它可以访问不适合int*的未对齐数据。 - Johannes Schaub - litb
这是我在Java中唯一缺少的东西。至少能够从字节数组中读取一个int将会非常棒。也许我会在我的JVM实现中实现一些字节码操作来解决这个问题。 - neoexpert

3

根据您想要如何读取它们,我感觉您想将4个字节转换为整数,如果在网络流数据上这样做,通常会得到以下结果:

int foo = *(int*)(stream+offset_in_stream);

1
这可能导致未对齐的访问。 - gimpf
即在80486和任何设置了对齐标志的更好的CPU上。 - Bodo Thiesen
对齐标志位何时被设置? - Rob Kennedy
在 iPhone 的发布版本中,这会导致崩溃! - AlexWien
为了兼容性,x86允许未对齐访问,但会有速度惩罚,但大多数(如果不是全部)现代架构如MIPS、SPARC、ARM等都禁止未对齐访问。在x86中,即使大多数SIMD指令(除了move unaligned)也需要数据对齐。 - phuclv
显示剩余2条评论

3
解决这个问题的简单方法是确保生成字节的工具在一致的字节序下进行。通常使用各种TCP/IP协议中的“网络字节序”最佳,库例程htonlntohl可以很好地处理它们,并且它们通常被优化得相当不错。
然而,如果未使用网络字节序,则可能需要以其他方式处理。您需要知道两件事:整数的大小和字节顺序。一旦您知道了这一点,就知道要提取多少字节以及以哪种顺序将它们组合成一个整数。
以下是一些示例代码,假设sizeof(int)是正确的字节数:
#include <limits.h>

int bytes_to_int_big_endian(const char *bytes)
{
    int i;
    int result;

    result = 0;
    for (i = 0; i < sizeof(int); ++i)
        result = (result << CHAR_BIT) + bytes[i];
    return result;
}

int bytes_to_int_little_endian(const char *bytes)
{
    int i;
    int result;

    result = 0;
    for (i = 0; i < sizeof(int); ++i)
        result += bytes[i] << (i * CHAR_BIT);
    return result;
}


#ifdef TEST

#include <stdio.h>

int main(void)
{
    const int correct = 0x01020304;
    const char little[] = "\x04\x03\x02\x01";
    const char big[] = "\x01\x02\x03\x04";

    printf("correct: %0x\n", correct);
    printf("from big-endian: %0x\n", bytes_to_int_big_endian(big));
    printf("from-little-endian: %0x\n", bytes_to_int_little_endian(little));
    return 0;
}

#endif

现在将“int”替换为“unsigned”,你的答案就正确了 ;) - Bodo Thiesen
1
我会分别用 | 和 |= 替换 + 和 +=。在这里使用数学运算符很令人困惑,我个人认为。 - Johannes Schaub - litb

3

你能行吗?

int int_from_bytes(const char * bytes, _Bool reverse)
{
    if(!reverse)
        return *(int *)(void *)bytes;

    char tmp[sizeof(int)];

    for(size_t i = sizeof(tmp); i--; ++bytes)
        tmp[i] = *bytes;

    return *(int *)(void *)tmp;
}

您可以像这样使用它:

int i = int_from_bytes(bytes, SYSTEM_ENDIANNESS != ARRAY_ENDIANNESS);

如果您的系统中将 void * 转换为 int * 可能会导致对齐冲突,可以使用下面的方法:

int int_from_bytes(const char * bytes, _Bool reverse)
{
    int tmp;

    if(reverse)
    {
        for(size_t i = sizeof(tmp); i--; ++bytes)
            ((char *)&tmp)[i] = *bytes;
    }
    else memcpy(&tmp, bytes, sizeof(tmp));

    return tmp;
}

1
只有在读取的数据由大于一个字节的数字组成时,您才需要担心字节序问题。
如果您正在读取 sizeof(int) 个字节并希望将它们解释为 int,则字节序会产生影响。本质上,endianness 是一台机器将多个字节解释为数值的方式。

1

只需使用一个for循环,以sizeof(int)块移动数组。
使用函数ntohl(在头文件<arpa/inet.h>中找到,至少在Linux上),将字节从网络顺序(网络顺序定义为大端)转换为本地字节顺序。该库函数被实现为执行适用于您正在运行的任何处理器的正确网络到主机转换。


当然,这仅适用于您实际从网络中读取某些内容的情况... - gimpf
好的,他在“注释(comment)”中说明他是从另一台机器上读取的。也许是通过刻录/读取光盘完成的,但更可能是指某种网络方式。 - gimpf

1

你不必担心字节序,除非你正在读取来自不同机器的源数据,例如网络流。

既然如此,难道你不能只是使用for循环吗?

void ReadBytes(char * stream) {
    for (int i = 0; i < sizeof(int); i++) {
        char foo = stream[i];
        }
    }
 }

你是在问比那更复杂的东西吗?


我的数据实际上来自不同的来源。 - kal

1

为什么要阅读,当你可以直接比较呢?

bool AreEqual(int i, char *data)
{
   return memcmp(&i, data, sizeof(int)) == 0;
}

如果你需要将所有整数转换为某个不变的形式,而又担心字节序问题,那么htonl和ntohl是很好的例子。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接