结构体(c语言)

一.结构体

1.结构的基础知识

结构是一些值得集合,这些值称为成员变量,结构得每个成员可以是不同类型的变量

2.结构体的声明

cs 复制代码
struct tag//结构体名称
 {
 member-list;//成员变量
 }variable-list;//全局变量

例:描述一个学生

cs 复制代码
struct Stu
{
	int age; //年龄
	char name[10];//名字
}//s1,s2,s3为全局变量;

int main()
{
//s4,s5为局部变量
    struct Stu s4;
    struct Stu s5;
    return 0;
}

3.结构体的初始化

cs 复制代码
struct Stu
{
	int age;
	char name[10];
};

int main()
{
	//pa为结构体的变量
	struct Stu pa = { 10,"lisi" };//对结构体进行初始化
	//pa.age,pa.name 为访问结构体的成员
	printf("%d %s", pa.age, pa.name);
	return 0;
}

结构体中含有指针怎么初始化

cs 复制代码
struct S
{
	char name[100];
	int* ptr;

};

int main()
{
	int a = 100;
	//用空指针变量来对结构体进行初始化
	struct S s = { "abcdef",NULL };
	return 0;
}

4.结构体的重命名

typedef 把struct Stu 直接变成student,以后要写结构体时,直接写student即可,不用再写struct Stu

cs 复制代码
typedef struct Stu
{
	int age;
	char name[10];
}student;//这里的student是结构体类型

int main()
{
	//pa为结构体的变量
	 student pa  = { 10,"lisi" };
	//pa.age,pa.name 为访问结构体的成员
	printf("%d %s", pa.age, pa.name);
	return 0;
}

5.结构体的自引用

结构体内部包含一个指向自身类型的指针,这叫做结构体的自引用

cs 复制代码
struct Node
{
	int data;
	//这是一个指向Node结构体的指针,用与指向下一节点
	struct Node* next;
};

int main()
{
	struct Node n1;
	struct Node n2;
	//这行代码将n1的next指针指向n2的地址
	//从而n1与n2产生了联系
	n1.next = &n2;
	return 0;
}

6.结构体的嵌套

就是一个结构体中套了一个结构体

cs 复制代码
struct S
{
	int a;
	char c;
};
struct B
{
	float f;
	struct S s;
};


int main()
{
	//结构体变量的初始化
	struct S s2 = { 100,'q' };
	//按照自己的顺序来初始化
	struct S s3 = { .c = 'r',.a = 2000 };
	//嵌套结构体的初始化
	struct B sb = { 3.14f,{10,'a'} };
	//打印嵌套结构体
	printf("%f %d %c", sb.f, sb.s.a, sb.s.c);
	//sb.s.a的意思是用结构体B的变量sb进入到B的结构体中来访问结构体的成员
	//.s就是进入到结构体S中,最后.c就是访问S中的结构体成员
	return 0;
}

7.结构体的内存对齐(计算结构体的大小)

知识引入:

1.结构体的第一个成员永远放在偏移量为0的地址处

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

对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。

VS中默认的值为8

3.当成员全部存放进去时,结构体的总大小必须是所有成员对齐数

的最大对齐数的整数倍。

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

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


例:求下面代码结构体所占空间大小

看图理解👇

答案是12个字节

使用offsetof计算偏移量

代码如下👇

cs 复制代码
//offsetof的头文件
#include<stddef.h>
struct S
{
	char c;
	int a;
};

int main()
{
	//offsetof 可以用来算偏移量
	printf("%d\n", offsetof(struct S, c));
	//a是写在偏移量为4的地址处,因为是从0开始的,到4结束
	printf("%d\n", offsetof(struct S, a));
	return 0;
}

使用#pragma可以修改默认对齐数

cs 复制代码
include <stdio.h>
 #pragma pack(4)//设置默认对齐数为4
 struct S1
 {
 char c1;
 int i;
 char c2;
 };
 #pragma pack()//取消设置的默认对齐数,还原为默认

为什么存在内存对折

1.平台原因

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

2.性能原因

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。


8.结构体的函数传参

1.当结构体是普通变量(非指针类型)时,使用.操作符来访问结构体中的成员

2.当结构体是通过指针来引用时,使用->操作符来访问结构体中的成员。

cs 复制代码
struct S
{
int data[1000];
int num;
};
//s为结构体变量
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
// . 应用于结构体传参
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
    //->应用于指针传参
	printf("%d\n", ps->num);
}
int main()
{
	print1(s);  //传结构体
	print2(&s); //传地址
	return 0;
}
我们如何从结构体传参和指针传参中选择

函数传参的时候,参数需要压栈,会有时间和空间上的系统开销。

1.如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大

,所以会导致性能下降。

2.如果使用指针的时候,参数压栈的系统开销比较小,性能会提升很多

总结:

结构体传参的时候,要传结构体的地址

9.位段

1.位段的成员必须是unsigned int 或 signed int

2.位段的成员后边必须有一个 : 和 数字

cs 复制代码
//结构体内部是位段
struct A
{
	int _a : 2;//:后边的数字是bit(比特)
	int _b : 5;
	int _c : 10;
	int _d : 30;
};

位段的内存分配

1. 位段的成员可以是 int unsigned int signed int 或者是 char 类型

2. 位段的空间上是按照需要以4个字节( char (属于整形家族)类型 int )或者1个字节( char )的方式来开辟的。

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

注意:这是在vs编译器下的位段分配方式!!👇

在VS编译器中,先开辟一个字节空间,如果不够用,则会继续开辟

分配到内存的比特位是从右向左进行使用的。位段中的成员在内存中从左向右分配。

分配的比特位不够用,直接浪费掉,直接开辟下一个字节空间

位段的跨平台问题

1.int位被当成有符号位,还是无符号位不确定

2.位段的最大数目不确定,在16位机器上是16,在32位机器上是32,在64位机器上是64,(如果位段是27,在16位机器上就不适用了)

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

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

总结:

位段相对于结构体,可以节省很多空间,但是有跨平台问题出现


相关推荐
ChoSeitaku32 分钟前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
娅娅梨34 分钟前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
DdddJMs__13538 分钟前
C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
c语言·leetcode·题解
汤米粥40 分钟前
小皮PHP连接数据库提示could not find driver
开发语言·php
冰淇淋烤布蕾43 分钟前
EasyExcel使用
java·开发语言·excel
拾荒的小海螺1 小时前
JAVA:探索 EasyExcel 的技术指南
java·开发语言
马剑威(威哥爱编程)1 小时前
哇喔!20种单例模式的实现与变异总结
java·开发语言·单例模式
娃娃丢没有坏心思1 小时前
C++20 概念与约束(2)—— 初识概念与约束
c语言·c++·现代c++
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
好睡凯1 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法