C语言中的位域、字节序、比特序、大小端

本文深入探讨了比特序(位序)的概念及其在不同字节序(大小端)CPU中的应用,通过实例展示了位域在C语言中的表现形式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.比特序 / 位序 /  bitnumbering / bit endianness

 

我们知道一个字节有8位,也就是8个比特位。从第0位到第7位共8位。比特序就是用来描述比特位在字节中的存放顺序的。通过阅读网页http://en.wikipedia.org/wiki/Bit_numbering的内容,关于比特序我们得到下面的结论:

(1比特序分为两种:LSB 0 位序和MSB 0 位序

     LSB是指 leastsignificant bitMSB是指 most significantbit

LSB 0 位序是指:字节的第0位存放数据的leastsignificant bit,即我们的数据的最低位存放在字节的第0位。

MSB 0 位序是指:字节的第0位存放数据的most significantbit,即我们的数据的最高位存放在字节的第0位。

 

所以说对于代码:char *ch = 0x96;  //  0x96 = 1001 0110

 

指针ch到底指向哪里呢?不难知道,如果是LSB 0 位序则显然指针ch指向最右边的也是最低位的0.

而如果是MSB 0 位序则显然指针ch指向最左边的也是最高位的1.

LSB 0: A container for 8-bit binary numberwith the highlighted leastsignificant bit assigned the bit number 0



MSB 0:A container for 8-bit binary numberwith the highlighted most significantbit assignedthe bit number 0




2小端CPU通常采用的是LSB 0 位序,但是大端CPU却有可能采用LSB0 位序也有可能采用的是MSB0 位序

       (Little-endian CPUsusually employ "LSB 0" bit numbering, however both bit numberingconventions can be seen in big-endianmachines. )

3推荐的标准是MSB0 位序。

       (The recommended style for Request forComments documents is "MSB 0" bit numbering.)

(4Bit numbering is usually transparent tothe software.



2.大小端和字节序   http://en.wikipedia.org/wiki/Endianess

In computing, the term endian or endianness refersto the ordering of individually addressable sub-components within therepresentation of a larger data item as stored in external memory (or,sometimes, as sent on a serial connection). Each sub-component in therepresentation has a unique degree of significance, like the place value of digitsin a decimal number. These sub-components are typically 16- or 32-bit words, 8-bit bytes, or even bits. Endianness isa difference in data representation at the hardware level and may or may not betransparent at higher levels, depending on factors such as the type of highlevel language used.

计算机中,术语“端”是指:在内存中的一个较大的数据,它是由各个可以被单独寻址的部分组成,这些组成部分在该数据中是以怎样的顺序存放的呢?而这个问题涉及到的概念,CPU是大端还是小端决定了这些组成部分的存放顺序

这些组成部分可能是1632位的字、8位的字节、甚至是比特位。

The most commoncases refer to how bytes are ordered within a single 16-32-, or 64-bit word

我们通常碰到的情况是:字节是以怎样的顺序存放在一个163264位的数据中

(当我们要存取一个163264数据的某一组成部分,也就是某一个或几个字节时,就要特别注意机器的大小端

 A big-endian machinestores the most significant byte first, and a little-endian machinestores the least significant byte first.

Quick Reference - Byte Machine Example

Endian

First Byte
(lowest address)

Middle Bytes

Last Byte
(highest address)

Summary

big

most significant

...

least significant

Similar to a number written on paper (in Arabic numerals)

little

least significant

...

most significant

Arithmetic calculation order (see carry propagation)

Examples ofstoring the value 0A0B0C0Dh in memory

Big-endian 


Atomic elementsize 8-bit, address increment 1-byte (octet)

increasing addresses  →

...

0Ah

0Bh

0Ch

0Dh

...

The most significantbyte (MSB)value, which is 0Ah in our example, is stored at the memory locationwith the lowest address, the next byte value in significance, 0Bh, isstored at the following memory location and so on. This is akin toLeft-to-Right reading in hexadecimal order.

Atomic elementsize 16-bit

increasing addresses  →

...

0A0Bh

0C0Dh

...

The most significant atomic elementstores now the value 0A0Bh, followed by 0C0Dh.

Little-endian 


Atomic elementsize 8-bit, address increment 1-byte (octet)

increasing addresses  →

...

0Dh

0Ch

0Bh

0Ah

...

The leastsignificant byte (LSB) value, 0Dh, is at the lowestaddress. The other bytes follow in increasing order of significance.

Atomic elementsize 16-bit

increasing addresses  →

...

0C0Dh

0A0Bh

...

The least significant 16-bit unit storesthe value 0C0Dh, immediately followed by 0A0Bh. Notethat 0C0Dh and 0A0Bh represent integers, not bit layouts(see bit numbering).

很显然“小端”机器符合“高高低低”的原则。及高位字节或字存放在高地址,低位字节或字存放在低地址。

另外“小端”机器中,数据在CPU的寄存器和内存中的存放顺序是一致的。



Byte addresses increasing from right toleft

在我们写: 0xFF86 时,很明显地址是从右向左递增的。也就是低位写在右边,高位写在左边。

但是当我们写字符串时:char *str = "Hello world!"却是低位的字符写在左边,高位的字符写在了右边。

With 8-bitatomic elements:

←  increasing addresses

...

0Ah

0Bh

0Ch

0Dh

...

The leastsignificant byte (LSB) value, 0Dh, isat the lowest address. The other bytes follow in increasing order ofsignificance.(这个明显符合我们的习惯)

With 16-bit atomic elements:

←  increasing addresses

...

0A0Bh

0C0Dh

...

The least significant 16-bit unit storesthe value 0C0Dh, immediately followed by 0A0Bh.

The display of text is reversed from thenormal display of languages such as English that read from left to right. Forexample, the word "XRAY" displayed in this manner, with eachcharacter stored in an 8-bit atomic element:

←  increasing addresses

...

"Y"

"A"

"R"

"X"

...

可以看到和我们手写的顺序是相反的,这一点特别要注意! 

If pairs of characters are stored in16-bit atomic elements (using 8 bits per character), it could look evenstranger:

←  increasing addresses

...

"AY"

"XR"

...

相关的一个C例子:

1.  #include <stdio.h>

2.  #include <stdlib.h>

3.  #include <string.h>

4.  int main()

5.  {

6.          chara[] = {'a', 'b', 'c'};

7.          charb[] = {'d', 'e', 'f'};

8.   

9.          a[3] = 0;

10.          printf("strlen(a)=%d,strlen(b)=%d\n", strlen(a), strlen(b));

11.          printf("a=%s,b=%s\n", a, b);

12.          printf("sizeof(a)=%d,sizeof(b)=%d\n", sizeof(a), sizeof(b));

13.          return0;

14.  }

运行结果:

strlen(a)=3, strlen(b)=6
a=abc, b=defabc
sizeof(a)=3, sizeof(b)=3

分析:

字符数组ab都分配在栈上,先分配a, a中的字符是如何分配的呢?显然因为“写字符串时,低位的字符写在左边,高位的字符写在了右边”。'a'是最低位,'b'在中间,而'c'在最高位。而栈是从高地址从低地址扩展的。假如是小端CPU的话,按照“高高低低”的原则,高位的'c'应该最先分配,接着是'b',最后是'a'

分配玩字符数组a之后,在分配字符数组b,同样的道理,高位的'f'应该最先分配,接着是'e',最后是'd'

再执行a[3] = 0;显然a[3]的地址应该比'c'字符的地址要高。所以该语句执行玩之后的栈的情况如下:

高地址 <<---- 低地址

\0  c   b   a   f   e   d

所以:a字符打印的结果是:abc,而b字符打印的结果是:defabc.

strlen函数是计算字符的长度,当然要找到最后的结束字符'\0',才停止计算。所以字符a的长度是3,而字符b的长度是6.

sizeof并不根据末尾的结束字符来计算大小。

 例子2

1.  #include <stdio.h>

2.   

3.  int main()

4.  {

5.          unsignedlong array[] = {0x12345678, 0xabcdef01, 0x456789ab};

6.          unsignedshort ret;

7.   

8.          ret = *((unsigned short *)((unsigned long)array+7));

9.          printf("0x%x\n", ret);

10.   

11.          return0;

12.  }

在“小端”CPU上结果为:0xabab。在“大端”CPU上应该为:0x0112.

例子3

1.  #include <stdio.h>

2.  #include <stdlib.h>

3.  int main(void){

4.      int a[5]={1,2,3,4,5};

5.      int *ptr =(int *)(&a+1);

6.      printf("%d,%d\n",*(a+1),*(ptr-1))

7.      return 0;

8.      

9.  }

结果为:25 (此题与“大小端”无关。)



判断CPU是大端还是小端的方法有有多种:

1.  #include <stdio.h>

2.  #include <assert.h>

3.   

4.  int main()

5.  {

6.      unsigned short x = 0xff01;

7.   

8.      assert(sizeof(x) >= 2);

9.      if(*(char*)&x == 1) //if(char(x) == 1)

10.          printf("little-endian\n");

11.      else if((char)x > 1)

12.          printf("big-endian\n");

13.      else

14.          printf("unknown\n");

15.      return0;

16.  }

方法2

1.  #include <stdio.h>

2.   

3.  int main()

4.  {

5.          union{

6.                  charc;

7.                  int i;

8.          }u;

9.          u.i = 0x0201;

10.   

11.          if(u.c == 1)

12.                  printf("little-endian\n");

13.          else if(u.c == 2)

14.                  printf("big-endian\n");

15.          else

16.                  printf("unknown\n"); 

17.          

18.          return0;

19.  }

3.C语言中的位域

先看几个例子:

1.  #include <stdio.h>

2.  union u{

3.          struct {

4.                  chari:1;

5.                  charj:2;

6.                  charm:3;

7.          } s;

8.          charc;

9.  }r;

10.  int main()

11.  {

12.          r.s.i = 1; // 1

13.         r.s.j = 2; // 10

14.         r.s.m = 3; // 011

15.          printf("0x%x\n", r.c);

16.          return0;

17.  }

gcc -o unionunion.c

./union

结果:0x1d == 0001 1101 == 011 10 1

1.  #include <stdio.h>

2.   

3.  union {

4.      struct

5.      {

6.          unsignedchar a1:2;

7.          unsignedchar a2:3;

8.          unsignedchar a3:3;

9.      }x;

10.      unsignedchar b;

11.  }d;

12.   

13.  int main(int argc, char* argv[])

14.  {

15.      d.b = 100;   //100 == 0110 0100

16.   

17.     printf("0x%x\n0x%x\n0x%x\n", d.x.a1, d.x.a2, d.x.a3);

18.      return0;

19.  }

gcc -o union2 union2.c

结果:

0x0 == 00

0x1 == 001

0x3 == 011

上面两个例子的运行结果,似乎都说明:小端机器中,位域的低位组成数据的低位,位域的高位组成了数据的高位

似乎也符合:小端CPU通常采用的是LSB 0 位序 的惯例。

但是这里有意个疑问:在大端CPU中,上面两个例子的结果是什么呢?结果和小端CPU一样吗?结果唯一吗?

因为前面我们说过:“但是大端CPU却有可能采用LSB 0 位序也有可能采用的是MSB 0 位序

 

### C语言结构体的使用方法和特性 #### 的基本概念 在C语言中,(Bit Fields)是一种特殊的数据结构,它允许程员在一个字节内定义多个二进制字段。这使得可以在有限的空间内存储多个布尔值或枚举值[^1]。 #### 定义的方式 通过在结构体内声明成员变量并指定其宽度来实现。基本语法如下: ```c struct { unsigned int member1 : n1; unsigned int member2 : n2; } yourStructName; ``` 其中 `unsigned int` 表示成员的数据类型,可以是 `signed int`, `unsigned int` 或 `_Bool`;而冒号后的数字指定了该成员占据多少个比特[^4]。 #### 实际应用案例 下面是一个具体的例子展示了如何创建包含不同长度的结构体: ```c typedef struct _net_pro_cdu { unsigned char acs : 4; // 占用前四 unsigned char : 0; // 不占任何, 起分隔作用 unsigned char ace : 4; // 接着占用后面的四个置 unsigned char dve : 4; // 新的一组四比特列 } Ncdu; ``` 这里定义了一个名为Ncdu的新类型,内部有三个分别占有4bit空间的成员acs、ace以及dve,并且还有一个匿名零宽用来强制结束当前字节边界上的填充[^3]。 #### 平台依赖性与移植注意事项 值得注意的是,尽管能够有效地利用内存资源,但由于编译器之间存在差异,对于相同代码片段所产生的实际布局可能有所不同。这意味着当涉及到跨平台开发时,应当特别注意确保程逻辑不会受到这种不确定性的负面影响[^2]。 #### 组合运用——联合体配合 除了单独作为结构体的一部分外,还可以将与其他高级特性结合起来使用。比如把放在联合体内可以让同一块物理地址既能按整体读写又能逐操作,从而进一步增强了灵活性[^5]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值