【C语言】结构体structure

【C语言】结构体structure:

  • C语言可以自定义数据类型。结构体是其中一个自定义的数据类型。
  • 结构体类型是复杂的数据类型,将多个不同数据按一定功能进行整体封装,用一个名称来给结构体命名。可用typedef为结构体提供别名。
  • 关键字struct。结构体包括结构体名称、结构体成员(由成员类型和成员变量名组成)。
  • 结构体中各成员的类型可以不同,可以是基本数据类型,也可以其它结构体或指针等。
  • 需要定义结构体类型。结构体可以作为变量,需要定义该类型的变量。结构体也可以作为函数参数传递,也可以作为函数的返回类型。结构体还可以定义更复杂的抽象数据类型(链表、树等)。

1、定义结构体类型

结构体类型:

关键字struct。结构体包括结构体名称、结构体的成员(由成员类型和成员变量名称组成)。

结构体中各成员的类型可以不同,可以是基本数据类型,也可以其它结构体或指针等。

注意:

① 最后的分号";"不能省略。

② 一般在所有函数之前定义结构体类型。

struct 结构体名称          //结构体名称:即结构体标签
{结构体的成员1;         // 结构体的成员:由成员类型和成员变量名称组成结构体的成员2;              ...
};
// 例如:名为person的结构体,成员变量有字符串name,整数age,字符串job
struct person
{char name[32];int age;char job[16];
};

2、结构体作为变量

(2-1)定义结构体变量

定义结构体类型后,定义结构体变量。可以定义一个或多个结构体变量。

注意:

① 定义结构体类型,不会分配内存。定义结构体变量后,才会为变量分配内存。

② 一般在所有函数之前定义结构体类型。在具体函数中定义局部的结构体变量。

③ 结构体变量名可以和结构体名称相同。

struct person
{char name[32];int age;char job[16];
};struct person p;                   // 定义一个结构体变量
struct person p1, p2, p3;          // 定义多个结构体变量
struct person p1, p2[60], *p3;     // 定义多个结构体变量

(2-2)访问结构体成员

使用成员访问运算符(".")来访问结构体成员,即:结构体变量名.结构体成员变量名。

#include <stdio.h>
#include <string.h>struct person
{char name[32];int age;char job[16];
};int main(void)
{struct person p; strcpy(p.name, "Mark");p.age = 25;strcpy(p.job, "teacher");printf("p.name = %s, p.age = %d, p.job = %s\n", p.name, p.age, p.job);return 0;
}// 结果:
p.name = Mark, p.age = 25, p.job = teacher

补充:

也可以在定义结构体类型的时候定义结构体变量,在结构体的末尾大括号{ }外 最后一个分号;前。可以定义一个或多个结构体变量。

定义结构体变量时可以给变量设置初始值,可以使用成员访问运算符(".")访问结构体成员。

注意:若在所有函数之前定义结构体类型,则同时定义的结构体变量就是全局变量。

#include <stdio.h>struct person
{char name[32];int age;char job[16];
} p = {"Jack",18,"programmer"};     // 定义结构体类型时,定义一个结构体变量int main(void)
{printf("p.name = %s, p.age = %d, p.job = %s\n", p.name, p.age, p.job);return 0;
}// 结果:
p.name = Jack, p.age = 18, p.job = programmer
#include <stdio.h>struct person
{char name[32];int age;char job[16];
} p[] = {                             // 定义结构体类型时,定义一个结构体数组变量{"Jack",18,"programmer"},{"Mark",25,"teacher"}
};int main(void)
{int n = sizeof(p) / sizeof(struct person);   // 获取数组元素个数for(int i = 0;i < n;i++){printf("p[%d].name = %s, p[%d].age = %d, p[%d].job = %s\n", i, p[i].name, i, p[i].age, i, p[i].job);}   return 0;
}// 结果:
p[0].name = Jack, p[0].age = 18, p[0].job = programmer
p[1].name = Mark, p[1].age = 25, p[1].job = teacher

3、结构体作为函数参数

结构体作为参数传给函数,传入方式与其它类型变量或指针相同。

注意:结构体作为函数参数,向函数传递的是结构体副本,不会修改原结构体内容。若要修改原结构体内容,则使用结构体指针作为函数参数。

#include <stdio.h>struct person
{char name[32];int age;char job[16];
};void changestruct(struct person person);int main(void)
{struct person p; strcpy(p.name, "John");p.age = 18;strcpy(p.job, "programmer");printf("programmer: %s, age %d\n", p.name, p.age);changestruct(p);                        // 结构体作为参数传入函数printf("End: programmer: %s, age %d\n", p.name, p.age);return 0;
}void changestruct(struct person person)    // 参数类型是结构体类型 
{    person.age = 22;                  // 不修改原结构体printf("After: programmer: %s, age %d\n", person.name, person.age);
}// 结果:
programmer: John, age 18
After: programmer: John, age 22
End: programmer: John, age 18

4、结构体作为函数返回值

函数若返回结构体,则函数的返回类型是结构体类型,需有结构体变量接收函数返回的结构体。

#include <stdio.h>
#include <string.h>struct person
{char name[32];int age;char job[16];
};struct person getstruct(void);         // 函数返回类型是结构体类型int main(void)
{struct person p;                  // 定义结构体变量p = getstruct();                  // 接收函数返回的结构体if(strcmp(p.job, "programmer") == 0)printf("programmer: %s, age %d\n", p.name, p.age);return 0;
}struct person getstruct(void)         // 函数返回类型是结构体类型 
{struct person person; strcpy(person.name, "Willion");person.age = 30;strcpy(person.job, "programmer");return person;                    // 函数返回结构体
}// 结果:
programmer: Willion, age 30

 5、结构体可以作为其它结构体的成员

注意:若结构体的成员是其它结构体(子结构体),则子结构体必须在之前定义,才能作为结构体的成员。否则,报错(error: field 'birth' has incomplete type。 注:此处birth为成员变量名)。

#include <stdio.h>
#include <string.h>struct birthday
{int year;int month;
};struct person
{char name[32];int age;char job[16];struct birthday birth;      // 结构体成员为其它结构体
};int main(void)
{struct person p;strcpy(p.name, "Jack");p.age = 18;strcpy(p.job, "programmer");p.birth.year = 2006;        // 使用成员访问运算符"."一级一级访问成员p.birth.month = 2;printf("%s, %d, %s, birthday %d-%d", p.name, p.age, p.job, p.birth.year, p.birth.month);return 0;
}// 结果:
Jack, 18, programmer, birthday 2006-2

 6、结构体大小

  • 使用sizeof获取结构体大小。
  • sizeof(结构体变量),返回结构体所有成员的内存的大小以及可能的填充字节。
  • 因结构体的内存布局和对齐方式,可能会占用字节填充。
#include <stdio.h>struct person
{char name[32];        // 字符串name:32字节int age;              // 整数age:4字节char *job;            // 字符串指针job:8字节(64位的计算机。若是32位的计算机,则4字节)
};int main(void)
{struct person p;printf("p memory size is %d\n", sizeof(p));return 0;
}// 结果:
p memory size is 48       // 优化对齐,有填充

可使用__attribute__设置内存布局,告诉编译器在编译过程中进行优化对齐。此功能和编译器有关。gcc编译器是非紧凑模式的。

__attribute__((packed)):使用紧凑内存布局,即取消优化对齐,按实际占用字节数对齐。

注意:attribute两边都是两个下划线"_"。

#include <stdio.h>struct person
{char name[32];        // 字符串name:32字节int age;              // 整数age:4字节char *job;            // 字符串指针job:8字节(64位的计算机)
};struct person_packed
{char name[32];int age;char *job;
}__attribute__((packed));    // 取消优化对齐int main(void)
{struct person p;struct person_packed p_packed;printf("p memory size is %d\n", sizeof(p));printf("p_packed memory size is %d\n", sizeof(p_packed));return 0;
}// 结果:
p memory size is 48            // 优化对齐,有填充
p_packed memory size is 44     // 取消优化对齐

 可使用标准库stddef.h中的宏offsetof查看结构体中各成员相对于结构体开头偏移多少字节。

宏 offsetof 的声明:      offsetof(type, member-designtor)

参数:type是class类型(例如:结构体),member-designtor是结构体的成员变量名。

返回:结构体的成员变量名相对于结构体开头的偏移量(单位:字节)。

#include <stdio.h>
#include <stddef.h>struct person
{char name[32];        // 字符串name:32字节int age;              // 整数age:4字节char *job;            // 字符串指针job:8字节(64位的计算机)
};struct person_packed
{char name[32];int age;char *job;
}__attribute__((packed));    // 取消优化对齐int main(void)
{printf("structure person: name offset %d bytes\n",offsetof(struct person, name));printf("structure person: age offset %d bytes\n",offsetof(struct person, age));printf("structure person: job offset %d bytes\n",offsetof(struct person, job));printf("structure person_packed: name offset %d bytes\n",offsetof(struct person_packed, name));printf("structure person_packed: age offset %d bytes\n",offsetof(struct person_packed, age));printf("structure person_packed: job offset %d bytes\n",offsetof(struct person_packed, job));return 0;
}// 结果:
structure person: name offset 0 bytes
structure person: age offset 32 bytes
structure person: job offset 40 bytes          // 优化对齐,有填充
structure person_packed: name offset 0 bytes
structure person_packed: age offset 32 bytes
structure person_packed: job offset 36 bytes

7、结构体指针

指针指向结构体时,该指针称为结构体指针。指针变量存储的是结构体变量的内存地址。

  • 需要结构体变量,并获取结构体变量的内存地址(&结构体变量)。
  • 需要结构体指针(*结构体指针变量)。
  • 指针指向结构体(结构体指针变量=&结构体变量)。
  • 通过"->"访问指针指向的结构体的成员(结构体指针变量->结构体成员)。也可以使用"."访问结构体成员( (*结构体指针变量). 结构体成员)。
#include <stdio.h>struct person
{char name[32];int age;char job[16];
};int main(void)
{struct person person = {"John", 18, "programmer"};       // 声明结构体变量,并初始化struct person *p;                                        // 声明结构体指针变量p = &person;                                             // 指针指向结构体printf("%s, %d, %s\n", p->name, p->age, p->job);         // 通过"->"访问结构体成员printf("%s, %d, %s\n", (*p).name, (*p).age, (*p).job);   // 通过"."访问结构体成员return 0;
}// 结果:
John, 18, programmer
John, 18, programmer

 可以根据用户输入,通过指针将内容写入结构体中。

#include <stdio.h>struct person
{char name[32];int age;char job[16];
};int main(void)
{struct person *p, person;                                // 声明结构体指针和结构体变量p = &person;                                             // 指针指向结构体printf("Input name: ");scanf("%s", &p->name);                                   // 获取用户输入,存储到结构体指针指向的结构体成员变量printf("Input age: ");scanf("%d", &p->age);printf("Input job: ");scanf("%s", &p->job);printf("%s, %d, %s\n", p->name, p->age, p->job);         // 通过"->"访问结构体成员printf("%s, %d, %s\n", (*p).name, (*p).age, (*p).job);   // 通过"."访问结构体成员return 0;
}// 结果:
Input name: John                 【输入:John】
Input age: 18                    【输入:18】
Input job: programmer            【输入:programmer】
John, 18, programmer
John, 18, programmer

可以将结构体指针作为函数参数,结构体指针指向结构体,可通过指针修改原结构体内容。

#include <stdio.h>
#include <string.h>struct person
{char name[32];int age;char job[16];
};int changestruct(struct person *p);int main(void)
{struct person *p, p1 = {"John", 18, "programmer"};p = &p1;                                               // 结构体指针指向结构体(即结构体指针变量中存储结构体的内存地址)printf("Before: %s, %d, %s\n", p->name, p->age, p->job);changestruct(p);                                       // 结构体指针作为参数传入函数printf("After: %s, %d, %s\n", p->name, p->age, p->job);return 0;
}int changestruct(struct person *p)                          // 函数参数是结构体指针
{strcpy(p->name, "Mark");p->age = 25;strcpy(p->job, "teacher");return 0;
}// 结果:
Before: John, 18, programmer
After: Mark, 25, teacher

8、typedef 数据类型重命名

每次定义结构体变量时,都要使用"struct 结构体名称 结构体变量名",使得代码繁琐。

可以使用typedef给整个结构体定义提供别名,每次定义结构体变量时,只需"别名 结构体变量名"即可,即用 "别名" 代替 "struct 结构体名称" 。

typedef:数据类型重命名(提供别名)

  • 可以给基本数据类型重命名,也可以给指针、数组、结构体等数据类型重命名。
  • 结构体重命名后,原结构体定义仍保留,不会丢失或改变。
  • 数据类型重命名,可以减少因误解或混淆导致的错误,使得代码更简洁易读。
typedef struct person
{char name[32];int age;char job[16];
} Person;                   //名为person的结构体,别名为PersonPerson person;              // 定义结构体变量person
Person *p                   // 定义结构体指针p
int fun1(Person person)     // 结构体person作为函数参数
int fun1(Person *p)         // 结构体指针p作为函数参数
...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/816864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android中协程的理解

Android中的协程是一种轻量级的并发编程结构&#xff0c;它允许在子线程中挂起和恢复运行。协程的主要目标是提高多线程编程的效率和可读性&#xff0c;特别是在需要频繁切换任务或操作的数据驱动的场景中。 理解协程需要理解其核心概念&#xff1a;任务、线程和状态。 任务&…

Qt 中默认代码

目录 主函数 widget的声明 widget的定义 form file .pro 文件 主函数 #include "widget.h" ​ #include <QApplication> ​ int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 上面就是 Qt 刚创建的一…

机器学习-随机森林温度预测模型优化

文章目录 前言旧模型训练新模型训练参数查看组合参数训练学习模型评估 前言 在机器学习-随机森林算法预测温度一文中&#xff0c;通过增大模型训练数据集和训练特征的方式去优化模型的性能&#xff0c;本文将记录第三方种优化方式&#xff0c;通过调整随机森林创建模型参数的方…

【高阶数据结构】哈希表 {哈希函数和哈希冲突;哈希冲突的解决方案:闭散列,开散列;红黑树结构 VS 哈希结构}

一、哈希表的概念 顺序结构以及平衡树 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间没有对应的关系。因此在查找一个元素时&#xff0c;必须要经过关键码的多次比较。顺序查找时间复杂度为O(N)&#xff1b;平衡树中为树的高度&#xff0c;即O(log_2 N)&#xf…

区间比较指令

1&#xff0c;比较值和和区间值比较 2&#xff0c;指令 ZCP K2 K5 C0 Y000 3&#xff0c;当比较值小于 区间返回 软元件灯亮 当比较直在区间内软元件1 接通 > 第一个 < 第二个 大于第二个值 软元件2接通 如果区间中第二个值大于第一个值那个只比较第一个值 直接比较 参数…

【HTML】制作一个简单的实时字体时钟

目录 前言 HTML部分 CSS部分 JS部分 效果图 总结 前言 无需多言&#xff0c;本文将详细介绍一段HTML代码&#xff0c;具体内容如下&#xff1a; 开始 首先新建文件夹&#xff0c;创建一个文本文档&#xff0c;两个文件夹&#xff0c;其中HTML的文件名改为[index.html]&am…

CSS3进阶技巧:Flexbox布局实战与高级应用

Flexbox布局是CSS3中一种强大的布局技术&#xff0c;它可以帮助我们更灵活地布局网页元素&#xff0c;解决传统布局方式中的一些问题。在本文中&#xff0c;我将介绍一些Flexbox布局的进阶技巧和高级应用。 Flexbox的基本概念 Flexbox布局是基于弹性盒子模型的&#xff0c;它主…

设计模式:生活中的命令模式

将命令模式类比到日常生活中,可以想象一个餐厅的运作过程。在这个类比中,顾客点菜的过程就像是发出一个命令,而厨师则是执行命令的接收者。 角色类比 顾客(Client):下达命令。他们决定要什么菜,并通知服务员。服务员(Invoker):接收命令,并传递给厨师。服务员不需要…

202209青少年软件编程(scratch图形化) 等级考试试卷(四级)

第1题&#xff1a;【 单选题】 运行下列程序&#xff0c;说法正确的是&#xff1f;&#xff08;&#xff09; A:列表中的数字全部小于11 B:列表的长度为 10 C:变量i最终值为 20 D:列表中有大于 10 的数字 【正确答案】: D 【试题解析】 : 程序运行后&#xff0c;变量i最…

Spark 应用程序优化和调优总结

文章目录 前言调整 Spark 默认配置查看和设置 Spark 配置信息动态扩展集群负载 数据的缓存和持久化DataFrame.cache()DataFrame.persist()何时缓存和持久化何时不缓存和持久化 Spark 中的 JOINs广播连接排序合并连接 总结 前言 本文总结了 Spark 中比较重要和常用的调优手段&a…

【探讨】光场空间结构全维度非线性调控理论及应用

摘要&#xff1a;得益于数字全息与几何相位平面光学技术的逐渐成熟&#xff0c;空间结构光场调控及应用研究已在线性光学领域取得蓬勃发展。与之相比&#xff0c;以非线性光学为物理途径的相关研究虽能实现许多关键功能(如光场间信息交互)却仍处于起步阶段。笔者课题组在国家自…

MoonBit 最新动态:MoonBit 引入实验性的测试覆盖率统计工具

MoonBit更新 支持 array.iter intrinsic 并且已经对标准库中的函数进行标注&#xff0c;从而可以在特定情况下将循环进行内联&#xff0c;以提升运行效率 /// intrinsic %array.iter pub fn iter[T](self : Array[T], f : (T) -> Unit) -> Unit {for i 0; i < self…

C语言100道练习题打卡(1)

1 有1&#xff0c;2&#xff0c;3&#xff0c;4四个数字&#xff0c;能组成多少个互不相同且不重复的三位数&#xff0c;都是多少 #include<stdio.h> //有1&#xff0c;2&#xff0c;3&#xff0c;4四个数字&#xff0c;能组成多少个互不相同且不重复的三位数&#xff…

Spring Batch

Spring是一个开放源代码的J2EE应用程序框架&#xff0c;由Rod Johnson发起&#xff0c;是针对bean的生命周期进行管理的轻量级容器&#xff08;lightweight container&#xff09;。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题&#xff0c;提供了功能强大IOC、AOP及W…

物联网数据集CIC IoT Dataset 2023和(TON_IoT)数据集以及网络数据集UNSW-NB15 Dataset:可单卖(si聊有优惠)

数据集描述如下&#xff1a; CIC IoT Dataset 2023是由加拿大网络安全研究所提供的一个数据集&#xff0c;旨在促进物联网&#xff08;IoT&#xff09;环境中大规模攻击的安全分析应用程序的开发。该数据集包含33种攻击&#xff0c;分为7类&#xff0c;包括DDoS、DoS、侦察、基…

mysql报错-mysql服务启动停止后,某些服务在未由其他服务或程序使用时将自动停止和数据恢复

启动mysql服务时出现该错误: 本地计算机上的mysql服务启动停止后,某些服务在未由其他服务或程序使用时将自动停止。 我的mysql版本是8.0.18 系统&#xff1a;win10 如何安装mysql&#xff0c;可以看我这一篇文章&#xff1a;mysql的安装 ---必会 - bigbigbrid - 博客园 (cn…

在数字鼎新的风口浪尖到底什么是下一个浪潮?

乔布斯有一句话&#xff0c;叫做我们是站在人文与技术的十字路口上&#xff0c;很多人把这句话的理解为苹果除了追求技术性能&#xff0c;还追求艺术美感&#xff0c;但如果你看看他 2001 年接受 NHK 的采访&#xff0c;你会明白乔布斯说的不是这个意思&#xff0c;他的意思应该…

常见分类算法详解

在机器学习和数据科学的广阔领域中&#xff0c;分类算法是至关重要的一环。它广泛应用于各种场景&#xff0c;如垃圾邮件检测、图像识别、情感分析等。本文将深入剖析几种常见的分类算法&#xff0c;帮助读者理解其原理、优缺点以及应用场景。 一、K近邻算法&#xff08;K-Nea…

注解(整理)

一、Spring相关注解 Autowired&#xff1a;这是Spring提供的一个非常方便的注解&#xff0c;用于自动装配bean。它可以应用于字段、构造函数和方法参数等&#xff0c;Spring容器会自动寻找匹配的bean并注入。这样&#xff0c;开发者无需手动编写繁琐的setter注入代码。Compone…

Android ParcelFileDescriptor实现进程间通信

需求 一个通信通道&#xff0c;实现跨进程的的Socket网络通信。 具体的通信通道的图如下。 需求分析 我们需要一个进程一直做通信通道的事情&#xff0c;业务进程把数据通过进程间通信交给通信进程。通信进程通过Socket通道将数据发给网络另外一端的通信进程。接收端的通信进…