小端序还是大端序:如何解释测试

9

我正在编写一个程序来测试机器的字节序并打印出来。我了解小端和大端的区别,但是从我在网上查找到的内容中,我不明白为什么这些测试可以显示机器的字节序。

以下是我在网上找到的内容。*(char *)&x 是什么意思,为什么它等于 1 就证明机器是小端字节序?

int x = 1;
if (*(char *)&x == 1) {
    printf("Little-Endian\n");
} else {
    printf("Big-Endian\n");
}

4
如果你“已经理解”大端和小端的区别,为什么一个基本的字节序测试就不容易理解呢? - wallyk
我知道它的作用,但不完全确定为什么检查变量的解引用版本,强制转换为char指针会告诉你那个。除非char的字节顺序是标准的,在这种情况下,它将告诉您在位运算转换期间是否更改。 - zebediah49
6个回答

14

如果我们将其分成不同的部分:

  1. &x: 这会得到变量 x 的地址,即 &x 是指向 x 的指针。类型是 int *

  2. (char *)&x: 这将获取 x 的地址(它是一个 int *),并将其转换为 char *

  3. *(char *)&x: 这会取消引用由 &x 指向的 char *,即获取存储在 x 中的值。

现在如果我们回到 x 和数据的存储方式。在大多数机器上,x 是四个字节长的。将 1 存储在 x 中将最低有效位设置为 1,其余为 0。在小端机器上,这被存储在内存中为 0x01 0x00 0x00 0x00,而在大端机器上,它被存储为 0x00 0x00 0x00 0x01

该表达式所做的是获取这些字节中的第一个字节,并检查它是否为 1


4
以下是内存的示意图,假设使用32位整数:
Little-endian
0x01000000 = 00000001000...00

Big-endian
0x00000001 = 0......01

解引用一个char *指针会返回一个字节。您的测试通过将地址解释为char *并对其进行解引用来获取该内存位置的第一个字节。请注意保留HTML标记。

@HunterMcMillen 可能是。你是什么意思? - cnicutar
也许我有点糊涂了,你是在使用0x80000000作为内存中的字节地址吗? - Hunter McMillen
@HunterMcMillen 我正在使用它来表示十六进制下的数字2^31 - cnicutar
@cnicutar:实际存储的值将为0x01、0x00、0x00、0x00,而不是0x80000000。字节中的位并未反转--只有存储在内存中的字节顺序。 - tomlogic
请参考@Sangeeth Saravanaraj的答案,了解该值存储方式的更好描述。 - tomlogic

1

分解*(char *)&x

&x是整数x的地址

(char *)将整数x的地址视为字符(也称为字节)的地址

*引用字节的值


显然@Joahcim Pileborg解释得更好,更快。 - Brian Swift

1
int x;

x是一个可以容纳32位值的变量。

int x = 1;

一个给定的硬件可以将值1以以下一种格式之一存储为32位值。
Little Endian
0x100    0x101    0x102    0x103
00000001 00000000 00000000 00000000 

(or) 

Big Endian
0x100    0x101    0x102    0x103
00000000 00000000 00000000 00000001

现在让我们尝试分解这个表达式:

&x 

获取变量x的地址。假设x的地址为0x100

(char *)&x 

&x 是一个整数变量的地址。(char *)&x 将地址 0x100(int *) 转换为 (char *)

*(char *)&x 

取消引用存储在 (char *) 中的值,这实际上是指 4 字节(32 位整数 x)中从左到右的第一个字节。

(*(char *)&x == 1)

如果从左到右的第一个字节存储值为00000001,那么它是小端序。如果从左到右的第四个字节存储值为00000001,那么它是大端序。

谢谢!我还有一个快速问题,如果您不介意的话。 (char *)&x会给我一个x地址的字符串吗?另外,我如何获取变量中所有单个位的值?例如整个字节。 - atb
C字符串的松散定义是:“一个或多个(数组)字符(char),以‘\0’结尾”。在C中,字符串可以表示为char x[]char *x - Sangeeth Saravanaraj
有关位运算,请参考http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/。 - Sangeeth Saravanaraj

0
如果一个大端字节序的4字节无符号整数看起来像0xAABBCCDD,等于2864434397,则同样的4字节无符号整数在小端处理器上看起来像0xDDCCBBAA,也等于2864434397。
如果一个大端字节序的2字节无符号短整数看起来像0xAABB,等于43707,则同样的2字节无符号短整数在小端处理器上看起来像0xBBAA,也等于43707。
这里有一些方便的#define函数,可以交换从小端到大端和大端到小端的字节。-->
// can be used for short, unsigned short, word, unsigned word (2-byte types)
#define BYTESWAP16(n) (((n&0xFF00)>>8)|((n&0x00FF)<<8))

// can be used for int or unsigned int or float (4-byte types)
#define BYTESWAP32(n) ((BYTESWAP16((n&0xFFFF0000)>>16))|((BYTESWAP16(n&0x0000FFFF))<<16))

// can be used for unsigned long long or double (8-byte types)
#define BYTESWAP64(n) ((BYTESWAP32((n&0xFFFFFFFF00000000)>>32))|((BYTESWAP32(n&0x00000000FFFFFFFF))<<32))

0

是的,那回答了这个问题。这里有一个更一般性的答案:

#include <iostream>
#include <cstdlib>  
#include <cmath>

using namespace std;

int main()
{
cout<<"sizeof(char) = "<<sizeof(char)<<endl;
cout<<"sizeof(unsigned int) = "<<sizeof(unsigned int)<<endl;
//NOTE: Windows, Mac OS, and Linux and Tru64 Unix are Little Endian architectures
//Little Endian means the memory value increases as the digit significance increases
//Proof for Windows: 

unsigned int x = 0x01020408; //each hexadecimal digit is 4 bits, meaning there are 2
                             //digits for every byte
char *c = (char *)&x;
unsigned int y = *c*pow(16,0) +pow(16,2) * *(c+1)+pow(16,4) * *(c+2)+pow(16,6) * *(c+3);
//Here's the test: construct the sum y such that we select subsequent bytes of 0x01020408
//in increasing order and then we multiply each by its corresponding significance in
//increasing order.  The convention for hexadecimal number definitions is that  
//the least significant digit is at the right of the number.  
//Finally, if (y==x),then...     
if (y==x) cout<<"Little Endian"<<endl;
else cout<<"Big Endian"<<endl;

cout<<(int) *c<<endl;
cout<<(int) *(c+1)<<endl;
cout<<(int) *(c+2)<<endl;
cout<<(int) *(c+3)<<endl;
cout<<"x is "<<x<<endl;
cout<<(int)*c<<"*1 + "<<(int)*(c+1)<<"*16^2 + "<<(int)*(c+2)<<"*16^4 + "<<(int)*(c+3)<<" *16^6 = "<<y<<endl;
system("PAUSE"); //Only include this on a counsel program
return 0;
}

这将显示c、c+1、c+2和c+3的解引用值,分别为8、4、2和1。 总和y为16909320,等于x。尽管数字的重要性从右到左增长,但这仍然是小端模式,因为相应的内存值也是从右到左增长的,这就是为什么左移二进制运算符<<会增加变量的值,直到非零数字被完全移出变量。不要将此运算符与std::cout的<<运算符混淆。 如果这是大端模式,那么c、c+1、c+2和c+3的显示将如下所示: 1 2 4 8


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