代码编织梦想

前言

C 语言提供了一种更好的利用内存空间的方式。可以定义变量的宽度来告诉编译器,您将只使用这些字节

位域

如果程序的结构中包含多个开关量,只有 TRUE/FALSE 变量,如下:

struct
{
unsigned int widthValidated;
unsigned int heightValidated;
} status;

这种结构需要 8 字节的内存空间,但在实际上,在每个变量中,我们只存储 0 或 1。在这种情况下,C 语言提供了一种更好的利用内存空间的方式。如果您在结构内使用这样的变量,您可以定义变量的宽度来告诉编译器,您将只使用这些字节。例如,上面的结构可以重写成:

struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status;

现在,上面的结构中,status 变量将占用 4 个字节的内存空间,但是只有 2 位被用来存储值。如果您用了 32 个变量,每一个变量宽度为 1 位,那么 status 结构将使用 4 个字节,但只要您再多用一个变量,如果使用了 33 个变量,那么它将分配内存的下一段来存储第 33 个变量,这个时候就开始使用 8 个字节。让我们看看下面的实例来理解这个概念:

#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

位域声明

有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有 0 和 1 两种状态,用 1 位二进位即可。为了节省存储空间,并使处理简便,C 语言又提供了一种数据结构,称为"位域"或"位段"。

所谓"位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

典型的实例:

  • 用 1 位二进位存放一个开关量时,只有 0 和 1 两种状态。
  • 读取外部文件格式——可以读取非标准的文件格式。例如:9 位的整数。

位域的定义和位域变量的说明
位域定义与结构定义相仿,其形式为:

struct 位域结构名 
{

 位域列表

};

其中位域列表的形式为:

type [member_name] : width ;
下面是有关位域中变量元素的描述:

元素描述
type只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型,决定了如何解释位域的值。
member_name位域的名称。
width位域中位的数量。宽度必须小于或等于指定类型的位宽度。

带有预定义宽度的变量被称为位域。位域可以存储多于 1 位的数,例如,需要一个变量来存储从 0 到 7 的值,您可以定义一个宽度为 3 位的位域,如下:

struct
{
  unsigned int age : 3;
} Age;

上面的结构定义指示 C 编译器,age 变量将只使用 3 位来存储这个值,如果您试图使用超过 3 位,则无法完成。

struct bs{
    int a:8;
    int b:2;
    int c:6;
}data;

data 为 bs 变量,共占两个字节。其中位域 a 占 8 位,位域 b 占 2 位,位域 c 占 6 位。

让我们再来看一个实例:

struct packed_struct {
  unsigned int f1:1;
  unsigned int f2:1;
  unsigned int f3:1;
  unsigned int f4:1;
  unsigned int type:4;
  unsigned int my_int:9;
} pack;

在这里,packed_struct 包含了 6 个成员:四个 1 位的标识符 f1…f4、一个 4 位的 type 和一个 9 位的 my_int。

让我们来看下面的实例:

#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

对于位域的定义尚有以下几点说明:

  • 一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
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;
};

从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。

位域的使用

位域的使用和结构成员的使用相同,其一般形式为:

位域变量名.位域名
位域变量名->位域名
位域允许用各种格式输出。

请看下面的实例:

实例

#include <stdio.h>
 
int 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;    /* 把位域变量 bit 的地址送给指针变量 pbit */
    pbit->a=0;    /* 用指针方式给位域 a 重新赋值,赋为 0 */
    pbit->b&=3;    /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
    pbit->c|=1;    /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
    printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);    /* 用指针方式输出了这三个域的值 */
}

上例程序中定义了位域结构 bs,三个位域为 a、b、c。说明了 bs 类型的变量 bit 和指向 bs 类型的指针变量 pbit。这表示位域也是可以使用指针的。

结构体内存分配原则

原则一:结构体中元素按照定义顺序存放到内存中,但并不是紧密排列。从结构体存储的首地址开始 ,每一个元素存入内存中时,它都会认为内存是以自己的宽度来划分空间的,因此元素存放的位置一定会在自己大小的整数倍上开始。

原则二: 在原则一的基础上,检查计算出的存储单元是否为所有元素中最宽的元素长度的整数倍。若是,则结束;否则,将其补齐为它的整数倍。

测试实例:

#include <stdio.h>

typedef struct t1{
    char x;
    int y;
    double z;
}T1;

typedef struct t2{
    char x;
    double z;
    int y;
}T2;

int main(int argc, char* argv[])
{
    printf("sizeof(T1) = %lu\n", sizeof(T1));
    printf("sizeof(T2) = %lu\n", sizeof(T2));

    return 0;
}

输出:

sizeof(T1) = 16
sizeof(T2) = 24

解析

sizeof(T1.x) = sizeof(T2.x) = 1;
sizeof(T1.y) = sizeof(T2.y) = 4;
sizeof(T1.z) = sizeof(T2.z) = 8;

T1: 若从第 0 个字节开始分配内存,则 T1.x 存入第 0 字节,T1.y 占 4 个字节,由于第一的 4 字节已有数据,所以 T1.y 存入第 4-7 个字节,T1.z 占 8 个字节,由于第一个 8 字节已有数据,所以 T1.z 存入 8-15 个字节。共占有 16 个字节。

T2: 若从第 0 个字节开始分配内存,则 T1.x 存入第 0 字节,T1.z 占 8 个字节,由于第一的 8 字节已有数据,所以 T1.z 存入第 8-15 个字节,T1.y 占 4 个字节,由于前四个 4 字节已有数据,所以 T1.z 存入 16-19 个字节。共占有 20 个字节。此时所占字节不是最宽元素(double 长度为 8)的整数倍,因此将其补齐到 8 的整数倍,最终结果为 24。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_62052372/article/details/128750968

c语言学习笔记-结构体-位域_哈哈哈士奇的博客-爱代码爱编程

#include "pch.h" #include <iostream> /*本文演示结构体struct的用法,还有位域 数组允许定义可存储相同类型数据项的变量,结构是 C 编程中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。 为了定义结构,您必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数

c语言学习笔记--c结构体_yunfei_chen的博客-爱代码爱编程

结构体 C数组允许定义可存储相同类型数据项的变量,结构是C编程中另一种用户自定义的可用的数据类型,它允许存储不同类型的数据项。 结构用于表示一条记录,假设你想要跟踪图书馆中书本的动态,可能需要跟踪每本书的下列属性:Titl

c语言学习笔记--c共用体_yunfei_chen的博客-爱代码爱编程

共用体 共用体是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。 定义共用体 为了定义共用体,

c语言学习笔记--c位域&typedef_yunfei_chen的博客-爱代码爱编程

位域 如果程序的结构中包含多个开关量,只有TRUE/FALSE变量,如下: struct { unsigned int widthValidated; unsigned int heightValidated;

C语言学习笔记之结构体-爱代码爱编程

主题:C语言 时间:2021年1月6日 作者:ybb 参考: C语言 10.3 C语言结构体指针 定义:当一个指针变量指向结构体时,我们就称它为结构体指针(指针变量指向的是结构体) 一般结构:struct 结构体标签名 *变量名 struct stu *pstu=&stu1; 注:结构体变量名和数组名不同,数组名在表达式中会被转换成指针

【编程知识】c语言位域-爱代码爱编程

一、 位域的概念 有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间, 并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在

C语言学习笔记-爱代码爱编程

数据类型 基本类型:整数类型、浮点类型void类型 :表示没有可用的值枚举类型:表示定义离散数据派生类型:比如:指针类型、数组,结构类型,函数类型,共用体类型。其中数组和结构称为聚合类型,函数类型指的是函数返回值的类型。C 中的变量声明 变量的声明有两种情况: 一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。另一种是不需

c语言数组实际作用,C语言学习笔记3——数组-爱代码爱编程

一、什么叫数组? 数组:顾名思义就是很多数的组合。 特点是:1、这些数的类型都是相同的;2、在数组里面,这些数在内存里是连续储存的,是一组有序数据的集合;3、用一个数组名和下标来唯一地确定数组中的元素。 二、一维数组 1、一维数组的定义 一般形式:数据类型 数组名 [ 常量表达式] 例如:定义一个有10个整型元素,名叫a的数组 int a[1

C语言学习笔记之内存精讲-爱代码爱编程

主题: 简介: 参考: 作者: 时间: 14.1 存储在硬盘中的程序需要载入内存才能运行,CPU也只能从内存中读取数据和指令,对于CPU而言,memory仅仅存放instruction和data,不能在memory完成运算,任何计算都需要读取到CPU内部才能进行运算。 CPU——Memory——Disk CPU:运算单元、寄存器、缓存 缓存:如果每次都从

0808学习笔记(位域、位运算符)_赵小坏记大过!的博客-爱代码爱编程

关于位域 #include <stdio.h> void test_bit(); /*关于位域*/ void test_bit() { struct bit_size { int a:2;//一个int类型是32个位,“:”后面指定所占位数 int b:5;//定义位段:成员名后加“:”和指定所占位数 int c:10;

c语言学习笔记1_ebeae_www的博客-爱代码爱编程

1、const定义常量,将变量转换为常量 语法:const int a = 1;//a不能被修改 2、static修饰全局变量的话,外部链接属性会变成内部链接属性,只能在自己所在源文件内使用 static保持局部变量的存在,注:作用域不会被改变 #include <stdio.h> /* 函数声明 */ void func1(void

c语言——位段-爱代码爱编程

文章目录 思维导图:一. 什么是位段二.位段的内存分配三.位段的跨平台问题四.位段的应用结语: 思维导图: 一. 什么是位段 位段的声明和结构体类似,但是有2个不同: 位段的成员必须是int

java基础学习 day34(stringbuilder,链式编程)-爱代码爱编程

1. StringBuilder概述 StringBuilder可以看作是一个容器,创建之后里面的内容是可变的作用:提高字符串的操作效率 例如: String s1 = “aaa”; String s2 = “bbb”;

在线教育-谷粒学院学习笔记(六)-爱代码爱编程

文章目录 1 内容介绍2 课程大纲列表后端实现3 课程大纲列表前端实现4 修改课程基本信息5 课程章节操作6 课程小节操作7 课程信息确认8 课程最终发布 1 内容介绍 课程大纲管理 课程大纲列表显

nlp学习笔记(六) transformer简明介绍-爱代码爱编程

大家好,我是半虹,这篇文章来讲 Transformer \text{Transformer}

c语言学习笔记——冒号的用法之位域_冒号 位域-爱代码爱编程

参考链接 有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”

c语言学习笔记8——构造类型-爱代码爱编程

1.结构体 1)结构体类型描述不占内存空间(struct xx{ };整个部分都是类型描述或者叫变量类型),所以不能在定义结构体时给结构体成员变量赋值。类似int i;中是i而非int占内存空间 2)结构体定义的最后勿忘写分号“;” 3)结构体定义放在函数外,防止有些校验严格的编译器不认识这种定义方式 4)结构体变量名也仅仅是结构体的起始位置地址,