C/C++ 位域
来源:互联网 发布:做淘宝怎么提高销量 编辑:程序博客网 时间:2024/06/03 21:41
1 //假设硬件平台是intel x86(little endian) 2 3 typedef unsigned int uint32_t; 4 void inet_ntoa(uint32_t in) 5 { 6 char b[18]; 7 register char *p; 8 p = (char *)∈ 9 #define UC(b) (((int)b)&0xff) 10 sprintf(b, "%d.%d.%d.%d/n", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 11 printf(b); 12 } 13 int main() 14 { 15 inet_ntoa(0x12345678); 16 inet_ntoa(0x87654321); 17 }
有点难度的一道题目,其实理解的也很简单。
位域(Bit-fields)分析
位域是c++和c里面都有的一个概念,但是位域有一点要注意的有很多问题我们一样样的看:
大端和小端字节序
这个很简单,就是起始点该怎么确定。
先看一个程序:
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 }
那么x的a1,a2,a3该怎么分配值,100的二进制是:0110 0100,那么a1到a3是不是就是依次取值恩?
不是!
我们先看看100分配位的低端是左边的0还是右边的0?很明显是右边的0,那么我们再看a1到a3的分配是从低端到高端的
那么,对应的应该是
<<<<<<--内存增大
a3 a2 a1
011 001 00
内存增大之所以这么写是因为,011是在高位!
而不是通常认为的的:
a1 a2 a3
011 001 00
还有一个情况多见就是一个二进制的数字转化为点分十进制数值,如何进行,这里涉及到大端还是小端的问题,上面没有涉及,主要是因为上面是一个字节,没有这个问题,多个字节就有大端和小端的问题了,或许我们应该记住这一点就是,在我们的计算机上面,大端和小端都是以字节为准的,当然严格来说更应该以位为准不是吗?具体可以参考维基百科上面的一篇文章,他给出了一个以位为准的大小端序的图:
http://en.wikipedia.org/wiki/Endianess
下面研究字节为单位的大小端序,继续看代码吧,如下:
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个字节,就是从高位字节到低位字节:12,34,56,78,那么这里该怎么放?如下:
---->>>>>>内存增大
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]
内存随着箭头增大!同时小端存储也是低位到高位在内存中的增加!
这样我们知道了内存怎么分布了
那么:
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
位域的符号(正负)
看完大端和小端以后,再看看位域的取值的问题,上面我们谈到了一些,首先就是位域是按照位来取值的跟我们的int是32位char是8
位一样,很简单,但是,要注意一点就是位域也有正负,指有符号属性的,就是最高位表示的,也会涉及到补码这个一般被认为非常
恶心的东西,看看程序吧:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 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 <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 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+1=1,再取符
号负数,就是-1.
整型提升
最后的打印是用的%d,那么就是对应的int的打印,这里的位域肯定要提升,这里有一点,不管是提升到有符号还是无符号,都是自
己的符号位来补充,而不改变值的大小(这里说的不改变值大小是用相同的符号属性来读取),负数前面都补充1,正数都是用0来补充
,而且也只有这样才能保证值不变,比如,char提升到int就是前面补充24个char的最高位,比如:
char c = 0xf0; int p = c; printf("%d %d/n",c,p);
输出:-16 -16
p实际上就是0xfffffff0,是负数因此就是取反加1得到
c是一个负数那么转化到x的时候就是最高位都用1来代替,得到的数不会改变值大小的。
再看:
char c = 0xf0; unsigned int x = c; printf("%u/n",x);
得到的结果是4294967280,也就是0xfffffff0,记住,无符号用%u来打印。
地址不可取
最后说的一点就是位域是一个字节单元里面的一段,是没有地址的!
1 struct BitField 2 { 3 unsigned char a:2; //最低位; 4 unsigned char b:3; 5 unsigned char c:3; //最高位; 6 }; 7 union Union 8 { 9 struct BitField bf;10 unsigned int n;11 };12 union Union ubf;13 ubf.n = 0; //初始化;14 ubf.bf.a = 0; //二进制为: 00015 ubf.bf.b = 0; //二进制为: 00016 ubf.bf.c = 1; //二进制为: 00117 printf("ubf.bf.n = %u\n", ubf.n);
1 #include <iostream> 2 #include <memory.h> 3 using namespace std; 4 struct A 5 { 6 int a:5; 7 int b:3; 8 }; 9 int main(void)10 {11 char str[100] = "0134324324afsadfsdlfjlsdjfl";12 struct A d;13 memcpy(&d, str, sizeof(A));14 cout << d.a << endl;15 cout << d.b << endl;16 return 0;17 }
在32位x86机器上输出:
高位 00110100 00110011 00110001 00110000 低位 '4' '3' '1' '0' 其中d.a和d.b占用d低位一个字节(00110000),d.a : 10000, d.b : 001
解析:在默认情况下,为了方便对结构体内元素的访问和管理,当结构体内的元素长 度都小于处理器的位数的时候,便以结构体里面最长的元素为对其单位,即结构体的长度一定是最长的数据元素的整数倍;如果有结构体内存长度大于处理器位数的 元素,那么就以处理器的位数为对齐单元。由于是32位处理器,而且结构体中a和b元素类型均为int(也是4个字节),所以结构体的A占用内存为4个字 节。
上例程序中定义了位域结构A,两个个位域为a(占用5位),b(占用3位),所以a和b总共占用了结构A一个字节(低位的一个字节)。
当程序运行到14行时,d内存分配情况:
高位 00110100 00110011 00110001 00110000 低位
'4' '3' '1' '0'
其中d.a和d.b占用d低位一个字节(00110000),d.a : 10000, d.b : 001
d.a内存中二进制表示为10000,由于d.a为有符号的整型变量,输出时要对符号位进行扩展,所以结果为-16(二进制为11111111111111111111111111110000)
d.b内存中二进制表示为001,由于d.b为有符号的整型变量,输出时要对符号位进行扩展,所以结果为1(二进制为00000000000000000000000000000001)
1 2 3 #include "stdio.h" 4 5 void main(int argn ,char *argv) 6 { 7 struct test { 8 unsigned a:10; 9 unsigned b:10;10 unsigned c:6;11 unsigned :2;//this two bytes can't use12 unsigned d:4;13 }data,*pData;14 data.a=0x177;15 data.b=0x111;16 data.c=0x7;17 data.d=0x8;18 19 pData=&data;20 printf("data.a=%x data.b= %x data.c=%x data.d=%xn",pData->a,pData->b,pData->c,pData->d);//位域可以使用指针21 22 printf("sizeof(data)=%dn",sizeof(data)); //4 bytes ,最常用的情况23 24 struct testLen{25 char a:5;26 char b:5;27 char c:5;28 char d:5;29 char e:5;30 }len;31 32 printf("sizeof(len)=%dn",sizeof(len)); //5bytes 规则233 34 struct testLen1{35 char a:5;36 char b:2;37 char d:3;38 char c:2;39 char e:7;40 }len1;41 printf("sizeof(len1) =%dn",sizeof(len1)); //3bytes 规则142 43 struct testLen2{44 char a:2;45 char :3;46 char b:7;47 long d:20; //4bytes48 char e:4;49 }len2;50 printf("sizeof(len2)=%dn",sizeof(len2)); //12 规则3,4,5,总长为4的整数倍,2+3 占1byte,b占1bye 由于与long对其,2+3+7 占4字节,后面 d 与 e进行了优化 占一个4字节51 52 53 struct testLen3{54 char a:2;55 char :3;56 char b:7;57 long d:30;58 char e:4;59 }len3;60 printf("sizeof(len3)=%dn",sizeof(len3));//12 规则3,4,5,总长为4的整数倍,2+3 占1byte,b占1bye 由于与long对其,2+3+7 占4字节,后面 d占一个4字节,为了保证与long对其e独占一个4字节61 }
- C位域
- C/C++位域
- C语言 位域
- c语言位域
- c/c++位域
- C/C++位域
- c语言位域
- C语言位域
- C 位域
- c位域
- c中的位域
- c位域
- C位域总结
- C语言 位域
- c位域
- C语言位域!
- c语言--位域
- C语言位域
- C++ string的大小写转换
- C++ transform
- C++find函数
- openwrt设置内网端口映射
- 大小端模式
- C/C++ 位域
- 原码,补码表示
- C中signed与unsigned
- C语言变参问题
- C++ new(1)
- Geometric Progression----(BestCoder Round #54 (div.2))
- C++ new(2)
- C++ new(3)
- LoaderManager的理解