C语言之结构体

欢迎拜访雾里看山-CSDN博客
本篇主题 :C语言之结构体
发布时间 :2025.1.11
隶属专栏C语言

目录

结构体类型的声明

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

结构体内部的成员可以是标量、数组、指针、也可以是其他结构体。

声明

c 复制代码
struct tag
{
	member-list;
}variable-list;

例如用结构体描述一个朋友

c 复制代码
typedef struct friend
{
	char name[20];//名字
	int age;//年龄
	char sex[5];//性别
	char number[20]//电话号码
}friend;//结尾分号不能丢

特殊的声明

在对结构体进行声明的时候, 可以进行不完全声明。

c 复制代码
struct
{
	int a;
	char b;
	float c;
}x;

struct
{
	int a;
	char b;
	float c;
}a[20], *p;

上面的两个结构体在声明的时候,省略了结构体的标签

若对于以上代码执行p = &x;语句,则会报错。因为编译器会认为这是两个不同的结构体。

结构体的自引用

在结构体中包含一个类型为该结构体本身的结构体成员是否可以?

c 复制代码
struct Node
{
	int data;
	struct Node next;//包含自身
};

不可以!! 不明白的可以想一下sizeof(struct Node)的结果是多少。上述代码会在初始化结构体的时候报错。

正确的引用方式如下:

c 复制代码
struct Node
{
	int data;
	struct Node *next;	
};

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

c 复制代码
struct Point
{
	int x;
	int y;
}p1;//声明类型的同时定义变量P1;
struct Point P2;//直接定义变量P2;

//初始化:定义变量的同时赋初值
struct Point P3 = { x, y};

struct Friend
{
	char name[20];
	int age;
};
struct Friend f = {"zhangsan", 20};//初始化

struct Node
{
	int data;
	struct Point P;
	struct Node* next;
}n1 = {10, {4, 5}, NULL};//结构体嵌套初始化

struct Node n2 = {20, {3, 4}, NULL};//结构体嵌套初始化

结构体成员访问

结构体变量访问内部成员是通过点操作符 . 对内部成员进行访问。点操作符接受两个操作数。

例如:

当我们需要访问结构体内的成员nameage 时,可以进行如下操作

c 复制代码
struct Friend f;
strcpy(f.name, "lisi");//使用.操作符访问name成员
f.age = 21;//使用.操作符访问age成员

在实际应用中,我们更多的时候是遇到结构体的指针,这时我们可以进行如下操作。

c 复制代码
struct Friend
{
	char name[20];
	int age;
};

void Print(struct Friend *pf)
{
	printf("name = %s   age = %d\n", (*pf).name, (*pf).age);
	//使用结构体指针访问指向对象的成员
	printf("name = %s   age = %d\n", pf->name, pf->age);
}

int main()
{
	struct Friend f = {"zhangsan", 20};
	Print(&f);
	return 0;
}

结构体传参

我们仔细观察下面的代码。

c 复制代码
struct Friend
{
	char name[20];
	int age;
};
//结构体传参
void Print1(struct Friend f)
{
	printf("name = %s   age = %d\n", f.name, f.age);
}
//结构体指针传参
void Print2(struct Friend *pf)
{
	printf("name = %s   age = %d\n", pf->name, pf->age);
}

int main()
{
	struct Friend f = {"zhangsan", 20};
	Print1(f);//传结构体
	Print2(&f);//传结构体指针
	return 0;
}

在实际应用中,传结构体和传结构体指针哪个更好一点?

在函数传参的时候,参数是需要压栈的,如果传递结构体,当结构体太大的时候,参数压栈的系统开销会很大,结果则会导致性能的下降。

结论: 传结构体指针更优。

结构体内存对齐(重要部分)

结构体对齐的规则

  1. 第一个成员在与结构体变量偏移量为 0 的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = min(编译器默认对齐数, 该成员大小)
  3. 结构体总大小为最大对齐数(每一个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体, 嵌套的结构体先确定自己的大小,再按照上面的规则计算。

常见结构体的计算

以在VS编译器下为例(默认对齐数是8)

c 复制代码
struct S1
{
	char c1;//编译器默认对齐数为8, 该成员大小1,对齐数1 
	int i;//编译器默认对齐数为8, 该成员大小4,对齐数4
	char c2;//编译器默认对齐数为8, 该成员大小1,对齐数1 
};
printf("%d\n", sizeof(struct S1));//最终大小为12
struct S2
{
	int i;//编译器默认对齐数为8, 该成员大小4,对齐数4 
	char c1;//编译器默认对齐数为8, 该成员大小1,对齐数1 
	char c2;//编译器默认对齐数为8, 该成员大小1,对齐数1 
};
printf("%d\n", sizeof(struct S2));//最终大小为8
struct S3
{
	double d;//编译器默认对齐数为8, 该成员大小8,对齐数8 
 	char c;//编译器默认对齐数为8, 该成员大小1,对齐数1 
 	int i;//编译器默认对齐数为8, 该成员大小4,对齐数4 
};
printf("%d\n", sizeof(struct S3));//最终大小为16
struct S4
{
 	char c1;//编译器默认对齐数为8, 该成员大小1,对齐数1 
 	struct S3 s3;//编译器默认对齐数为8, 该成员大小16,对齐数8 
 	double d;//编译器默认对齐数为8, 该成员大小8,对齐数8 
};
printf("%d\n", sizeof(struct S4));//最终大小为32

为什么存在内存对齐

  1. 平台原因(代码的可移植性):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件只能在某些地址处取某些特定类型的数据,否则则会抛出异常。
  2. 性能问题:数据结构(尤其是栈),应该尽可能的在自然边界上对齐原因在于,为了访问未对齐的内存,处理器需要做两次内存访问; 而对齐的内存只需要访问一次即可。

总体来说 : 结构体的内存对齐是拿空间 换取时间的做法。

在设计的时候,要让占用空间小的尽量在一起,这样即满足对齐,又可以节省空间。

c 复制代码
struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	char c2;
 	int i;
};

S1和S2内部成员函数相同,但是S2所占的空间却小于S1。

除此之外,还可以修改默认对齐数

修改默认对齐数

在结构体对齐方式不合适的时候, 我们可以自己更改默认对齐参数。

使用#pragma这个预处理命令可以更改我们的默认对齐数。

c 复制代码
#include<stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int mian()
{
	printf("S1 = %d\n", sizeof(struct S1));
	printf("S2 = %d\n", sizeof(struct S1));
	return 0;
}

⚠️ 写在最后:以上是我对C语言结构体部分的一些学习后的总结, 如有错误或者需要补充的地方欢迎各位大佬私信我交流!!!

相关推荐
郝学胜-神的一滴1 天前
Python中的鸭子类型:理解动态类型的力量
开发语言·python·程序人生·软件工程
日更嵌入式的打工仔1 天前
InitLWIP() 初始化
笔记·嵌入式硬件·学习
峰顶听歌的鲸鱼1 天前
38.Shell脚本编程2
linux·运维·服务器·笔记·学习方法
猫头虎1 天前
如何解决 pip install -r requirements.txt extras 语法 ‘package[extra’ 缺少 ‘]’ 解析失败问题
开发语言·python·开源·beautifulsoup·virtualenv·pandas·pip
zhangfeng11331 天前
R语言 读取tsv的三种方法 ,带有注释的tsv文件
开发语言·r语言·生物信息
eqwaak01 天前
动态图表导出与视频生成:精通Matplotlib Animation与FFmpeg
开发语言·python·ffmpeg·音视频·matplotlib
刘新明19891 天前
Frida辅助分析OLLVM虚假控制流程(下)
java·开发语言·前端
第二只羽毛1 天前
重载和继承的实践
java·开发语言
迎風吹頭髮1 天前
UNIX下C语言编程与实践35-UNIX 守护进程编写:后台执行、脱离终端、清除掩码与信号处理
java·c语言·unix
光军oi1 天前
全栈开发杂谈————JAVA微服务全套技术栈详解
java·开发语言·微服务