iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言的结构体你了解吗
  • 722
分享到

C语言的结构体你了解吗

2024-04-02 19:04:59 722人浏览 泡泡鱼
摘要

目录结构体内存对齐结构体传参结构体实现位段什么是位段位段在内存中的存储位段的问题总结结构体内存对齐 当我们创建一个结构体变量时,内存就会开辟一块空间,那么在创建结构体变量时内存到底是

结构体内存对齐

当我们创建一个结构体变量时,内存就会开辟一块空间,那么在创建结构体变量时内存到底是怎么开辟空间的呢?会开辟多大的空间呢?我们来看一下下面的代码

struct S
{
	int i;
	char c;
	char b;
};
struct G
{
	
	char c;
	int i;
	char b;
};
int main()
{
	struct S u;
	struct G g;
	printf("%d\n", sizeof(u));
	printf("%d\n", sizeof(g));
	return 0;
}

在这个代码中,我们创建了两个结构体类型,并用这两个类型创建了两个结构体变量,当变量被创建的时候,内存就会为这些变量开辟空间,而在这两个变量中,我们都创建了两个char类型的成员和一个int类型的成员,不同的是这三个成员的排列顺序不同,然后我们来打印一下这两个变量所占的字节,来看一下有什么不同

在这里插入图片描述

我们发现,这两个结构体变量虽然里面的元素类型一样,但是他们的大小并不一样,而他们之间的区别就是结构体成员的排列顺序不同,所以我们可以知道,结构体成员的排列顺序是会影响结构体的大小的,那么他到底是怎么影响的呢,这就和结构体的创建规则有关。

结构体在创建时要进行内存对齐,对齐的规则是:

1.第一个成员在与结构体变量偏移量为0的地址处。

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8

3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

下面我们来根据对齐规则来算一个下上面的两个结构体的大小:

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

首先,我们先看struct S,第一个成员是int类型,占4个字节,它的对齐数就是4,第二个是char类型,占一个字节,对齐数是1,一个放在1的整数位上,也就是可以接着往下放,那就是在int的4个字节后面紧跟着来存放,第三个也是char类型,对齐数也是1,那么也可以接着放在后面,三个成员的最大对齐数是4,根据对齐规则,结构体的大小是最大对齐数的整数倍,而我们刚刚算完这三个占的大小已经是6个字节了,所以为了对齐,我们要浪费两个字节的空间,使这个结构体的大小为8个字节,是最大对齐数的2倍。

struct G
{
	
	char c;
	int i;
	char b;
};

我们再来看第二个,这里,我们的第一个元素变成了char类型,占一个字节,但是第二个成员是int类型,对齐数为4,根据对齐规则要对齐到4的整数倍处,那就只能放在结构体开始储存的位置往后4个字节的地址处,加上它本身占4个字节,这时候我们在内存中就使用了8个字节,然后在存入第三个成员char类型,对齐数是1,可以直接在后面存放,最大对齐数还是4,但是我们已经使用了9个字节,所以这时候的大小应该是12个字节,4的3倍。

以上的结果也是符合刚刚的运行结果的。

下面我们来看一个结构体里嵌套了一个结构体时的例子:

struct G
{

	char c;
	int i;
	char b;
};
struct S
{
	char a;
	int i;
	struct G c;

};

我们在结构体struct S中嵌套了一个结构体struct G,根据对齐规则,我们嵌套的结构体一个对齐到自己的最大对齐数的整数倍上,那就是4的整数倍,而第一个成员是char,第二个是int,int对齐到4的整数倍上,那前两个成员就占了8个字节,而struct G正好对齐到int的后面,大小我们刚刚算出来是12个字节,加起来就是20个字节,下面我们来验证一下

在这里插入图片描述

与运行结果相同,那么我们的计算就没有问题。

那么我们结构体在储存时为什么要进行内存对齐呢?主要有以下两个原因。

1.平台原因(移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2.性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。

原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

跟主要的可能是第二种原因,为了增加处理器的访问效率,我们选择了用浪费空间的方式,来使我们的处理器可以一次性的访问到我们需要的元素,用空间来换时间。

根据我们的对齐规则我们也可以发现,如果我们想要在创建结构体变量时节省空间,我们应该尽量让小的成员集中在一起,这样可以减少空间的浪费,节省空间。

在规则中我们看到,计算每个成员的对齐数时要选择默认对齐数与该成员大小的较小值,在vs编译器中这个默认对齐数是8,而在有的编译器中没有默认对齐数,如GCc编译器的默认对齐数就是成员自身的大小,当然,这个默认对齐数也是可以该的,而我们如何来修改默认对齐数呢,我们看下面的代码

struct H
{
	char c1;
	double d;
	char c2;
};
int main()
{
	struct H h;
	printf("%d\n", sizeof(h));
	return 0;
}

根据我们的对齐规则,这个结构体的大小应该是24个字节,我们来运行一下看看结果

在这里插入图片描述

如果我们要把它的默认对齐数改为4,那么我们再来重新计算一下,首先第一个char类型占一个字节,然后double类型占8个字节,但是对齐数为4,对齐到4的倍数,就可以在第4个字节的位置开始存储,这时候前两个只占12个字节,最后一个char占一个字节,最大对齐数为4,大小为4的倍数,应该为16,我们来验证一下

在这里插入图片描述

根据运行的结果我们可以看到,确实是改变了默认对齐数

修改默认对齐数的方法就是在结构体类型前加上#pragma pack(n),n表示修改后的默认对齐数的值(一般都是2的次方数,当改为1时,表示不存在对齐),在结构体类型的后面加上#pragma pack()表示取消修改。

#pragma pack(4)
struct H
{
	char c1;
	double d;
	char c2;
};
#pragma pack()

如上面的代码,表示我们只把#pragma pack(4)~#pragma pack()之间的结构体类型的默认对齐数改为了4。

结构体传参

学习函数的时候我们曾经学到,函数在调用时有两种方法,一种是传值调用,一种是传址调用C语言–函数,我们来看下面的代码。

struct S {
	int data[1000];
	int num;
};

//结构体传参
void print1(struct S s) 
{
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps) 
{
	printf("%d\n", ps->num);
}
int main()
{
	struct S s = { {1,2,3,4}, 1000 };
	print1(s); //传结构体
	print2(&s); //传地址
	return 0;
}

在这个代码中,我们的print函数的目的是打印结构体变量中的一个成员,print1传参时传的就是结构体变量的值,print2传的就是结构体变量的地址,他们的不同点在于传值的时候,我们的形参是实参的一份临时拷贝,也就是说,当我把结构体变量的以传值的方式传参给函数时,当我们调用这个函数,内存就会把这个结构体拷贝一份,当我们的结构体变量比较小时还好,但是当这个结构体变量里的成员非常多,占据的空间非常大时,就会导致系统开销比较大,性能下降,而如果我们使用传址的方式,我们一个地址的大小也就4或者8个字节,就没有上面的问题,所以在结构体传参时,我们尽量要传地址,既可以节省时间,也可以节省空间。

结构体实现位段

什么是位段

位段的声明和结构是类似的,有两个不同:

1.位段的成员必须是 char、int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。

struct A 
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};

这里的A就是位段,每个成员后面的数字代表他们需要的二进制位。

位段在内存中的存储

1.位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型

2.位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。

3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

我们来举个例子

struct 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;
}

我们来研究这个位段在内存中是如何储存的

首先,我们看成员a,它占3个二进制位,是一个char类型,我们需要创建出一个字节的空间,也就是8个二进制位,a要走了3个,我们假设在存储是,我们存储的顺序是与小段存储的方式类似,也是从右向左存储的,那么a在内存中存储的位置应该是后面的三个二进制位

低地址->高地址
  ********
       aaa

假设一个代表内存中的一个二进制位,这八个代表一个字节,这3个a上面对应的*就是a在这一个字节中所占的空间。
b需要4个二进制位,而我们刚刚创建的一个字节中还有5个二进制位,所以我们把b的4个二进制位放在a的后面。

低地址->高地址
  ********
   bbbbaaa

这就是a与b在内存中的存储情况,而这是c需要个二进制位,我们只剩下一个二进制位了,而c又是一个char类型,所以我们需要再创建一个字节的空间,而当我们创建好了新的空间,c的个二进制位应该怎么储存呢,我们是接着第一个字节把剩下的二进制位用完还是在我们新创建的字节里重新储存呢,假设内存在存储时选择直接浪费掉哪个二进制位,在新的空间进行储存,这时内存中的存储分布应该是这样

低地址->高地址
  ********  ********
   bbbbaaa     ccccc

存储d时,d需要4个二进制位,第二个字节中的二进制位也不够,所以我们再创建一个字节,存储d

低地址->高地址
  ********  ********  ********
   bbbbaaa     ccccc      DDDd

如果我们的假设没错的话,这一个就是a,b,c,d这4个成员在内存中的储存位置,然后我们又对这4个成员进行了赋值

a=10=>1010(二进制数)=>010(3个二进制位)

b=12=>1100(二进制数)=>1100(4个二进制位)

c=3=>11(二进制数)=>00011(3个二进制位)

d=4=>100(二进制数)=>0100(4个二进制位)

当我们用上面的数据对我们刚刚的位段进行赋值,那么这个位段在内存中存储的内容应该是这样的

低地址->高地址
  0bbbbaaa  000ccccc  0000dddd
  01100010  00000011  00000100(二进制)
     62        03        04   (十六进制)

根据我们的计算,我们发现,如果位段按照我们刚刚的假设来存储,那么在内存中存储的内容应该是62 03 03,那我们现在来调试一下看看

在这里插入图片描述

结果与我们推断的一样,那么就说明当前的编译器位段的存储是按照我们假设的方式来存储的。

位段的问题

但是位段在C语言中的规定又有许多不确定的地方,

1.int 位段被当成有符号数还是无符号数是不确定的。

2.位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。

3.位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。

不同的平台可能对上述的问题有不同的规定,所以位段是不能跨平台的

跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

位段的使用环境是在我们传输一个数据包时,可以使用位段使数据包在不能压缩的情况下,所占的空间最小。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!  

--结束END--

本文标题: C语言的结构体你了解吗

本文链接: https://www.lsjlt.com/news/139851.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C语言的结构体你了解吗
    目录结构体内存对齐结构体传参结构体实现位段什么是位段位段在内存中的存储位段的问题总结结构体内存对齐 当我们创建一个结构体变量时,内存就会开辟一块空间,那么在创建结构体变量时内存到底是...
    99+
    2024-04-02
  • C语言结构体struct详解
    目录结构体的概念结构体类型的声明结构体变量的创建typedef关键字结构体的嵌套结构体变量的初始化结构体成员的访问结构体的传参总结结构体的概念 结构体是由一系列具有相同类型或不同类型...
    99+
    2024-04-02
  • Go语言结构体struct详解,Go空结构体的这些妙用你知道吗?
    本文详解了Go语言结构体的各个知识点,最后介绍了空结构体的3种妙用。希望对你有帮助。 定义 结构体,是一种自定义的数据类型,由多个数据类型组合而成。用于描述一类事物相关属性。 定义方式: typ...
    99+
    2023-09-07
    golang java 算法
  • 深入了解Go语言结构体
    Go语言作为一种快速、简洁、并发的编程语言,结构体(struct)是其重要的数据结构之一。结构体允许我们将不同类型的数据组合在一起,形成一个更为复杂的自定义数据类型。本文将深入探讨Go...
    99+
    2024-03-10
    go语言 结构体 深入
  • C语言之结构体(struct)详解
    目录为什么需要引入结构体struct定义typedef与#define结构体变量初始化及成员访问结构体访问总结为什么需要引入结构体 原有的数据类型不能满足需求,因此才设计了构造类型结...
    99+
    2024-04-02
  • python的语句结构你真的了解吗
    目录一、程序的组织结构二、顺序结构三、对象的布尔值四、分支结构选择结构单分支结构双分支结构多分支结构嵌套if五、ass空语句pass语句利用对象的布尔值总结一、程序的组织结构 任何简...
    99+
    2024-04-02
  • C语言 - 结构体、结构体数组、结构体指针和结构体嵌套
    结构体的意义 问题:学籍管理需要每个学生的下列数据:学号、姓名、性别、年龄、分数,请用 C 语言程序存储并处理一组学生的学籍。 单个学生学籍的数据结构: 学号(num): int 型姓名(name) :char [ ] 型性别(sex):c...
    99+
    2023-08-30
    c语言 开发语言
  • 浅谈C语言结构体
    目录前言什么是结构体结构体类型的声明结构的自引用结构体变量的定义和初始化结构体的使用结构体内存对齐结构体传参总结前言 在C语言中,除了内置的许多数据类型,C语言还为我们提供了自定义的...
    99+
    2024-04-02
  • 详解C语言中结构体的使用
    目录结构体的声明结构体成员的类型结构体成员的访问结构体的声明 结构体的定义:结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量。 举例: //定义结构体类...
    99+
    2024-04-02
  • C语言结构体(struct)的详细讲解
    目录引言1. 动态内存管理2. 结构体2.1 定义语法2.2 定义示例2.3 初始化2.4 结构体赋值2.5 结构体数组2.6 结构体指针赋值3. 学生管理系统附:结构体变量的存储原...
    99+
    2024-04-02
  • C语言结构体的全方面解读
    目录前言一、结构体的声明与定义1.结构体的声明2.结构成员的类型3.结构体的定义二、初始化结构体三、访问结构体成员四、结构体嵌套五、结构体指针六、结构体传参总结前言 C语言提供了不同...
    99+
    2024-04-02
  • 一篇文章带你了解C语言的选择结构
    目录一、if-else语句1.单选择 2.双选择3.多选择二.switch语句总结一、if-else语句 1.单选择 单选择通过if语句就可以实现,if语句的语法及执行流程...
    99+
    2024-04-02
  • C语言结构体,枚举,联合体详解
    目录1.什么是结构体、枚举、联合体2.定义结构体2.1 包含结构体成员变量、variable2.2 tag、结构体成员变量2.3 用结构体声名变量2.4 用typedef 创建新类型...
    99+
    2024-04-02
  • C语言中结构体实例解析
    目录一.结构体定义二.实例演示结构体作为函数参数结构体指针三.typedef struct 和 struct的区别1、声明不同2、访问成员变量不同3、重新定义不同总结一.结构体定义 ...
    99+
    2024-04-02
  • C语言结构体超详细讲解
    目录前言1、结构体的声明1.1 结构的基础知识1.2 结构的声明1.3 结构成员的类型1.4 结构体变量的定义和初始化2、结构体成员的访问2.1 点操作符访问2.2 ->操作符...
    99+
    2024-04-02
  • C语言的strcpy函数你了解吗
    目录strcpy函数原型参数讲解返回值详解函数详解第二个注意点:目标空间必须足够大,以确保能存放源字符串第三个注意点:目标空间必须可变总结strcpy 拷贝字符串到目标地址 函数原...
    99+
    2024-04-02
  • C语言的运算符你了解吗
    目录前言一、算数运算符(数学运算) + -* / %二、逻辑运算符(判断真假)|| &&> >= < <=!? : ...
    99+
    2024-04-02
  • C语言struct结构体介绍
    目录structstruct的嵌套实验struct C 语言没有其他语言的对象(object)和类(class)的概念,struct 结构很大程度上提供了对象和类的功能。 下面是st...
    99+
    2024-04-02
  • c语言结构体怎么用
    非常抱歉,由于您没有提供文章标题,我无法为您生成一篇高质量的文章。请您提供文章标题,我将尽快为您生成一篇优质的文章。...
    99+
    2024-05-15
  • C语言结构体简单入门讲解
    结构体 定义:用于存储不同的数据类型,存储在同一块内存空间里面 关键字 struct 标签 结构体名称 成员 例如: struct student {...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作