15

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

a)如果需要检查字节顺序,我们需要担心什么情况?

b)考虑或不考虑字节顺序,您将如何读取前 4 个字节。

编辑:我读过的sizeof(int)字节需要与一个整数值进行比较。

解决这个问题的最佳方法是什么

4

9 回答 9

20

你的意思是这样的吗?:

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

如果数据源来自不同的平台(如设备),您只需要担心字节顺序。

于 2009-02-13T06:47:26.387 回答
10

a) 如果数据是在大端机器上创建并在小端机器上处理的,您只需要担心“字节顺序”(即字节交换),反之亦然。发生这种情况的方式有很多,但这里有几个例子。

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

在任何一种情况下,您都需要对所有大于 1 字节的数字进行字节交换,例如,shorts、ints、longs、doubles 等。但是,如果您总是处理来自同一平台的数据,则使用 endian问题是无关紧要的。

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));
}
于 2009-02-13T07:10:12.837 回答
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;
}
于 2009-02-13T11:08:31.080 回答
3

取决于你想如何阅读它们,我觉得你想将 4 个字节转换为一个整数,通过网络流数据这样做通常会以这样的方式结束:

int foo = *(int*)(stream+offset_in_stream);
于 2009-02-13T06:45:33.927 回答
3

解决此问题的简单方法是确保生成字节的任何内容都以一致的字节顺序进行。通常,各种 TCP/IP 东西使用的“网络字节顺序”是最好的:库例程htonlntohl可以很好地处理这个问题,而且它们通常都得到了很好的优化。

但是,如果没有使用网络字节顺序,您可能需要以其他方式执行操作。您需要知道两件事:整数的大小和字节顺序。一旦知道了这一点,您就知道要提取多少字节以及以何种顺序将它们组合成一个 int。

假设 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
于 2009-02-13T07:10:59.060 回答
1

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

于 2009-02-13T06:48:50.890 回答
1

除非您从不同机器上创建的源(例如网络流)读取字节,否则您不必担心字节顺序。

鉴于此,您不能只使用 for 循环吗?

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

您是否要求比这更复杂的东西?

于 2009-02-13T06:38:52.397 回答
1

只有当您读取的数据由大于一个字节的数字组成时,您才需要担心字节顺序。
如果您正在读取 sizeof(int) 字节并期望将它们解释为 int 那么字节序会有所不同。本质上,字节序是机器将超过 1 个字节的序列解释为数值的方式。

于 2009-02-13T06:43:47.610 回答
1

当您可以比较时,为什么还要阅读?

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

如果您在需要将所有整数转换为某种不变形式时担心字节顺序。htonl 和 ntohl 就是很好的例子。

于 2009-02-13T07:20:44.247 回答