【问题标题】:Reading "integer" size bytes from a char* array.从 char* 数组中读取“整数”大小的字节。
【发布时间】:2010-10-07 09:25:41
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 我对你想要做什么感到有点困惑。你能写一些伪代码,作为一个例子吗?您是否尝试从字符数组中解析整数?
  • 我正在尝试从 char* 数组中查找 sizeof(int) 字节,并尝试将其与整数进行比较。数据的来源是不同的机器。

标签: c++ c endianness


【解决方案1】:

你的意思是这样的吗?:

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

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

【讨论】:

  • 还有什么更明显的? :D
  • 这是一种很好的合法方式,不会破坏类型双关/别名规则。对于那些想知道的人,“但它看起来比演员表慢!” a) 演员表是未定义的行为,所以不要去那里 b) 生成的代码对于 x86/x64 没有什么不同:godbolt.org/g/gxtVFZ
【解决方案2】:

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

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

在任何一种情况下,您都需要对所有大于 1 个字节的数字进行字节交换,例如,short、int、long、double 等。但是,如果您总是处理来自同一字节的数据平台,字节序问题无关紧要。

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));
}

【讨论】:

  • 你应该提到第一个代码sn-p和丹尼尔斯一样有同样的问题:它可以访问不适合int*的未对齐数据
  • 这是我在 Java 中唯一缺少的东西。至少能够从字节数组中读取 int 会很棒。也许我在我的 JVM 实现中为此实现了一些字节码操作
【解决方案3】:

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

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

【讨论】:

  • 这可能会导致访问不对齐。
  • @gimpf:我很好奇:这实际上会在哪些系统上导致错误?
  • 即在 80486 和任何更好的 CPU 上设置了 Align-Flag。
  • 什么时候设置对齐标志?
  • 在我的教授 cpu 上会导致总线错误。在太阳处理器(我相信 sparcs)上,这也可能失败。基本上任何不支持未对齐读/写的处理器
【解决方案4】:

解决此问题的简单方法是确保生成字节的任何内容都以一致的字节顺序进行。通常,各种 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

【讨论】:

  • 现在将“int”替换为“unsigned”,你的答案是正确的;)
  • 我会将 + 和 += 替换为 |和 |= 分别。恕我直言,在这里使用数学运算符令人困惑。
【解决方案5】:

怎么样

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;
}

【讨论】:

    【解决方案6】:

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

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

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

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

    【讨论】:

    • 我的数据实际上是从不同的来源创建的
    【解决方案7】:

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

    【讨论】:

      【解决方案8】:

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

      【讨论】:

      • 当然,这仅适用于您实际上是从网络上读取内容的情况...
      • 好的,他在 comment 中说他正在从另一台机器上读取它。好吧,也许通过刻录/读取 CD 来完成,但更可能的是他确实指的是某种网络。
      【解决方案9】:

      既然可以比较,为什么还要阅读?

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

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

      【讨论】:

        猜你喜欢
        • 2018-05-27
        • 1970-01-01
        • 2014-09-17
        • 2011-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-03
        相关资源
        最近更新 更多