南七星之家 发表于 2024-10-3 22:08:11

C/C++ 判断盘算机存储器字节序(端序)的几种方式

字节序分为存储器字节序和网络字节序(通常接纳大端),这里重要讨论的是主存储器字节序。
主存是存储器中的一种,为什么只讨论主存?因为编写运行在现代主流操作体系上的步伐,是没有 I/O 权限的。
主存字节序

所谓字节序就是字节分列的顺序,拿主存来说就是如果低字节存放在低地点处,就是低端字节序(小端),反之为高端字节序(大端)。拿 0x1234567 来说:
判断字节序


[*]通过指针
既然字节序就是字节的分列顺序,那么我们把至少 2 字节的字节序列存放到主存中,如果能获取该数据的最低或最高地点的 1 字节数据,不就知道字节序了吗?对应 C/C++ 来说就是指针。
#include <stdio.h>

int main(void)
{
    unsigned int i = 1;
    char *ch_ptr = (char*)&i;// 创建一个指向 i 的字符(字节)指针

    if (*ch_ptr)
    {
      printf("Little-Endian\n");// 如果 *ch_ptr 为 1,表示最低位字节为 1,为小端
    }
    else
    {
      printf("Big-Endian\n");   // 否则为大端
    }

    return 0;
}为了保障可移植性,我这里用的是 unsigned int i,以确保每个平台都至少 2 个字节。别的数据范例甚至是数组都是可以的,没有本质的区别,都是在主存中存储相应数量的字节序列。

[*]使用团结体(Union)
团结体答应在相同的内存位置存储不同的数据范例,并且可以通过不同的成员来检视同一块内存区域。利用该特性,我们可以在团结体中定义两个成员,其中一个成员确保为 1 字节(固然,也可以定义成数组,然后取数组中的第一个元素),而另一个成员则确保每个平台都至少为 2 字节。
#include <stdio.h>

typedef union
{
        unsigned int i;
        char byte;
} ByteOrder;

int main(void)
{
    ByteOrder order;
    order.i = 1;// 将整数 1 存储到联合体中

    if (order.byte)
        {
      printf("Little-Endian\n");// 如果最低位字节存储的是 1,则为小端
    }
        else
        {
      printf("Big-Endian\n");   // 否则为大端
    }

    return 0;
}如前所述,char byte 可以改为 char ch_arr 情势的数组,然后需要将 order.byte 改为 order.ch_arr。
这两种方法本质上是一样的,都是通过判断数据的低位字节在内存中的位置来判断字节序。可以根据实际环境选择其中一种方法来使用。
除了使用团结体和指针外,还有一些别的方法可以检测字节序。

[*]位移和掩码
这种方法利用位操作(位移和掩码)来检测字节序。它不依赖于团结体,也不需要指针操作,而是直接通过数值操作来判断:
#include <stdio.h>

int main(void)
{
        unsigned int i = 1; // 只有最低位是 1 的整数

    if ( (i >> 0) & 1 ) // 将 i 右移 0 位后与 1 进行与操作
        {
      printf("Little-Endian\n");
    }
        else
        {
      printf("Big-Endian\n");
    }

    return 0;
}这种方法简朴明了,通过将整数 1(其二进制情势在小端中为 01 00 00 00,在大端中为 00 00 00 01)的最低位(最右边的位)与 1 进行与操作。如果结果为 1,那么阐明机器是小端字节序。
这种方法的好处是代码简朴,且没有使用额外的内存(如团结体或指针)。它直接通过整型数值本身的操作来确定字节序。
性能对比


[*]团结体:这种方法涉及访问团结体的不同成员。团结体方法的长处是直观易懂,但访问团结体成员可能导致微小的性能开销,尤其是在编译器优化不足的环境下。
[*]指针:这种方法涉及将一个整数的地点转换为字符指针,然后查抄具体的字节。这种方法可能轻微快一点,因为它直接操作内存,没有额外的抽象层。然而,这通常是微不足道的。
[*]位移和掩码:此方法使用位操作来查抄字节序。位操作服从非常高,因为它是直接在寄存器级别上进行的,没有内存访问的开销。
在大多数实际应用中,字节序的查抄通常只在步伐启动或初始化阶段进行一次,因此这里的性能差异险些可以忽略不计。即便云云,从纯粹理论和微优化的角度来看,位移和掩码方法可能是最快的,因为它避免了任何内存访问,直接在处置处罚器中完成所有操作。
然而,选择哪种方法应该基于代码的可读性、可维护性以及平台兼容性,而不仅仅是微小的性能差异。在大多数环境下,清楚和正确的代码要比微小的性能提升更加紧张。
别的方法

除了前面提到的 3 种常见方法,还可以使用一些更具体或高级的技能来检测或处置处罚字节序标题,尤其是在涉及到跨平台兼容性或网络通信时。
标准库函数

在某些编程环境中,标准库提供了函数来处置处罚字节序标题。例如,在 C 语言中,网络编程常用的库如提供了 htonl() 和 ntohl() 函数,用于将主机字节序转换为网络字节序,或反之。这些函数主动考虑了底层平台的字节序:
#include <stdio.h>
#include <arpa/inet.h>

int main(void)
{
    unsigned int x = 0x12345678;
    unsigned int y = htonl(x);// 主机到网络字节序

    if (y == x)
        {
      printf("Big-Endian\n");
    }
        else
        {
      printf("Little-Endian\n");
    }

    return 0;
}该方法不仅能判断字节序,还能在需要的时候转换字节序,非常得当网络通信中的数据交换。
编译器特定的预定义宏

一些编译器提供预定义的宏来指示目的平台的字节序。例如,GCC 和一些其他编译器可能定义了特定的宏,可以在编译时判断字节序。这种方法在编译时就确定了字节序,无需运行时检测。

[*]GCC 和 Clang 编译器
GCC 和 Clang 通常不直接提供检测字节序的宏,但你可以根据平台或者架构特定的预定义宏来推断字节序。例如,你可以查抄是否定义了特定于某个架构的宏:
#include <stdio.h>

int main(void)
{
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    printf("Little-endian\n");
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    printf("Big-endian\n");
#else
    printf("Unknown byte order\n");
#endif

        return 0;
}这里使用了 GCC 和 Clang 编译器提供的 __BYTE_ORDER__ 宏以及相关的 __ORDER_LITTLE_ENDIAN__ 和 __ORDER_BIG_ENDIAN__ 宏来确定字节序。

[*]MSVC 编译器
MSVC 编译器没有直接提供检测字节序的宏,因为 Windows 平台通常是小端字节序。如果你在使用 Visual Studio 且需要编写可移植的代码,可能需要自行定义这些宏或者使用其他方法来确定字节序。

[*]跨平台编译
如果项目涉及不同的编译器和平台,就需要组合多种方法来使用,确保在这些宏未定义的环境下中也能够处置处罚。
#include <stdio.h>

int main(void)
{
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    printf("Little-endian\n");
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    printf("Big-endian\n");
#elif defined(_BIG_ENDIAN)
    printf("Big-endian\n");
#elif defined(_LITTLE_ENDIAN)
    printf("Little-endian\n");
#else
    printf("Byte order unknown or assuming default (e.g., little-endian)\n");
#endif

        return 0;
}常见 CPU 的字节序


[*]大端字节序:IBM、Sun、PowerPC。
[*]小端字节序:x86、DEC 。
ARM 体系的 CPU 则巨细端字节序通吃,具体用哪类字节序由硬件选择。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: C/C++ 判断盘算机存储器字节序(端序)的几种方式