结构体使用和大小计算以及位段

文章详细介绍了C语言中的结构体声明、定义、初始化以及结构体的大小计算,强调了内存对齐的重要性。同时,提到了位段的概念,包括位段的声明、内存分配以及使用位段的优点和缺点。文章通过示例代码解释了结构体在栈上的存储方式,并讨论了结构体作为函数参数时的传参策略。
摘要由CSDN通过智能技术生成

目录

struct的声明

结构体变量的定义和初始化

结构体使用:

这里需要对一个知识点补充:栈

        

结构体大小计算

最后以一道题来进行练习

位段

 什么是位段

位段的内存分配


struct的声明

首先来了解下结构体的语法,

结构体:是一些值的集合,这些值称为成员变量,结构的每个成员是不同类型的变量.结构体是定义在全局变量的一个自定义类型,语法结构为:

struct 数据类型名{ 自定义成员 }变量名(可直接在结构体定义中就创建这个变量);

例如:我现在创建一个学生类型Student ,定义了学生s这个变量

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

struct Student
{
	char name[20];
	int id;
	int age;
	int grade;
}s;
int main()
{
	struct Student s;


	return 0;
}
struct b
{
char c;
short s;
double d;
}
struct stu//这也是一个结构属性,是一种类型
{
         //成员变量
struct B sb;
        char name[20];//名字
          int age;
char id[20];
}s1,s2是全局变量
int main()
{
         //s是局部变量
struct stu s //利用struct stu类型创建一个变量,相当于int类型创建一个变量a int a =0;这是个对象
//struct stu这个类型相当于图纸,这个类型要创建对象s相当于用图纸创建房子,这个行子有卧室有客厅,
//代表对象里面有空间存放stuct stu里面的name age啥的
struct stu s = {{'w',20,3,14},"张三",30,"202005034"};

结构体变量的定义和初始化

结构成员的类型:可以是标量,数组,指针,甚至是其他结构体

结构体成员的访问:运用连个操作符 .   ->

总结:调用者是指针就用->  ,调用者是对象就用 . 

struct stu* ps = &s;//*说明ps是指针变量的类型是struct stu
printf("%c\n",(*ps).sb.c);//对ps解引用找到s
printf("%c\n",ps->sb.c);//ps是指针,所以可以用指针指向sb

注意:1.在结构体的定义和声明中是没法进行成员变量的初始化的!

        2.并且结构体成员内部是可以放另一个结构体类型的成员

        3.结构体成员内部调用本身,会出现死循环(也就是结构的自引用)

结构体使用:

结构体传参有两种表现形式

第一种利用传递结构体变量,第二种利用指针传递结构体地址

void print1(struct Stu t)//第一种利用传递结构体变量
{
	printf("%c %d %lf %s %d %s\n", t.sb.c, t.sb.s, t.sb.d, t.name, t.age, t.id);
}

void print2(struct Stu* ps)//第二钟利用指针传递结构体地址
{
	printf("%c %d %lf %s %d %s\n", ps->sb.c, ps->sb.s, ps->sb.d, ps->name, ps->age, ps->id);
}

int main()
{
	s是局部变量
	struct Stu s = { {'w', 20, 3.14}, "张三", 30, "202005034" };//对象
	写一个函数打印s的内容
	print1(s);
	print2(&s);

	return 0;
}

上面的print1和print2函数哪个好些

是print2函数

函数传参时候,参数是需要开辟空间,也就是压栈的,如果传递一个结构体对象的时候,结构体过大,参数压栈的数值过大,对系统的开销比较大,可能会影响性能的下降,而且地址仅占用四个字节或者八个字节,对空间利用率大,且第二种可以改变结构体函数的参数

这里需要对一个知识点补充:栈

        

函数调用的参数压栈:

栈,是一种数据结构:先进后出,后进的先出

因为数据的放入是从顶上放进去的,可以想象成罐子,里面一层一层从顶层放下,放到后面越需要挤压顶部,这就叫压栈,删除一个栈就是出栈,也是需要先把最后一个放下的先拿出来

每一个函数调用都会在内存的栈区上开辟一块空间

当传递过去到c后,add函数的内存就销毁了,在此之前函数的传参是会压栈的

总结:结构体传参的时候:要传结构体的地址

说完这些基础知识,来到了结构体的重头戏了

结构体大小计算

        可能大伙看到下面代码,会觉得结果为多少呢?

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

struct Student
{
	char name;
	int age;
	int id;
	int grade;
}s;
int main()
{
	printf("%d\n",sizeof(struct Student));


	return 0;
}

打印结果有绝大多数人认为就是13个字节,认为结构体仅仅只是占据13个字节的内存,实际上结果是16,这是为什么?结构的大小并非是所谓的类型所占字节之和,其实是涉及到了内存对齐的机制,以32位机器为例

其实可以总结成四条规则:

1-结构体的第一个成员永远都放在0偏移处
刚刚已经知道,局部变量是存放在堆区内部的,那么假设我存放的地址为0x000000,那么第一个结构体成员就在0偏移处,以上面代码为例,char name就会放到0偏移处

2.从第二个成员开始,以后的每个成员都要对齐到某个对齐数的整数倍处

这个对齐数是:成员目身大小和默认对齐数的较小值

注:

VS环境下默认对齐数是8

gcc环境下没有默认对齐数,没有默认对齐数时,对齐数就是成员自身的大小
以vs环境为例:

当char name存储完毕使用了一个字节后,int age就要存储了,但是因为name已经占用了一个字节,age的数据类型允许他的对齐数为4,4跟默认对齐数8相比小,因此选择4 ,所以要先浪费掉三个字节后,再存储int age,因此前面的两个成员已经占用了八个字节了,而后面的id看到前面占用了八个字节,刚刚好是整数倍,已经不用再对齐了,直接存储4个字节,后续也是全部存进去,最终占用了16个字节

3.当成员全部存放去后

结构体的总大小必须是所有成员的对齐数中最大对齐数的整数倍。

如果不够,则浪费空间对齐。

因为满足条件所以上述代码自然不用再继续浪费空间对齐了,如果是其他类型代码是要在最后存储后以最大的对齐数跟已有占用的内存比较,然后根据规则进行浪费

4如果嵌套了结构体,嵌套的结构体成员要对齐到自己成员的最大对齐数的整数倍处。

整个结构的大小.必须是最大对齐数的整数倍,最大对齐数包含中嵌套的结构体成员中的对齐数

最后以一道题来进行练习

#include<stdio.h>
 
typedef struct Test
{
	short a;     //按8字节对齐 2+6
	struct 
	{
        int b;//4+4
		double c;//8
		char e;//1+7
	};
	int d;//4+4
}Test;
int main()
{
	printf("%d", sizeof(Test));
}
//运行结果为40;

该结构体内部的结构体大小为24;需要注意的是该结构的对齐值为成员变量中结构体中的对大类型值,而不是内部结构体的大小,故short类型应该与8对齐为2+6; 8+24+4=36,36不是8的整数倍,故将int类型补齐为4+4;所以最终结果为8+24+8=40.

位段

结构体实现位段的能力

 什么是位段

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

        1.位段的成员必须要int ,unsigned或者signed int

        2.位段的成员名后边有一个冒号和一个数字

struct A
{

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

位段冒号的数字-----这个变量所占的二进制位

并且在位段中,所有的成员的二进制位都是一起使用同一块内存的,

优点:能够最大程度的节省空间

缺点:修改其中一个变量的值可能会改变整个位段内结构体的其他成员的值

struct A
{

	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};
int main()
{

	printf("%d\n",sizeof(struct A));
	return 0;
}

结果为8个字节,也就是64个比特位,但我们正常计算是47个比特位,在很大程度上确实节省了空间

位段的内存分配

   位段的空间上是按照需要以四个字节或者一个字节的方式开辟的  

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

涉及到位段的赋值需要遵守两个规则

第一个是分配到内存中的比特位是由右向左使用的

第二个是分配的内存剩余的比特位不够使用时,浪费掉

代码所示

struct S
{

	int a : 3;
	int b : 4;
	int c : 5;
	int d : 4;
};
int main()
{
	struct S s = {0};
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	printf("struct的内存为:%p\n", s);
	return 0;
}

用图解来表示

如果有帮助的话,请多多关注+评论这是对我最大的鼓励

☆╭┐┌╮☆°.·
╭┘└┘└╮∴°☆°
└┐..┌┘—╮∴°
╭┴——┤       ├╮
│o o│  │●°
╰┬——╯       │ ∴°·
 ☆ \ˍˍˍ|ˍˍˍ/ˍˍˍ/ˍˍˍ/∴☆.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值