Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3295137
  • 博文数量: 406
  • 博客积分: 10227
  • 博客等级: 上将
  • 技术积分: 9807
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-17 21:48
个人简介

非淡泊无以明志,非宁静无以致远

文章存档

2020年(2)

2019年(5)

2018年(4)

2017年(3)

2016年(24)

2015年(8)

2014年(7)

2013年(3)

2012年(1)

2011年(23)

2010年(179)

2009年(147)

分类: 嵌入式

2011-03-01 17:28:04

 

 

位域是c++c里面都有的一个概念,但是位域有要注意的很多问题:

 

1.大端和小端字节序

这个很简单,就是起始点该怎么确定。

先看一个程序:

 

1.union {

2.    struct 

3.    {

4.        unsigned char a1:2;

5.        unsigned char a2:3;

6.        unsigned char a3:3;

7.    }x;

8.    unsigned char b;

9.}d;

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

11.{

12.    d.b = 100;

13.    return 0;

14.}

那么xa1a2a3该怎么分配值,100的二进制是:0110 0100,那么a1a3是不是就是依次取值恩?

不是!

我们先看看100分配位的低端是左边的0还是右边的0?很明显是右边的0,那么我们再看a1a3的分配是从低端到高端的,那么,对应的应该是

内存大  内存小

a3   a2  a1

011  001 00

 

 

 

还有一个情况多见就是一个二进制的数字转化为点分十进制数值,如何进行,这里涉及到大端还是小端的问题,上面没有涉及,主要是因为上面是一个字节,没有这个问题,多个字节就有大端和小端的问题了,或许我们应该记住这一点就是,在我们的计算机上面,大端和小端都是以字节为准的,当然严格来说更应该以位为准不是吗?

具体可以参考维基百科上面的一篇文章,他给出了一个以位为准的大小端序的图:

 

 

下面研究字节为单位的大小端序,继续看代码吧,如下:

 

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

2.{

3.    int a = 0x12345678;

4.    char *p = (char *)&a;

5.    char str[20];

6.    sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

7.    printf(str);

8.    return 0;

9.}

这个程序假设是小端字节序,那么结果是什么?

我们看看应该怎么放置呢?

每个字节8位,0x12345678分成4个字节,就是从高位字节到低位字节:12345678,那么这里该怎么放?如下:

---->>>>>>内存增大

78 56 34 12

 

因为这个是小端,那么小内存对应低位字节,就是上面的结构。

 

接下来的问题又有点迷糊了,就是p怎么指向,是不是指向0x12345678的开头--12处?不是!12是我们所谓的开头,但是不是内存的开始处,我们看看内存的分布,我们如果了解p[0]p[1]的操作是&p[0]+1,就知道了,p[1]地址比p[0]地址大,也就是说p的地址也是随内存递增的!

 

12 ^ p[3]

    |

34 | p[2]

    |

56 | p[1]

    |

78 | p[0]

内存随着箭头增大!同时小端存储也是低位到高位在内存中的增加!

这样我们知道了内存怎么分布了

 

那么:

 

1.sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

str就是这个结果了:

120.86.52.18(注意:这个地方是以十进制的形式输出的)

 

那么反过来呢?

 

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

2.{

3.    int a = 0x87654321;

4.    char *p = (char *)&a;

5.    char str[20];

6.    sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

7.    printf(str);

8.    return 0;

9.}

依旧是小端,8位是一个字节那么就是这样的啦:

 

87 ^ p[3]

     |

65 | p[2]

    |

43 | p[1]

    |

21 | p[0]

 

结果是:

33.67.101.-121

为什么是负的?因为系统默认的char是有符号的,本来是0x87也就是135,大于127因此就减去256得到-121

那么要正的该怎么的弄?

如下就是了:

 

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

2.{

3.    int a = 0x87654321;

4.    unsigned char *p = (unsigned char *)&a;

5.    char str[20];

6.    sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

7.    printf(str);

8.    return 0;

9.}

用无符号的!

结果:

33.67.101.135

 

2.位域的符号(正负)

看完大端和小端以后,再看看位域的取值的问题,上面我们谈到了一些,首先就是位域是按照位来取值的跟我们的int32char8位一样,很简单,但是,要注意一点就是位域也有正负,指有符号属性的,就是最高位表示的,也会涉及到补码这个一般被认为非常恶心的东西,看看程序吧:

 

1.#include

2.#include

3.#include

4.int main(int argc, char** argv)

5.{

6.    union

7.    {

8.        struct

9.        {

10.            unsigned char a:1;

11.            unsigned char b:2;

12.            unsigned char c:3;

13.        }d;

14.        unsigned char e;

15.    } f;

16.    f.e = 1;

17.    printf("%d\n",f.d.a);

18.    return 0;

19.}

<小端>

那么输出是什么?

换一下:

 

1.#include

2.#include

3.#include

4.int main(int argc, char** argv)

5.{

6.    union

7.    {

8.        struct

9.        {

10.            char a:1;

11.            char b:2;

12.            char c:3;

13.        }d;

14.        char e;

15.    } f;

16.    f.e = 1;

17.    printf("%d\n",f.d.a);

18.    return 0;

19.}

输出又是什么?

 

小端的话,那么,再d.a上面分得1,而这个是无符号的char,那么前者输出是1,没有问题,第二个输出是-1,哈哈。为什么?

第二个是有符号的,就一个位分得1,那么就是最高位分得1,就是负数,负数用的补码,实际的值是取反加1,就是0+11,再取符号负数,就是-1.

3.整型提升

最后的打印是用的%d,那么就是对应的int的打印,这里的位域肯定要提升,这里有一点,不管是提升到有符号还是无符号,都是自己的符号位来补充,而不改变值的大小(这里说的不改变值大小是用相同的符号属性来读取),负数前面都补充1,正数都是用0来补充,而且也只有这样才能保证值不变,比如,char提升到int就是前面补充24char的最高位,比如:

 

1. char c = 0xf0;

2. int p = c;

3. printf("%d %d\n",c,p);

输出:-16 -16

p实际上就是0xfffffff0,是负数因此就是取反加1得到c是一个负数那么转化到x的时候就是最高位都用1来代替,得到的数不会改变值大小的。

再看:

 

1. char c = 0xf0;

2. unsigned int x = c;

3. printf("%u\n",x);

得到的结果是4294967280,也就是0xfffffff0,记住,无符号用%u来打印。

地址不可取

最后说的一点就是位域是一个字节单元里面的一段,是没有地址的!

阅读(1070) | 评论(0) | 转发(1) |
0

上一篇:BEST WISHES

下一篇:#define用法详解

给主人留下些什么吧!~~