C语言自定义数据类型详解(二)——结构体类型(下)

news2025/3/3 22:15:10

书接上回,前面我们已经给大家介绍了如何去声明和创建一个结构体,如何初始化结构体变量等这些关于结构体的基础知识。下面我们将继续给大家介绍和结构体有关的知识:

今天的主题是:结构体大小的计算并简单了解一下位段的相关知识。

目录

一、结构体的内存对齐

(1)发现并提出问题: 

(2)内存对齐规则:

(3)解决问题:

附一:offsetof宏的介绍:

(4)为什么会存在结构体的内存对齐:

附二:带来的启发:

附三:修改编译器的默认对齐数:

附四:关于前面问题的解答:

二、关于位段:

(1)搭建应用场景:

(2)位段的概念性理解:

(3)位段的内存分配:


学习指南:如果你是期末备考的大学生,简单学习博主的结构体类型(上)的内容就够了。如果你对自己有更高的要求,希望系统深入了解结构体知识可以继续跟上博主的步伐啦!

一、结构体的内存对齐

(1)发现并提出问题: 

如果要谈结构体大小的计算,就不得不提结构体的底层特性——内存对齐。什么是内存对齐呢?我们先来看一下,下面这两个例子:

#include<stdio.h>
struct S1
{
	char c1;
	char c2;
	int i;
};

int main()
{
    printf("%zd\n", sizeof(struct S1));
    return 0;
}

 这个结构体大小是多少呢?是不是 1 + 1 + 4(sizeof(char) + sizeof(char) + sizeof(int))的结果呢?,我们不妨在自己的机器上运行一下。

下面这张截图是在博主2022 Visual Studio这款IDE上运行的结果:

这个结果挺莫名奇妙的,我一时也想不清楚这个结果为啥是这个。嗯……我们现在不妨再来做一个事情,我们继续声明和创建一个S2的结构体,然后呢,这个结构体有和S1一样的成员变量(c1,c2,i)。然后只是改一下它们之间的顺序,我们来看一下这个结构体变量的大小又是多少。如图所示:

#include<stdio.h>
struct S1
{
	char c1;
	char c2;
	int i;
};
struct S2
{
	char c1;
	int i;
	char c2;
};

int main()
{
	printf("%zd\n", sizeof(struct S1));
	printf("%zd\n", sizeof(struct S2));
	return 0;
}

运行截图:

 这也太不可思议了,我仅仅是改了一下结构体各个成员变量的顺序,竟然就改变了整个结构体变量的大小。显然种种迹象均表明:结构体变量的大小并不是各个成员变量大小的简单相加。

那这具体是怎回事呢,欸,我们接着往下探索!

(2)内存对齐规则:

前面我们通过实验发现,结构体变量的大小不是其各个成员变量的简单相加。其实其本质的原因就在于有内存对齐的底层特性存在。

那关于结构体的内存对齐有哪些规则呢?这里博主总结了关于内存对齐这一块,大致有以下四条规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处;
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处;
  3. 结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍;
  4. 如果嵌套了结构体,嵌套结构体要对齐到自己的最大对齐数的整数倍处,其他规则照旧。

上述规则中的一些概念,我们这里做简单的阐释。首先关于什么是对齐数

对齐数 = Min(编译器默认的一个对齐数,成员变量自身的大小)

一般来说:结构体的每一个成员变量都有自己的一个对齐数,不同大小的成员变量一般有不同的对齐数,其中我们把一个结构体它所有成员变量中最大的那个对齐数,称之为最大对齐数。

特别地,如果成员变量是一个数组。那这里成员变量的大小,注意啦,不是指数组的大小,而是数组中单个元素的大小。我们下面以VS 2022为例子(编译器的默认对齐数是8),来说明这个问题:

struct S
{
    char c;       //8 < 1 —— 对齐数是1
    int  i;       //8 < 4 —— 对齐数是4
    char name[12];//8 < 12 ?No,No,No!    8 < 1 ——对齐数是1
};

另外关于编译器的默认对齐数,如果通过查阅相关编译器的技术文档,你能够查到该编译器的默认对齐数那当然再好不过了。但是如果说你实在查不到,那你就忽略这个编译器的默认对齐数(或者说假设它是无穷大)。如果说此时发现假设和实际结果不相一致,我们通过对结果进行分析,也可以推导出编译器的默认对齐数的大小。

那话说回来,我们还是希望大家记一些常见的关于编译器的默认对齐数:像VS系列都是8,然后gcc系列是无穷大,无穷大的另一种理解就是:对齐数 = 成员变量自身的大小。

(3)解决问题:

OK,当然啦,说了这么多,我们就可以分析和解决前面所提到的那个问题。我们这里假设是在VS 2022的环境下,也就是说编译器的默认对齐数是8。

#include<stdio.h>
struct S1
{
	char c1;
	char c2;
	int i;
};
struct S2
{
	char c1;
	int i;
	char c2;
};

int main()
{
	printf("%zd\n", sizeof(struct S1));
	printf("%zd\n", sizeof(struct S2));
	return 0;
}

对于结构体struct S1而言,它的内存布局即为下图所示:

分析:

红色块是c1的内存空间,旁边的数字对应的是这个内存块相较于结构体起始地址的偏移量。

由于c1是第一个成员,因此偏移量为0。而且c1只需要1个字节的空间,综上红色块就是c1的内存空间。

绿色块是c2的内存空间,这是因为: 8(编译器默认对齐数)< sizeof(c2) == 1,因此对于c2来说它的对齐数就是1,也就是说c2内存的起始位置在偏移量为1的倍数的位置上,显然就要我们绿色块的位置上。

蓝色块是i的内存空间,这是因为:8(编译器默认对齐数)< sizeof(i) == 4,因此对于i来说它的对齐数就是4,偏移量为4的倍数的位置,恰好就是对应我们蓝色块的起始位置。然后往下延展4个字节,这是因为sizeof(i) == 4。

最后,显然最大对齐数就是4,此时c1 + c2 + i的内存大小是8,正好就是4的倍数,因此结构体的内存大小是8。

 

对于结构体struct S2而言,它的内存布局即为下图所示:

分析:

c1,i,c2的分析略,这里我们简单说一下结构体的大小分析。对于这个结构体我们不难推导出最大对齐数——4,然后此时c1 + i + c2的内存大小是9,9不是最大对齐数4的倍数,于是向下延展至12个字节,此时正好是4的倍数,由此结构体变量的大小是12。

如果你现在觉得自己强得可怕,不妨试着继续挑战一下,下面这两个结构体大小的计算:

struct S3
{
    char c1;
    int i;
    char name[12];
};
struct S4
{
    int i;
    struct S3 s;
    char c1;
};

对于结构体struct S3而言,大家只需要注意博主给大家提醒的如果成员变量是一个数组。那对齐数那里所说的成员变量的大小,不是指数组的大小,而是数组中单个元素的大小。因此最后结构体的最大对齐数也不是12,而是4。结构体整体的大小就是20。如图所示:

其次对于结构体struct S4而言,它里面嵌套了其他的结构体struct S3 s,我们只要注意s的对齐数是这个结构体的最大对齐数。就可以了。如果所有的一些没有问题,最后struct S4的大小就是28。如图所示:

附一:offsetof宏的介绍:

offsetof是一个宏,有了offsetof我们的用户,只需要给出一个结构体,然后再给出这个结构体的一个成员变量,你就可以获得:这个成员变量距离结构体起始位置的偏移量。然后如果你想使用这个宏,请#include<stddef.h>

我们这里以前面的struct S4结构体为例子,来看一下这个宏具体是如何使用的:

#include<stdio.h>
#include<stddef.h>
struct S3
{
    char c1;
    int i;
    char name[12];
};
struct S4
{
    int i;
    struct S3 s;
    char c1;
};


int main()
{
    printf("struct S4 的i 的相较于起始位置的偏移量:%zd\n", offsetof(struct S4, i));
    printf("struct S4 的s 的相较于起始位置的偏移量:%zd\n", offsetof(struct S4, s));
    printf("struct S4 的c1的相较于起始位置的偏移量:%zd\n", offsetof(struct S4, c1));
	return 0;
}

运行截图:

而这些和我们所分析的结果是一致的。

有小伙伴可能对这个宏的底层实现比较感兴趣,我们这里也把这个offsetof宏的一种实现方式给大家放在下面了:

#define offsetof(type, member) ((size_t)&((type*)0)->member)

它的基本策略就是把地址为0的位置当作是结构体的起始地址,然后结构体的成员member再取地址就是这个结构体成员相较于起始位置的偏移量了。

(4)为什么会存在结构体的内存对齐:

C语言的结构体,为什么会有内存对齐的特性。有一种说法是说如果结构体的内存是对齐的话,会带来更好的内存访问效率。

OK,为了更好的说明这个问题,我们先来假定一些场景:

假如我们的用户创建了结构体S,并定义了一个该结构体的变量s,如图所示:

struct S
{
    char c;
    int i;
}s;

然后我们假定我们拥有一台32位的机器,注意这里的32位是指这台机器的机器字长是32位,机器字长32位也就意味着这台机器的CPU一次性要从内存拿32bit的数据,然后能一次性对32bit的数据进行处理(现代CPU对数据的各种处理都是基于机器字长为单位的)。

 然后如果说我不是内存对齐的,我这次想要处理变量s中的一个成员i,那这个过程可以用下图进行阐述:

这个图片告诉我们:如果说我读的数据是i,我们的CPU会从结构体变量s的起始地址处开始读数据(如果可以CPU可以选择不这么做,显然这里我们不得不这么做),一次读4Byte数据(4Byte == 32bit,图中绿框所标识的范围),显然我第一次只能拿到i的前3个字节的内容,我需要再读一遍才能完整地读取到i。也就是说: 如果内存是不对齐的,对i变量的读取CPU要读两次。而且对于变量i来说,还有一些没必要的东西被读进来了。

紧接着,我们继续探讨,如果说,我们的内存的是对齐的。那这个时候的过程则为下图所示:

显然,这时我们发现,如果说CPU要对i进行处理,它完全可以做到只需要读一次,就可以将变量i完整地读进来。

从另一个角度,当你的数据是对齐的情况下,我们内存控制器和CPU的设计呢,会更加简单。因为显然的一点,如果内存对齐的情况下,我们的CPU不需要去处理那种跨边界的数据请求;非对齐的情况下进行访问,出现跨边界的数据请求时可能需要多次内存操作才能获取到所需要的数据。这增加了我们硬件设计的复杂性并带来了潜在的延迟。

因此我们这里可也以认为是说:这种结构体存储成员的策略,是一种利用空间效率来换取时间效率的做法。

这是博主能给大家介绍的一种为什么说会存在有内存对齐的一个原因。当然呢,也有其他地方有说法是说:这种内存对齐的策略会有利于硬件平台的移植……嗯,我们不希望在这里把这个问题搞得更复杂了,因此这些其他的原因就留给感兴趣的读者朋友去探索一下吧,不再进行深入的探讨。

附二:带来的启发:

在设计结构体时,如果我既希望满足对齐,又希望所浪费的空间尽可能的少。我们应该做到:让占用空间小的成员尽量集中在一起。比如说下面两个结构体的设计,显然struct S1的设计优于struct S2的设计:

 struct S1
 {
    char c1;
    char c2;
    int i;
 };
struct S2
 {
     char c1;
     int i;
     char c2;
 };

附三:修改编译器的默认对齐数:

C语言提供了#pragma pack(对齐数)这种格式的预处理指令,来对编译器的默认对齐数进行修改。理论上,对齐数都是大于0的正整数,特别地,如果指令格式里面的对齐数不写或写成0(其他的数据类型或负数往往会被认为非法),那就表示使用默认对齐数。

我们来看一个例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//先将编译器的默认对齐数设置为1:
#pragma pack(1)
struct S1
{
    char c;
    int i;
};
//再将编译器的默认对对齐数设置回来:
#pragma pack()
struct S2
{
    char c;
    int i;
};


int main()
{
    printf("sizeof(struct S1) == %zd\n", sizeof(struct S1));
    printf("sizeof(struct S2) == %zd", sizeof(struct S2));
	return 0;
}

运行截图:

我们会发现,如果编译器的默认对齐数是1,带来的感觉就是没有进行内存对齐。当然无论怎么说,这应该和我们所预期的结果(推理的结果和实际结果一致)是一致的。

附四:关于前面问题的解答:

我们在C语言自定义数据类型详解(一)——结构体类型(上)-CSDN博客里面留了一个问题:为什么说结构体的传参,一般情况下是传址优于传值。现在我们对于一个结构体它的大小是如何计算的应该都已经是小case了,回过头来思考这个问题:如果我结构体传址的话,就如果是指针的话,那该参数对于函数栈的消耗要么是4Byte,要么是8Byte(x86是4,x64是8)。而一个结构体变量的大小往往会超过8个字节(你想一个char,一个int,然后还有内存对齐,这个大小已经就有8Byte了,更不要说更复杂的结构体)。这相应地,所带来的压栈消耗也会更大。

因此我们说结构体传参,传址往往优于传值。

 

二、关于位段:

(1)搭建应用场景:

A公司最近购入了四台机器。小明作为该公司的程序员被派遣了一个任务:要求建立一个数据结构用于维护四台机器的各自状态,这其中就包括了机器各自的id(0,1,2,3),机器的运行时间time,标识机器是否正常运行的状态指针state……这些可能就是作为维护人员,我们需要去关心的一些参数:

于是小明据此声明了下面这种结构,用来维护工厂里的这四台机器:

struct Machine
{
    int id;     //唯一标识一台机器的id
    int time;   //机器的运行时间
    int state; //机器目前的状态:1表示正常运行,0表示不正常
}

但是小明后来在使用过程中也注意到了一个问题,在这个结构体中,有一些成员的取值是非常有限的,比如说一台机器的id只有0,1,2,3四种可能的结果,一个机器的状态也就正常和故障两种。

而对于这样的成员变量,我们用int来存,嗯……可以是可以,但是我实际上能用到的也就2个二进制位而已(00:0,01:1,10:2,11:3)。也就是我当前的这种结构,id中有30bit都是无效的。

有小伙伴说用char,当然啦,这也行,但是有没有其他的方法呢?OK,这里跟着博主的步伐来认识一下我们接下来的主角——位段。

(2)位段的概念性理解:

在某些应用场景中,为了尽可能地节省空间,我们的C语言结构体一般都有能够实现位段的能力。

位段的声明和结构体非常类似,但是有两个本质上的区别:

  1. 位段的成员只能是int,unsigned int 或signed int。C99之后支持了更多的数据类型,但是基本上就是int,char这样的数据类型。
  2. 在位段的成员名的后面,往往有一个冒号和一个数字,用来表示该成员变量的大小。以bit为单位。

也就是说,位段的成员虽然冠以int自称,但是它的实际大小往往低于int类型的4Byte。我们可以来看一个例子:

//我们这里在位段的成员名前面都加了下划线_,仅仅是为了便于区分它和一般的结构体成员变量,即这仅仅是个命名习惯罢了:
struct S
{
    int _a : 2;  //_a是位段成员,占2  bit位
    int _b : 5;  //_b是位段成员,占5  bit位
    int _c : 10; //_c是位段成员,占10 bit位
    int d;
};

(3)位段的内存分配:

对于位段成员的内存分配,C语言也没有提供具体的标准,但是一个大致的方向是:当你定义了一个位段成员时,它会先给你1Byte或4Byte空间(这一般取决于你前面的类型写的是int还是char),然后你不够了,编译器会继续再给你1Byte或4Byte空间。

什么意思呢?就拿我们前面定义的结构体来说,我们先把它拿过来:

#include<stdio.h>
//我们这里在位段的成员名前面都加了下划线_,仅仅是为了便于区分它和一般的结构体成员变量,即这仅仅是个命名习惯罢了:
struct S
{
    int _a : 2;  //_a是位段成员,占2  bit位
    int _b : 5;  //_b是位段成员,占5  bit位
    int _c : 10; //_c是位段成员,占10 bit位
    int d;
};

int main()
{
    printf("sizeof(struct S) == %zd",sizeof(struct S));
    return 0;
}

首先编译器看你需要一个int类型的成员变量,于是它会先给你开辟4Byte空间。显然4Byte = 32bit,这对于_a,_b,_c显然是够用了的。因此_a,_b,_c会公用这4byte的空间。加上之后的int类型的b变量,这个结构体最终的大小大概就是8Byte左右。

显然这比你直接用4个int所需要的内存少多了。如图所示:

这就是一种简单地分析位段大小的一种方式。这里面其实还有很多的细节,如图所示:

具体到内部,_a要2bit,这2bit开辟,是从左往右,还是从右往左是C语言标准没有规定的。

另外,如果说是这样的:

#include<stdio.h>
//我们这里在位段的成员名前面都加了下划线_,仅仅是为了便于区分它和一般的结构体成员变量,即这仅仅是个命名习惯罢了:
struct S
{
    int _a : 15;  //_a是位段成员,占15 bit位
    int _b : 12;  //_b是位段成员,占12 bit位
    int _c : 10;  //_c是位段成员,占10 bit位
};

这里我们当然知道说这个结构体是8byte,然后_a和_b用了前4Byte的27bit位。问题是来给_c分配空间的时候:我们知道前面4Byte用了27bit了(_a和_b),它还有5bit没有被使用。那这5bit会不会给到_c,然后再从后面的4Byte拿出5bit,凑成最终的10bit呢。

还是说这前面4Byte遗留下的5bit不要了,_c的10bit全部在后面的4Byte里面。这件事情也是我们C语言标准所没有规定的事情。

换句话说,位段内部在给成员分配空间时,它的不确定性因素实在是太多了。这带来的直接问题便是:位段的跨平台性非常差!!!因为没有标准,每个编译器的厂家都是自己的那一套。因此我们实际中用位段其实用得非常非常少,这是希望大家注意到的一点。

博主在这里介绍位段的目的,也仅仅只是希望大家以后在纯C里面看到种定义结构体成员的方式,能够看得懂。

 

至此,关于结构体所有相关的周边知识就介绍完啦,我们下期再见!🙂

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2289094.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Maven的单元测试

1. 单元测试的基本概念 单元测试&#xff08;Unit Testing&#xff09; 是一种软件测试方法&#xff0c;专注于测试程序中的最小可测试单元——通常是单个类或方法。通过单元测试&#xff0c;可以确保每个模块按预期工作&#xff0c;从而提高代码的质量和可靠性。 2.安装和配…

Jetson Xavier NX 安装 CUDA 支持的 PyTorch 指南

本指南将帮助开发者完成在 Jetson Xavier NX 上安装 CUDA 支持的 PyTorch。 安装方法 在 Jetson 上安装 Pytorch 只有两种方法。 一种是直接安装他人已经编译好的 PyTorch 轮子&#xff1b;一种是自己从头开始开始构建 PyTorch 轮子并且安装。 使用轮子安装 可以从我的 Gi…

GWO优化GRNN回归预测matlab

灰狼优化算法&#xff08;Grey Wolf Optimizer&#xff0c;简称 GWO&#xff09;&#xff0c;是一种群智能优化算法&#xff0c;由澳大利亚格里菲斯大学的 Mirjalii 等人于 2014 年提出。该算法的设计灵感源自灰狼群体的捕食行为&#xff0c;核心思想在于模拟灰狼社会的结构与行…

Unity 粒子特效在UI中使用裁剪效果

1.使用Sprite Mask 首先建立一个粒子特效在UI中显示 新建一个在场景下新建一个空物体&#xff0c;添加Sprite Mask组件&#xff0c;将其的Layer设置为UI相机渲染的UI层&#xff0c; 并将其添加到Canvas子物体中&#xff0c;调整好大小&#xff0c;并选择合适的Sprite&#xff…

【大厂AI实践】OPPO:大规模知识图谱及其在小布助手中的应用

导读&#xff1a;OPPO知识图谱是OPPO数智工程系统小布助手团队主导、多团队协作建设的自研大规模通用知识图谱&#xff0c;目前已达到数亿实体和数十亿三元组的规模&#xff0c;主要落地在小布助手知识问答、电商搜索等场景。 本文主要分享OPPO知识图谱建设过程中算法相关的技…

C# 添加、替换、提取、或删除Excel中的图片

在Excel中插入与数据相关的图片&#xff0c;能将关键数据或信息以更直观的方式呈现出来&#xff0c;使文档更加美观。此外&#xff0c;对于已有图片&#xff0c;你有事可能需要更新图片以确保信息的准确性&#xff0c;或者将Excel 中的图片单独保存&#xff0c;用于资料归档、备…

赛博算卦之周易六十四卦JAVA实现:六幺算尽天下事,梅花化解天下苦。

佬们过年好呀~新年第一篇博客让我们来场赛博算命吧&#xff01; 更多文章&#xff1a;个人主页 系列文章&#xff1a;JAVA专栏 欢迎各位大佬来访哦~互三必回&#xff01;&#xff01;&#xff01; 文章目录 #一、文化背景概述1.文化起源2.起卦步骤 #二、卦象解读#三、just do i…

iperf 测 TCP 和 UDP 网络吞吐量

注&#xff1a;本文为 “iperf 测网络吞吐量” 相关文章合辑。 未整理去重。 使用 iperf3 监测网络吞吐量 Tom 王 2019-12-21 22:23:52 一 iperf3 介绍 (1.1) iperf3 是一个网络带宽测试工具&#xff0c;iperf3 可以擦拭 TCP 和 UDP 带宽质量。iperf3 可以测量最大 TCP 带宽…

内外网文件摆渡企业常见应用场景和对应方案

在如今的企业环境中&#xff0c;内外网文件摆渡的需求越来越常见&#xff0c;也变得越来越重要。随着信息化的不断推进&#xff0c;企业内部和外部之间的数据交换越来越频繁&#xff0c;如何安全、高效地进行文件传输成了一个关键问题。今天&#xff0c;咱就来聊聊内外网文件摆…

论文阅读(十五):DNA甲基化水平分析的潜变量模型

1.论文链接&#xff1a;Latent Variable Models for Analyzing DNA Methylation 摘要&#xff1a; 脱氧核糖核酸&#xff08;DNA&#xff09;甲基化与细胞分化密切相关。例如&#xff0c;已经观察到肿瘤细胞中的DNA甲基化编码关于肿瘤的表型信息。因此&#xff0c;通过研究DNA…

Android View 的事件分发机制解析

前言&#xff1a;当一个事件发生时&#xff08;例如触摸屏幕&#xff09;&#xff0c;事件会从根View&#xff08;通常是Activity的布局中的最顶层View&#xff09;开始&#xff0c;通过一个特定的路径传递到具体的View&#xff0c;这个过程涉及到三个关键的阶段&#xff1a;事…

内容检索(2025.01.30)

随着创作数量的增加&#xff0c;博客文章所涉及的内容越来越庞杂&#xff0c;为了更为方便地阅读&#xff0c;后续更新发布的文章将陆续在此汇总并附上原文链接&#xff0c;感兴趣的小伙伴们可持续关注文章发布动态&#xff01; 博客域名&#xff1a;http://my-signal.blog.cs…

【25美赛A题-F题全题目解析】2025年美国大学生数学建模竞赛(MCM/ICM)解题思路|完整代码论文集合

我是Tina表姐&#xff0c;毕业于中国人民大学&#xff0c;对数学建模的热爱让我在这一领域深耕多年。我的建模思路已经帮助了百余位学习者和参赛者在数学建模的道路上取得了显著的进步和成就。现在&#xff0c;我将这份宝贵的经验和知识凝练成一份全面的解题思路与代码论文集合…

新鲜速递:DeepSeek-R1开源大模型本地部署实战—Ollama + MaxKB 搭建RAG检索增强生成应用

在AI技术快速发展的今天&#xff0c;开源大模型的本地化部署正在成为开发者们的热门实践方向。最火的莫过于吊打OpenAI过亿成本的纯国产DeepSeek开源大模型&#xff0c;就在刚刚&#xff0c;凭一己之力让英伟达大跌18%&#xff0c;纳斯达克大跌3.7%&#xff0c;足足是给中国AI产…

H264原始码流格式分析

1.H264码流结构组成 H.264裸码流&#xff08;Raw Bitstream&#xff09;数据主要由一系列的NALU&#xff08;网络抽象层单元&#xff09;组成。每个NALU包含一个NAL头和一个RBSP&#xff08;原始字节序列载荷&#xff09;。 1.1 H.264码流层次 H.264码流的结构可以分为两个层…

【PyTorch】6.张量形状操作:在深度学习的 “魔方” 里,玩转张量形状

目录 1. reshape 函数的用法 2. transpose 和 permute 函数的使用 4. squeeze 和 unsqueeze 函数的用法 5. 小节 个人主页&#xff1a;Icomi 专栏地址&#xff1a;PyTorch入门 在深度学习蓬勃发展的当下&#xff0c;PyTorch 是不可或缺的工具。它作为强大的深度学习框架&am…

实现基础的shell程序

1. 实现一个基础的 shell 程序&#xff0c;主要完成两个命令的功能 cp 和 ls 1.1.1. cp 命令主要实现&#xff1a; ⽂件复制⽬录复制 1.1.2. ls 命令主要实现&#xff1a; ls -l 命令的功能 1.1. 在框架设计上&#xff0c;采⽤模块化设计思想&#xff0c;并具备⼀定的可扩…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.18 逻辑运算引擎:数组条件判断的智能法则

1.18 逻辑运算引擎&#xff1a;数组条件判断的智能法则 1.18.1 目录 #mermaid-svg-QAFjJvNdJ5P4IVbV {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-QAFjJvNdJ5P4IVbV .error-icon{fill:#552222;}#mermaid-svg-QAF…

知识库管理系统助力企业实现知识共享与创新价值的转型之道

内容概要 知识库管理系统&#xff08;KMS&#xff09;作为现代企业知识管理的重要组成部分&#xff0c;其定义涵盖了系统化捕捉、存储、共享和应用知识的过程。这类系统通过集成各种信息来源&#xff0c;不仅为员工提供了一个集中式的知识平台&#xff0c;还以其结构化的方式提…

SpringBoot 日志与配置文件

SpringBoot 配置文件格式 Properties 格式 Component ConfigurationProperties(prefix "person") //和配置文件person前缀的所有配置进行绑定 Data public class Person {private String name;private Integer age;private Date birthDay;private Boolean like;pr…