【结构体嵌套、大小及位域】

文章介绍了C语言中结构体嵌套的使用,如何通过定义共享结构体来存储老师和学生信息。接着讨论了字节对齐的重要性,包括其在不同平台的兼容性和性能优化方面的考虑。最后,文章讲解了位域的概念,如何利用位域节省存储空间,以性别为例展示了位域的运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.结构体嵌套结构体

1.含义

一个结构体中成员可以是另外一个结构体

2.语法结构

struct  结构体名

{

    struct  结构体名  成员名;

};

3.代码示例:

这里以需要储存老师和学生的信息为例子,老师的部分信息和学生的部分信息需要存储的是一样的

#include <stdio.h>
#include <string.h>
struct person
{
   char name[16];
   int age;
};
struct tea
{
    int salary;
	struct person teaper;
};
struct stu
{
  int score;
  struct person stuper;
};
int main(int argc, const char *argv[])
{
	struct tea tea1; 
	struct tea *p=&tea1;
	tea1.salary=5000;
	strcpy(tea1.teaper.name,"zhangsan");
	tea1.teaper.age=35;
	printf("salary=%d  name=%s  age=%d\n",p->salary,p->teaper.name,p->teaper.age);

	struct stu stu1;
	struct stu *t=&stu1;
	stu1.score=90;
	strcpy(stu1.stuper.name,"lisi");
    stu1.stuper.age=20;
    printf("score=%d  name=%s  age=%d\n",t->score,t->stuper.name,t->stuper.age);

	return 0;
}

 分析:

⭕在储存学生和老师信息的时候,两者的部分信息是一致的,年龄和名字都需要储存,所以我们可以定义一个共同的结构体来储存年龄和名字,然后再在两者分别的结构体中引用这个相同信息的结构;

⭕在引用结构体中的结构体成员时只需要用两个“ ..”就可以实现;

⭕需要注意的是在使用结构体指针时,指针不能连用->  ->来访问嵌套结构体成员;因为只有通过指针去访问的时候才是用"->",当用指针p访问teaper时用->连接两者,而之后此时teaper是一个变量,不是指针,所以就和普通的结构体一样用“.”又来访问它自己内部的成员;

二.结构体大小

1.字节对齐

含义:字节对齐主要是针对结构体而言的,通常编译器会自动对其成员变量进行对齐,以提高数据存取的效率;

作用:

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

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

通俗解释就是:

结构体中的数据的类型都是参差不齐的,如果要读取正确读取数据,为保证数据的完整性,就要访问两次,一次确定要数据的大小,第二次才是读取,所以就比较浪费时间,因此就有了用字节对齐来提高效率;字节对齐后,系统对结构体中的数据内存空间每次只读固定大小空间块,且不会破坏数据的完整性,相当于把数据空间进行整合,保证在每次读固定大小空间的时候,整块结构体的空间刚好都会是固定空间大小的整数倍读完,即以固定空间均分了结构体,且保证了数据的存储完整;

2.结构体大小的计算方法

 自身对齐:结构体成员自身占的字节数

 默认对齐:由系统或者编译器决定的字节数

 有效对齐:在自身对齐和默认对齐中小的那一个就是最后对齐的字节数

地址必须能够整除有效对齐的字节数,且地址是最小地址

最后整块结构体空间必须是数据类型中自身对齐字节数最大的整数倍;

这个部分有点抽象,下面找些例子

3.结构体大小计算

分析:

如图所示 该环境是32位环境,即4个字节,用自身所占的字节数与4相比,谁小就决定最后该变有效字节数长度。a变量有效对齐字节是1个字节,然后最小能够整除1字节的地址是0(假如从开始开辟的空间),然后b变量有效对齐是4个字节,然后最小能够整除4字节的地址是4,所以a到b之间隔了4个字节地址,然后b自己占四个字节且cd变量有效对齐都是1个字节,然后最小能够整除1字节的地址是就是8和9了,最后为了让整体的空间是默认字节的整数倍,所以又添了两个空间进来

三.位域

1.位域的定义

计算机的内存是以字节为单位,为变量开辟分配内存空间。但是实际上有些变量的大小并没有用到实际分配的空间大小,有时候只需要几个二进制位就够了。比如说储存男女性别是用0和1就足够,一个二进制位就能储存,用一个char(一个字节)来存储很浪费,因此,c语言中又提出了一种数据结构,称为“位域”

所谓的“位域”是把一个字节中的二进制位分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

2.位域的一般形式

 位域的一般形式:

struct 位域结构体名

{

位域列表;

};  

其中位域列表的形式为: 类型说明符  位域名:位域长度

3.代码举例:

char 类型在位域中可能会导致一些问题,尤其是在符号扩展方面。为了保证输出的准确性和避免符号扩展的影响,最好使用 unsigned 类型。

这样就用一个字节储存了三个变量;

#include <stdio.h>

struct Example {
    unsigned char flag1 : 3;    // 3 bits
    unsigned int flag2 : 5;    // 5 bits
    unsigned char flag3 : 2;    // 2 bits
};

int main() {
    struct Example ex;

    ex.flag1 = 5;  
    ex.flag2 = 31; 
    ex.flag3 = 2;  

    printf("flag1: %u\n", ex.flag1);  // %u for unsigned int
    printf("flag2: %u\n", ex.flag2);  // %u for unsigned int
    printf("flag3: %u\n", ex.flag3);  // %u for unsigned int

    return 0;
}

### C语言结构体嵌套结构体的用法和示例 #### 结构体嵌套的概念 在C语言中,结构体可以作为另一个结构体的成员。这种特性被称为**结构体嵌套**。通过这种方式,程序员能够创建更复杂的自定义数据类型来表示现实世界中的对象及其属性。 #### 示例代码 下面是一个简单的例子展示如何在一个结构体内嵌入另一个结构体: ```c #include <stdio.h> // 定义一个描述日期的结构体 struct Date { int day; int month; int year; }; // 定义一个员工信息的结构体,其中包含了一个Date类型的成员 struct Employee { int id; char name[50]; struct Date join_date; // 嵌套结构体 }; int main() { // 创建Employee结构体的一个实例并初始化其字段 struct Employee emp = {1, "Alice", {1, 1, 2023}}; printf("Employee ID: %d\n", emp.id); printf("Employee Name: %s\n", emp.name); printf("Joining Date: %d/%d/%d\n", emp.join_date.day, emp.join_date.month, emp.join_date.year); return 0; } ``` 在这个程序里,`struct Date` 被用来保存日期的相关信息,而 `struct Employee` 则包含了关于员工的信息以及他们的入职日期。这表明了即使是在相对简单的场景下,嵌套结构体也可以让代码更加清晰易读[^2]。 #### 动态内存管理与灵活性 如果需要动态地操作这些复合的数据结构,则可以通过指针来进行进一步的操作。例如,在某些情况下可能不知道提前有多少名雇员要录入系统,这时就可以利用malloc函数为每一个新的记录申请空间: ```c struct Employee* new_emp = (struct Employee*) malloc(sizeof(struct Employee)); if(new_emp != NULL){ (*new_emp).id = 2; strcpy((*new_emp).name,"Bob"); (*new_emp).join_date.day=2; (*new_emp).join_date.month=2; (*new_emp).join_date.year=2024; free(new_emp); // 记得释放不再使用的资源 }else{ perror("Failed to allocate memory."); } ``` 这里展示了如何使用指向结构体的指针,并且还强调了良好的编程习惯——总是检查分配请求的结果是否成功,最后记得清理已不需要的动态分配区以防泄露[^1]。 #### 总结 结构体嵌套提供了构建层次化、模块化的数据模型的能力,使得程序设计者可以根据实际需求组合不同类型的数据单元形成整体解决方案的一部分。无论是静态还是动态环境下都能找到合适的应用场合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

放牛的守护神_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值