-
2021-03-10 10:47:00
随笔 - 30 文章 - 0 评论 - 36 阅读 - 11万
C结构体之位域(位段)
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:struct 位域结构名
{位域列表
};
其中位域列表的形式为:类型说明符 位域名:位域长度
位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:struct bs
{
int a:8;
int b:2;
int c:6;
}data;
说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:- 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs
{
unsigned a:4
unsigned b:5 /从下一单元开始存放/
unsigned c:4
}
2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度。- 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
复制代码
struct k
{
int a:1
int :2 /无位域名,该2位不能使用/
int b:3
int c:2
};
复制代码
二、位域的使用下面例子是参加一个公司(白领科技-青岛)的笔试遇到的,当时做错了,为了怕忘了,赶紧写下来。
复制代码
1 #include
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机器上输出:$ ./langxun.exe
-16
1
解析:在默认情况下,为了方便对结构体内元素的访问和管理,当结构体内的元素长度都小于处理器的位数的时候,便以结构体里面最长的元素为对其单位,即结构体的长度一定是最长的数据元素的整数倍;如果有结构体内存长度大于处理器位数的元素,那么就以处理器的位数为对齐单元。由于是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)
三、位域的对齐
如果结构体中含有位域(bit-field),那么VC中准则是:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;
系统会先为结构体成员按照对齐方式分配空间和填塞(padding),然后对变量进行位域操作
更多相关内容 -
C语言结构体之位域详解
2020-07-25 18:41:59文章主要对C语言结构体的位域知识进行详细介绍。 -
详解C语言位域的使用与注意事项
2021-01-20 06:23:58位域的定义 有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种... -
C语言 位域详解及示例代码
2021-01-20 05:55:58正是基于这种考虑,C语言又提供了一种叫做位域的数据结构。 在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),这就是位域。请看下面的例子: struct bs{ unsigned m; unsigned n: 4; ... -
详细解读C++编程中的匿名类类型和位域
2020-09-02 21:05:50主要介绍了C++编程中的匿名类类型和位域,是C++入门学习中的基础知识,需要的朋友可以参考下 -
bitfields:JavaScript中的简单位域
2021-04-13 19:58:32位域 适用于Node.js和浏览器的易于使用的位域。 安装 对于Node.js或webpack项目,请使用NPM软件包管理器进行安装: npm install --save bitfields 要在没有捆绑程序的浏览器中使用,请在HTML中包含此脚本标签。 ... -
简单了解Java位域的一些知识
2020-08-25 20:56:47主要介绍了简单了解Java位域的一些知识,这个概念是在 Effective Java中了解到的, 可以通过EnumSet来代替位域这种方式表达,需要的朋友可以参考下 -
浅析C语言位域和位段
2020-09-05 02:29:51以下是对C语言中的位域和位段进行了详细的分析介绍,需要的朋友可以过来参考下 -
C++ 位域
2019-08-13 01:23:24NULL 博文链接:https://jacky-dai.iteye.com/blog/2306126 -
--置位域复位域运用计时器.pdf
2021-12-03 18:16:501200案例复位置位练习 -
Java中EnumSet代替位域代码详解
2020-08-28 02:41:34主要介绍了Java中EnumSet代替位域代码详解,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下 -
关于大小端、位域的一些概念详解
2020-09-04 23:53:33我们常用的x86结构都是小端模式,而大部分DSP,ARM也是小端模式,不过有些ARM是可以选择大小端模式。所以对于上面的maxHeight是应该以小端模式来存放,具体情况请看下面两表 -
数据结构位域
2015-08-13 16:47:54位域是c语言提供的一种数据结构,目的是为了节省存储空间 -
structex:Go结构注释,支持编码和解码; 类似于C样式的位域。 支持位域打包,自描述布局参数和对齐
2021-03-25 15:17:19结构扩展 structex提供了注释规则,该规则扩展了Go结构,以实现字节支持的数据帧的编码和解码。 structex旨在structex表示可直接嵌入代码中的行业标准数据格式的过程。 例子 给定SCSI查询数据的前三个字节T10 SCSI... -
C 结构体位域.docx
2019-09-10 09:54:08所谓“位域”是把一个字节中的二进位划分为几 个不同的区域,并说明每个区域的位数。本文档用例子描述了C结构体位域的应用,简单易懂,相信初学者很快就能掌握。 -
聊一聊C语言位域/位段
2022-03-05 17:53:09有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用 0 和 1 表示足以,也就是用一个二...正是基于这种考虑,C语言又提供了一种叫做位域的数据结构。目录
在做嵌入式开发的时候,我们经常会遇到这样的代码:
struct { unsigned int widthValidated : 1; unsigned int heightValidated : 1; } status;
这样定义结构体变量是什么意思呢?
主要原因是:有些信息在存储时,只需占几个或一个二进制位(bit),并不需要占用一个完整的字节。例如,在存放一个开关量时,只有0和1两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言提供了一种数据结构,称为“位域”或“位段”。
1、概念和定义
位域:是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
位域定义与结构定义相仿,其形式为:
struct 位域结构名 { 位域列表 };
其中位域列表的形式为:
type [member_name] : width ;
下面是有关位域中变量元素的描述:
位域的使用和结构体成员的使用相同,其一般形式为:
位域变量名.位域名 位域变量名->位域名
位域最大的作用就是节省存储空间,在本质上就是一种结构类型,不过其成员是按二进位分配的。例如以下案例:
#include <stdio.h> #include <string.h> /* 定义简单的结构 */ struct { unsigned int widthValidated; unsigned int heightValidated; } status1; /* 定义位域结构 */ struct { unsigned int widthValidated : 1; unsigned int heightValidated : 1; } status2; int main( ) { printf( "Memory size occupied by status1 : %d\n", sizeof(status1)); printf( "Memory size occupied by status2 : %d\n", sizeof(status2)); return 0; }
代码被编译和执行时,它会产生下列结果:
Memory size occupied by status1 : 8 Memory size occupied by status2 : 4
结构体status1是由正常的两个unsigned int类型变量组成,占用内存是8字节,结构体status2也是unsigned int类型变量,但是它仅使用了一个unsigned int类型内存的前2bit,实际上还有30bit没使用,所以占用内存是4字节。
对于位域的定义有以下几点说明:
- 一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs{ unsigned a:4; unsigned :4; /* 空域 */ unsigned b:4; /* 从下一单元开始存放 */ unsigned c:4 }
在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位。
-
位域的宽度不能超过它所依附的数据类型的长度,成员变量都是有类型的,这个类型限制了成员变量的最大长度,: 后面的数字不能超过这个长度。
-
位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
struct k{ int a:1; int :2; /* 该 2 位不能使用 */ int b:3; int c:2; };
- 当相邻成员的类型相同时,如果它们的位宽之和小于类型的 sizeof 大小,那么后面的成员紧邻前一个成员存储,直到不能容纳为止;如果它们的位宽之和大于类型的 sizeof 大小,那么后面的成员将从新的存储单元开始,其偏移量为类型大小的整数倍。例如:
#include <stdio.h> int main(){ struct bs{ unsigned m: 6; unsigned n: 12; unsigned p: 4; }; printf("%d\n", sizeof(struct bs)); return 0; }
运行结果:
4
m、n、p 的类型都是 unsigned int,sizeof 的结果为 4 个字节(Byte),也即 32 个位(Bit)。m、n、p 的位宽之和为 6+12+4 = 22,小于 32,所以它们会挨着存储,中间没有缝隙。
- 当相邻成员的类型不同时,不同的编译器有不同的实现方案,GCC 会压缩存储,而 VC/VS 不会。例如:
#include <stdio.h> int main(){ struct bs{ unsigned m: 12; unsigned char ch: 4; unsigned p: 4; }; printf("%d\n", sizeof(struct bs)); return 0; }
在 GCC 下的运行结果为 4,三个成员挨着存储;在 VC/VS 下的运行结果为 12,三个成员按照各自的类型存储(与不指定位宽时的存储方式相同)。
- 如果成员之间穿插着非位域成员,那么不会进行压缩。 例如:
struct bs{ unsigned m: 12; unsigned ch; unsigned p: 4; };
在各个编译器下 sizeof 的结果都是 12。
注意:位域成员往往不占用完整的字节,有时候也不处于字节的开头位置,因此使用
&
获取位域成员的地址是没有意义的,C语言也禁止这样做。地址是字节(Byte)的编号,而不是位(bit)的编号。2、实例
通过一个示例,加深对位域的理解和应用:
#include <stdio.h> #include <string.h> struct { unsigned int age : 3; } Age; int main( ) { Age.age = 4; printf( "Sizeof( Age ) : %d\n", sizeof(Age) ); printf( "Age.age : %d\n", Age.age ); Age.age = 7; printf( "Age.age : %d\n", Age.age ); Age.age = 8; // 二进制表示为 1000 有四位,超出 printf( "Age.age : %d\n", Age.age ); return 0; }
当上面的代码被编译时,它会带有警告,当上面的代码被执行时,它会产生下列结果:
Sizeof( Age ) : 4 Age.age : 4 Age.age : 7 Age.age : 0
当执行到Age.age = 8;时,二进制表示为:1000 有四位,超出位域,所以会提示警告。
-
位域 bit field
2021-02-12 03:07:17一、位域有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据...一、位域
有些信息在存储时,并不需要占用一个完整的字节,
而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,
用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几
个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位
域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:
struct 位域结构名
{ 位域列表 };
其中位域列表的形式为: 类型说明符 位域名:位域长度
例如:
struct bs
{
int a:8;
int b:2;
int c:6;
};
位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:
struct bs
{
int a:8;
int b:2;
int c:6;
}data;
说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:
1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs
{
unsigned a:4
unsigned :0 /*空域*/
unsigned b:4 /*从下一单元开始存放*/
unsigned c:4
}
在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。
3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
struct k
{
int a:1
int :2 /*该2位不能使用*/
int b:3
int c:2
};
从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。
二、位域的使用
位域的使用和结构成员的使用相同,其一般形式为: 位域变量名·位域名 位域允许用各种格式输出。
main(){
struct bs
{
unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
bit.a=1;
bit.b=7;
bit.c=15;
printf("%d,%d,%d\n",bit.a,bit.b,bit.c);
pbit=&bit;
pbit->a=0;
pbit->b&=3;
pbit->c|=1;
printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);
}
上例程序中定义了位域结构bs,三个位域为a,b,c。说明了bs类型的变量bit和指向bs类型的指针变量pbit。这表示位域也是可以使用指针的。
程序的9、10、11三行分别给三个位域赋值。(
应注意赋值不能超过该位域的允许范围)程序第12行以整型量格式输出三个域的内容。第13行把位域变量bit的地址送给指针变量pbit。第14行用指针
方式给位域a重新赋值,赋为0。第15行使用了复合的位运算符"&=", 该行相当于:
pbit->b=pbit->b&3位域b中原有值为7,与3作按位与运算的结果为3(111&011=011,十进制值
为 3)。同样,程序第16行中使用了复合位运算"|=", 相当于:
pbit->c=pbit->c|1其结果为15。程序第17行用指针方式输出了这三个域的值。
这也是在ChinaUnix上看了几篇关于C语言'位域(Bit
Fields)'的帖子之后,才想写下这篇文章的。其实在平时的工作中很少使用到'位域',我是搞服务器端程序设计的,大容量的内存可以让我毫不犹豫的任
意'挥霍'^_^。想必搞嵌入式编程的朋友们对位域的使用应该不陌生吧。这里我也仅仅是凭着对C语言钻研的兴趣来学习一下'位域'的相关知识的,可能有些
说法没有实践,缺乏说服力。
具体也不是很清楚当年C语言的创造者为什么要加入位域这一语法支持,那是太遥远的事情了,我们不需要再回顾了,既然大师们为我们创造了它,我们使用便是了。
毋庸置疑,位域的引入给用户的最大的好处莫过于可以有效的利用'昂贵'的内存和操作bit的能力了。而且这种操作bit位的能力很是方便,利用结构体域名即可对这些bit进行操作。例如:
struct foo {
int a : 1;
int b : 2;
short c : 1;
};
struct foo aFoo;
aFoo.a = 1;
aFoo.b = 3;
aFoo.c = 0;
通过结构体实例.域名即可修改某些bit得值,这些都是编译器的'甜头'。当然我们也可以自己通过一些'掩码'和移位操作来修改这些bit,当然如果不是十分需要,我们是不需要这么做的。
位域还提供一种叫'匿名'位域的语法,它常用来'填缺补漏',由于是'匿名',所以你不能像上面那样去访问它。如:
struct foo1 {
int a : 1;
int : 2;
short c : 1;
};
在foo1的成员a和c之间有一个2 bits的匿名位域。
在foo结构体的定义中,成员a虽然类型为int,但是它仅仅占据着4个字节中的一个bit的空间;类似b占据2个bit空间,但是b到底是占据第一个
int的2个bit空间呢还是第二个int的2个bit空间呢?这里实际上也涉及到如何对齐带有'位域'的结构体这样一个问题。我们来分析一下。
我们再来看看下面两个结构体定义:
struct foo2 {
char a : 2;
char b : 3;
char c : 1;
};
struct foo3 {
char a : 2;
char b : 3;
char c : 7;
};
我们来打印一下这两个结构体的大小,我们得到的结果是:
sizeof(struct foo2) = 1
sizeof(struct foo3) = 2
显然都不是我们期望的,如果按照正常的内存对齐规则,这两个结构体大小均应该为3才对,那么问题出在哪了呢?首先通过这种现象我们可以肯定的是:带有'位
域'的结构体并不是按照每个域对齐的,而是将一些位域成员'捆绑'在一起做对齐的。以foo2为例,这个结构体中所有的成员都是char型的,而且三个位
域占用的总空间为6 bit 但是三个成员位域所占空间之和为9 bit > 8 bit(1 byte),个人认为这句话应该改为:但是Foo3的前两个成员所占空间是2+3=5,所以第一个字节只剩下8-5=3,不够存放第三个成员的7个字节,而规则要求,不允许成员跨字节存放,所以只能另外将此7字节单独分配,故又占了一个字节,这里位域是不能跨越两个成员基本类型空间的,这时编译器将a和b两个成员'捆绑'按照char做对齐,而c单独拿出来以char类型做对齐,这样实际上在b和c之间出现了空隙,但这也是最节省空间的方法了。我们再看一种结构体定义:
struct foo4 {
char a : 2;
char b : 3;
int c : 1;
};
在foo4中虽然三个位域所占用空间之和为6 bit
通过上面的例子我们发现很难总结出很规律性的东西,但是带有'位域'的结构体的对齐有条原则可以遵循,那就是:"尽量减少结构体的占用空间"。当然显式的使用内存对齐的机会也并不多。^_^
-
C语言位域
2021-05-19 07:41:47在求职笔试中,C中的位域是一个常考点,特别是在嵌入式软件中更常见。位域的最大好处是可以根据自己需要定制位数,从而节省空间,例如:嵌入式编程稀缺的内存资源。还有在网络通讯中,对头信息部分的结构定义也常...在求职笔试中,C中的位域是一个常考点,特别是在嵌入式软件中更常见。位域的最大好处是可以根据自己需要定制位数,从而节省空间,例如:嵌入式编程稀缺的内存资源。还有在网络通讯中,对头信息部分的结构定义也常用到位域,少传一位是一位啊。
这里来分析EMC的一道笔试题(07年招聘试题):
1
typedef struct
bitstruct 2 { 3 int b1:5; 4 int :2; 5 int b2:2; 6 }bitstruct; 7 int main(int argc, char
*argv[]) 8 { 9 bitstruct b; 10
printf("%d\n",sizeof(bitstruct));
11
memcpy(&b,"EMC
Examination",sizeof(b));
12
printf("%d,%d\n",b.b1,b.b2); 13
return
0; 14
}
请问在little-endian systems(系统默认的存放顺序)中,输出结果是什么?
答案是
45,-2;
所需知识点:
1.位域的概念和特点
C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”(bit
field)。(1)位段成员的类型必须指定为unsigned或int类型;(2)若某一位段要从另一个字开始存放,用:0长度为0的空位段,作用就是使下一个位段从下一个存储单位(视不同编译系统而异)开始存放;(3)一个位段必须存储在同一存储单元中,不能跨两个单元;(4)可以定义无名字段例如":2";(5)位段的长度不能大于存储单元的长度,也不能定义位段数组;(6)位段可以用整形格式符输出;(7)位段可以在数值表达式中引用,它会被系统自动地转换成整形数。[1][3]
2.Little-endiansystems的内存布局特点
先问一个问题:Endian这个词是什么意思?
“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。
我们一般将endian翻译成“字节序”,将big
endian和little
endian称作“大尾”和“小尾”。这也在当今的CPU派别中一样存在。Motorola的PowerPC系列CPU采用的Big-endian,
Intel的X86系列CPU采用的是Little-endian。Little-endian的特点是高高低低,即高位地址存放最高有效字节,低位地址存放最低有效字节;而Big-endian正好相反。下面用图的方式说明起来更直观,例如0x12345678(特别注意,这是单个数,不是字符串,如果是字符串就不一定这样了。之前没有特别注意这点,害的我多花了冤枉时间)。
Big Endian
低地址高地址----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little
Endian
低地址高地址----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
上面的字节序的不同,在单机的操作中没有问题,因为一台单机就是采用单一的字节序嘛!但是在两个不同的主机进行协作时,就会出现问题了;另外在网络通讯中也同样会出现问题,详细参考[2]
就单个字节而言,也会有这样的问题:比特序有差别吗?
也分成两种序,如果我们处理的基本单位是字节以上的话,对此就不必担心了,因为CPU存储操作的最小单元是字节,所以比特位的顺序对我们来就是透明的,我们在读取某个字节时,不管它用的是Big
endian 还是Little
endian,我们读到的都是一个同样的字节,只不过硬件在读写时的顺序,一个是从高到底另一个是从低到高,对我们的使用不产生影响。但是如果涉及到位域的存放问题,还是要特别小心,上面这道题就是一个非常的好的例子。
Big Endian
msb lsb
---------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little
Endian
lsb msb
---------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3.memcpy()与strcpy()的区别
这个问题很简单,可以想象成这样:memcpy的基本单位是位,strcpy的基本单位是字符。所以,memcpy会根据提供的长度完全按位拷贝,而strcpy是两个字符串之间的拷贝。
回来原来的问题上,在采用little
endian的BUS64中,struct
bitstruct在没涉及到高低位问题时,也就是我们平时常会画出的一种形式是:
{b1 b1 b1 b1 b1 Ø Ø b2 b2 Ø Ø Ø
Ø Ø Ø Ø ØØØØØØØØ ØØØØØØØØ};
在内存中的实际布局是:
低地址高地址-------------------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|b2Ø Øb1
b1b1 b1
b1|Ø Ø Ø Ø Ø Ø
Ø b2|ØØØØØØØØ |ØØØØØØØØ|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Ø表示空填充,这当中包含一个原则:一个位域的存放不可以跨字存放,但可以跨字节存储;这里采用的比特续也是little
endian,说明了比特序对操作也是有影响的嘛!
memcpy按位拷贝“EMC Examination”到b中,一个字符是8位; 又因为sizeof(b)=4,所以只写入"EMC
"。"EMC "对应的位序列是:{0100 0101 0100
1101 0100 0011 0010
0000},从这里写到内存里的形式是:(在这里不要被little
endian给迷惑了,E不是放在高地址,参看上面红色的特别注意)
低地址高地址-------------------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|b2Ø
Øb1
b1b1 b1
b1|Ø
Ø Ø Ø Ø Ø Øb2|ØØØØØØØØ|ØØØØØØØØ||0 1 0 0 0 1 0 1|0 1 0 0 1 1
0 1|0100
0011|0010
0000|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+printf("%d,%d\n",b.b1,b.b2);读出来的b1是00101,它的值是5;
b2是10,转换成十进制是-2。到了这一步,似乎题目已经解答出来了,但还有两个地方有疑惑:
1.struct bitstruct在内存中的位存放顺序是这样的么?
低地址高地址-------------------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|b2Ø
Øb1
b1b1 b1
b1|Ø
Ø Ø Ø Ø Ø Øb2|ØØØØØØØØ|ØØØØØØØØ|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.memcpy函数在按位拷贝时的拷贝顺序是这样的么?
低地址高地址-------------------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|01000101|01001101|0100
0011|0010
0000|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
关于上面两个问题的验证工作留给路过的朋友来做:-P ..... 欢迎批评指正
注解:转载自(忘了什么地方)
-
C语言 位域的使用
2021-06-09 10:52:19目录什么是位域位域的定义位域的使用使用位域的注意点(重要)实际应用 什么是位域 有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有 0 和 1 两种状态,用... -
吕鑫:最博大精深的C语言视频教程 第22天 【第3堂课】位域结构体与联合体
2018-12-04 14:23:131、讲解位域结构体的原理和使用方法以及字节对齐问题; 2、讲解联合的概念以及联合体成员变量的内存分布;