如何根据“Endianness”将数据存储在位级别?

时间:2010-05-28 20:38:37

标签: c pointers endianness bit

我读到了关于Endianness并理解下蹲......

所以我写了这个

main()
{
    int k = 0xA5B9BF9F;

    BYTE *b = (BYTE*)&k;    //value at *b is 9f
    b++;    //value at *b is BF
    b++;    //value at *b is B9
    b++;    //value at *b is A5
}

k等于A5 B9 BF 9F

和(byte)指针“步行”o / p为9F BF b9 A5

所以我得到它的字节向后存储......好的。

所以现在我想它是如何存储在BIT级别的......

我的意思是“9f”(1001 1111)存储为“f9”(1111 1001)?

所以我写了这个

int _tmain(int argc, _TCHAR* argv[])
{
    int k = 0xA5B9BF9F;
    void *ptr = &k;
    bool temp= TRUE;
    cout<<"ready or not here I come \n"<<endl;

    for(int i=0;i<32;i++)
    {   
        temp = *( (bool*)ptr + i );
        if( temp )
            cout<<"1 ";
        if( !temp)
            cout<<"0 ";
        if(i==7||i==15||i==23)
            cout<<" - ";
   }
}

我得到一些随机输出

即使是nos。像“32”我没有任何理智。

为什么?

5 个答案:

答案 0 :(得分:7)

为了完整起见,机器用 字节顺序和位顺序来描述。

intel x86被称为Consistent Little Endian,因为它在内存地址增加时以LSB到MSB顺序存储多字节值。其位编号约定为b0 = 2 ^ 0且b31 = 2 ^ 31。

Motorola 68000被称为Inconsistent Big Endian,因为它随着内存地址的增加以MSB到LSB的顺序存储多字节值。它的位编号约定为b0 = 2 ^ 0和b31 = 2 ^ 31(与intel相同,这就是为什么它被称为'Inconsistent'Big Endian)。

32位IBM / Motorola PowerPC称为Consistent Big Endian,因为它随着内存地址的增加以MSB到LSB的顺序存储多字节值。其位编号约定为b0 = 2 ^ 31且b31 = 2 ^ 0。

在普通的高级语言使用下,位顺序通常对开发人员是透明的。使用汇编语言编写或使用硬件时,位编号确实起作用。

答案 1 :(得分:5)

Endianness,正如您在实验中发现的那样,是指字节存储在对象中的顺序。

比特不会以不同的方式存储,它们总是8位,并且总是“人类可读”(高 - >低)。

现在我们已经讨论过您不需要代码......关于您的代码:

for(int i=0;i<32;i++)
{   
  temp = *( (bool*)ptr + i );
  ...
}

这不是你认为它正在做的事情。你正在迭代0-32,一个字中的位数 - 很好。但是你的temp作业完全没错:)

值得注意的是,bool*的大小与int*的大小相同BigStruct*。同一台机器上的所有指针大小相同 - 32位机器上为32位,64位机器上为64位。

ptr + i正在向i地址添加ptr个字节。当i>3时,你正在阅读一个全新的词......这可能会导致段错误。

您要使用的是bit-masks。这样的事情应该有效:

for (int i = 0; i < 32; i++) {
  unsigned int mask = 1 << i;
  bool bit_is_one = static_cast<unsigned int>(ptr) & mask;
  ...
}

答案 2 :(得分:3)

您的机器几乎肯定无法处理单个内​​存位,因此字节内的位布局毫无意义。字节序仅指多字节对象内的字节顺序。

为了使你的第二个程序有意义(虽然没有任何理由,因为它不会给你任何有意义的结果)你需要了解按位运算符 - 特别是&这个应用程序

答案 3 :(得分:3)

Byte Endianness

在不同的机器上,此代码可能会产生不同的结果:

union endian_example {
   unsigned long u;
   unsigned char a[sizeof(unsigned long)];
} x;

x.u = 0x0a0b0c0d;

int i;
for (i = 0; i< sizeof(unsigned long); i++) {
    printf("%u\n", (unsigned)x.a[i]);
}

这是因为不同的机器可以按任何字节顺序自由存储值。这是相当随意的。在宏伟的计划中没有倒退或前锋。

位字节式

通常你不必担心比特字节序。访问单个位的最常用方法是使用移位(>><<),但这些方式实际上与值有关,而不是字节或位。他们对价值进行算术运算。该值存储在位(以字节为单位)中。

如果您使用位字段,那么您可能遇到带有位字节序的C中的问题。这是一个很少使用的(出于这个原因和其他一些)C的“特性”,它允许你告诉编译器struct成员将使用多少位。

struct thing {
     unsigned y:1; // y will be one bit and can have the values 0 and 1
     signed z:1; // z can only have the values 0 and -1
     unsigned a:2; // a can be 0, 1, 2, or 3
     unsigned b:4; // b is just here to take up the rest of the a byte
};

在这里,位字节式依赖于编译器。 ything应该是{{1}}中最重要还是最不重要的位?谁知道?如果您关心位排序(描述IPv4包头的布局,设备的控制寄存器,或文件中的存储格式),那么您可能不想担心某些不同的编译器这样做错误办法。此外,编译器并不总是像人们希望的那样聪明地使用位字段。

答案 4 :(得分:2)

这一行:

temp = *( (bool*)ptr + i );

...当您像这样进行指针运算时,编译器会将指针移动乘以您指向的事物的大小。因为你将void *转换为bool *,编译器会将指针移动一个“bool”的大小,这可能只是一个int下的封面,所以你将从更远的地方打印内存比你想象的还要好。

您无法解决字节中的各个位,因此询问它们存储的方向几乎毫无意义。 (您的机器可以按照您想要的方式存储它们,您将无法分辨)。你可能唯一需要关心的是当你实际通过物理接口(如I2C或RS232或类似物)吐出位时,你必须逐个吐出这些位。尽管如此,协议将定义将位吐出的顺序,并且设备驱动程序代码必须在协议顺序中的“一个值为0xAABBCCDD的int”和“一个位序列11100011 ... [无论如何]之间进行转换”