C/C++位域之我见
2009-12-06 20:25
274 查看
很早想说说这个问题了,经常也会有很多公司拿位域出来考人,呵呵要真的想弄清楚还要一点点的分析。 这里先看看网宿的一道笔试题目: //假设硬件平台是intel x86(little endian) char *inet_ntoa(uint32_t in) { static char b[18]; register char *p; p = (char *)in; #define UC(b) (((int)b)&0xff) (void) snprintf(b, sizeof(b), "%d. %d. %d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); return (b); } int main() { printf("%s, %s", inet_ntoa(0x12345678), inet_ntoa(87654321)); } 有点难度的一道题目,其实理解的也很简单。 位域分析 位域是c++和c里面都有的一个概念,但是位域有一点要注意的有很多问题我们一样样的看: 大端和小端字节序 这个很简单,就是起始点该怎么确定。 先看一个程序: union { struct { unsigned char a1:2; unsigned char a2:3; unsigned char a3:3; }x; unsigned char b; }d; int main(int argc, char* argv[]) { d.b = 100; return 0; } 那么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 还有一个情况多见就是一个二进制的数字转化为点分十进制数值,如何进行,这里涉及到大端还是小端的问题,上面没有涉及,主要 是因为上面是一个字节,没有这个问题,多个字节就有大端和小端的问题了,如下: int main(int argc, char* argv[]) { int a = 0x12345678; char *p = (char *)&a; char str[20]; sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]); printf(str); return 0; } 这个程序假设是小端字节序,那么结果是什么? 我们看看应该怎么放置呢? 每个字节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 那么反过来呢? int main(int argc, char* argv[]) { int a = 0x87654321; char *p = (char *)&a; char str[20]; sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]); printf(str); return 0; } 依旧是小端,8位是一个字节那么就是这样的啦: 87 ^ p[3] | 65 | p[2] | 43 | p[1] | 21 | p[0] 结果是: 33.67.101.-121 为什么是负的?因为系统默认的char是有符号的,本来是0x87也就是135,大于127因此就减去256得到-121 那么要正的该怎么的弄? 如下就是了: int main(int argc, char* argv[]) { int a = 0x87654321; unsigned char *p = (unsigned char *)&a; char str[20]; sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]); printf(str); return 0; } 用无符号的! 结果: 33.67.101.135 位域的符号(正负) 看完大端和小端以后,再看看位域的取值的问题,上面我们谈到了一些,首先就是位域是按照位来取值的跟我们的int是32位char是8 位一样,很简单,但是,要注意一点就是位域也有正负,指有符号属性的,就是最高位表示的,也会涉及到补码这个一般被认为非常 恶心的东西,看看程序吧: #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char** argv) { union { struct { unsigned char a:1; unsigned char b:2; unsigned char c:3; }d; unsigned char e; } f; f.e = 1; printf("%d/n",f.d.a); return 0; } <小端> 那么输出是什么? 换一下: #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char** argv) { union { struct { char a:1; char b:2; char c:3; }d; char e; } f; f.e = 1; printf("%d/n",f.d.a); return 0; } 输出又是什么? 小端的话,那么,再d.a上面分得1,而这个是无符号的char,那么前者输出是1,没有问题,第二个输出是-1,哈哈。 |
相关文章推荐
- c/c++的位域
- C/C++ struct位结构(位域)
- c/c++ sizeof以及位域
- c/c++语言位域注意事项
- C/C++ struct位结构(位域)
- C++ 枚举、布尔、位域的一些联想
- C++中的位域(bit-filed):一种节省空间的成员
- C++字节对齐和位域(全)(转)
- c++ 位域
- 【C/C++学习笔记】结构体的位域操作小结
- C/C++ 位域知识小结
- 【c++】位域
- C/C++ 位域之一
- C/C++ 位域
- C++中的位域(bit-filed):一种节省空间的成员
- C++位域学习
- c++中冒号(:)和双冒号(::)的用法和c/c++ 位域结构体
- C/C++开发: 位域计算详解 + 例子
- c++_位域&大小端&类型定义符
- c++中冒号(:)和双冒号(::)的用法和c/c++ 位域结构体