高端平面网站,怎么让自己做的网站让别人看到,菏泽市建设银行网站,医疗 企业 网站制作文章目录 一、什么是位段二、位段的内存分配1.位段内存分配规则练习1练习2 三、位段的跨平台问题四、位段的应用五、位段使用的注意事项 一、什么是位段 在上一节中我们讲解了结构体#xff0c;而位段的声明和结构是类似的#xff0c;它们有两个不同之处#xff0c;如下而位段的声明和结构是类似的它们有两个不同之处如下
位段的成员必须是 int、unsigned int 或signed int 在C99中位段成员的类型也可以选择其他类型位段的成员名后边有⼀个冒号和⼀个数字
比如
struct A
{int a:2;int b:5;int c:10;int d:30;
};位段位段那么变量后面的数字有没有可能就是二进制位比如成员a是不是就是占据2个二进制位呢 我们可以通过计算这个位段的大小来试着查看如图 如果它们都按整型算那么应该是16个字节现在却是8个字节说明它还真的有可能是我们说的每个成员后面是存储它们需要的二进制位 但是出了一些意外原本按二进制位算出来应该是47位也就是6个字节为什么这里是8个字节我们慢慢往后学习就知道了
二、位段的内存分配
1.位段内存分配规则
位段的使用有以下三个需要注意的地方
位段的成员可以是 int 、unsigned int 、signed int 或者是 char 等类型位段的空间上是按照需要以4个字节 int 或者1个字节 char 的⽅式来开辟的比如是整型int时就一次性开辟4个字节是字符型char就一次型开辟1个字节当开辟的空间不足时才再次开辟对应的空间不会一次性就开辟完所有空间而是一个一个开辟 接着我们通过举例说明它的特点以及应用
练习1
#include stdio.hstruct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};int main()
{printf(%zd\n, sizeof(struct S));return 0;
}
//空间是如何开辟的首先第一个成员是char类型所以会开辟一个字节的空间然后把a放进去那么a这三个比特位到底怎么存放呢从左边还是从右边开始使用如图 位段从开辟空间的左边还是右边开始使用这是C语言标准未规定的也就是说可能有的编译器实现时从左往右开始使用而有的编译器在实现时又是从右往左开始使用 我们当前的编译器是VS2022这个编译器在使用位段空间时就是采用从右往左开始使用那么按照这个逻辑我们看看能不能算出来位段S的最终大小 所以当成员a放入位段时应该是这个样子 接着我们继续看第二个成员b它占据4个比特位所以我们从右往左继续去存放b如图 接着我们来看第三个成员c它占用5个比特位但是现在开辟的1个字节的空间不够用了所以现在会像第2点说的空间不够时再次开辟对应的空间而这里的c是char类型所以会再次创建一个字节如图 那么此时问题又来了我们的c是会继续使用第一个字节剩下的那个比特位再加上第二个字节的4个比特位这种方式还是会直接浪费掉第一个字节剩下的比特位直接从右向左存放c 这也是C语言未定义的所以有的编译器可能会使用那剩余的比特位而有的编译器不会使用这完全取决于编译器 所以现在我们可以按两种思路分别看看位段S算出来的大小然后再运行代码看当前VS采用的是哪种方法 使用剩余的那个比特位此时我们的c应该占据如图空间 然后这种情况下我们来看看最后一个成员d它占据4个比特位刚好可以放下如图 所以可以看到在1这种情况下位段S只需要2个字节来存储 不使用剩余的那个比特位此时c的存放应该是这样的 存放完c之后我们来看看最后一个成员d占4个比特位很明显第二个字节已经不够用了并且由于我们这种情况不会利用剩余的空间所以还要继续开辟空间这里d也是char所以还是开辟一个字节的空间然后把d放进去如图 所以可以看到在2这种情况下位段S需要3个字节来存储 最后我们来看看这个代码在VS上运行的结果看看最后VS中是使用1的方法还是2的方法如图 我们得到了方法2的结果说明VS中很可能就不会使用剩余的比特位将它浪费掉为了进一步验证我们的想法我们可以对这个位段赋值如下
#include stdio.hstruct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};int main()
{struct S s {0};s.a 10;s.b 12;s.c 3;s.d 4;return 0;
}首先代码创建了一个位段s将它初始化为了全0如下 我们现在讲解时会把这些0暂时去掉方便观察在最后如果有位置没有被修改我们就重新把这些0画出来 接下来就到给位段中的成员赋值了我们将这些值按照刚刚方法2的思路放进去然后通过调试查看内存中是否是这样存放 现在我们开始存放这些数据首先是存放a10的二进制是1010但是成员a只有3个比特位所以会产生截断最后变成010将1截断了我们来通过画图看看a现在是什么样子 接下来我们来看成员b它存放的是12对应的二进制是1100因为b就是占用4个比特位所以可以刚好存放完如图 接下来我们来看c它存放的是3对应的二进制是11由于c占用5个比特位所以我们补0变成5位00011然后我们来看它的存储 最后来看d它存放的是4对应的二进制是100由于d占用4位所以补齐4位为0100然后我们来看看它的存储 由于我们最开始将位段s初始化为了0也就是原本位段s中存储的是全0而我们已经填写的位置就是在全0的基础上进行的修改现在其它的位置没有受到影响就还是初始化时的0如下 由于调试中的内存窗口采用16进制显示所以我们先把这三个字节的二进制转为十六进制方法就是4位二进制转1位十六进制如图 接下来我们在VS上调试这个代码打开内存窗口看看我们的想法和画的图是否正确如下 可以看到最后内存中的结果和我们分析的一模一样随后我们也可以得出最后的结论VS中存放位段时是从右往左存放当空间不够时会开辟新的空间并且不会使用旧空间中的剩余比特位 那么是不是所有编译器都是这样呢这个就不一定了因为这些都是C语言标准未规定的我们在后面也会讲到
练习2 这个练习就是我们开始讲位段的第一个例子如下
#include stdio.hstruct A
{int a : 2;int b : 5;int c : 10;int d : 30;
};int main()
{printf(%zd\n, sizeof(struct A));return 0;
}我们当时运行出来是8个字节我们来看看它是怎么来的 首先第一个成员a是整型所以会首先开辟4个字节然后我们就按从右往左的顺序将a放进去如图 现在开始存放bb是5个比特位剩余位数够用可以直接放进去如图 然后就是存放我们的cc占据10个比特位也是可以直接放进去的如图 现在就差最后一个成员d它占据30个比特位而现在只剩下了32-2-5-1015个比特位很明显不够用了所以我们还要继续开辟一个整型空间来存放d如图 所以最后VS算出来应该就是2个整型的大小也就是8个字节最后我们再来看一次代码运行结果
三、位段的跨平台问题 位段虽然很节省空间但是它却存在很大的跨平台问题可移植性很低因为在C语言标准中位段的很多东西是没有规定的导致各种编译器在实现它的时候采用了不同的方法我们现在来看看具体它的问题在哪里
int 位段被当成有符号数还是⽆符号数是不确定的也就是说可能在有的编译器是有符号数有的是无符号数那么在把int位段当作有符号数的编译器上写的代码可能就不能在把int位段当作无符号数的编译器上运行或者运行后有bug存在等等问题位段中最⼤位的数⽬不能确定因为32位机器的int是2个字节也就是16个比特位64位机器的int是4个字节也就是32个比特位如果把一个int位段成员写成27个比特位那么它在32位上的机器就会出问题位段中的成员在内存中从左向右分配还是从右向左分配C语言标准没有定义所以不同编译器就有不同的实现方法而VS就是采用从右向左的方式其它有的编译器可能就是从左向右分配空间所以在不同编译器运行相同的位段结果也可能不同当⼀个结构包含两个位段第⼆个位段成员比较无法容纳于第⼀个位段剩余的位时是舍弃剩余的位还是利用这是不确定的像VS就会舍弃那些剩余的位直接使用新的空间 所以综上4个理由位段在使用的时候不一定能跨平台也就是可移植性很低但是相比于结构体它能节省很多空间我们继续学习就知道了
四、位段的应用 位段的好处就是可以节省很多空间它可以应用在对空间有严格要求的地方我们现在举一个例子来进行说明 下图是⽹络协议中IP数据报的格式我们可以看到其中很多的属性只需要⼏个bit位就能描述如果使用整型就会浪费空间 这⾥使⽤位段能够实现想要的效果也节省了空间这样⽹络传输的数据报大小也会较小⼀些对网络的畅通是有帮助的 可能有人会问现在我们网络的速度非常快并且电脑内存空间也非常大了没有必要节省这么些空间吧 其实这是不太正确的几个字节可能没有什么影响但是由于网络上的数据量巨大可能会有上亿个几个字节被浪费并且这些空间会造成网络拥堵 我们假设网络就是高速公路而我们的ip数据包就是一辆辆的车我们来比较一下它们不同大小的区别 很明显当我们的ip数据包越大越容易造成网络拥堵所以使用位段来节省这些空间是很有必要的这就是位段的应用之一
五、位段使用的注意事项 位段的⼏个成员共有同⼀个字节这样有些成员的起始位置并不是某个字节的起始位置那么这些位置处是没有地址的因为内存中的地址都是以字节为单位⼀个字节内部的bit位是没有地址的 所以不能对位段的成员使⽤操作符这样就不能使⽤scanf直接给位段的成员输⼊值只能是先输⼊放在⼀个变量中然后赋值给位段的成员 我们先来看错误示范
#include stdio.hstruct A
{int a : 2;int b : 5;int c : 10;int d : 30;
};int main()
{struct A sa { 0 };scanf(%d, sa.b);
}由于这里的b只占据了5个比特的空间而地址都是以字节为单位的所以这里的b并没有地址使用b就是错误的写法 我们来看正确的写法
#include stdio.hstruct A
{int a : 2;int b : 5;int c : 10;int d : 30;
};int main()
{int b 0;scanf(%d, b);sa.b b;return 0;
}这里我们重新创建了一个整型变量b这个时候就可以对它取地址了然后用户对b输入后再录入我们位段的成员b这样就可以使用位段存储用户输入的数据了 结构体实现位段的全部内容我们都已经讲完了可能有一点点难只要多练习自己去画画图相信一定可以理解的如果有什么问题欢迎提问 那么今天的内容就到这里bye~