C语言 - 结构体、结构体数组、结构体指针和结构体嵌套

结构体的意义

问题:学籍管理需要每个学生的下列数据:学号、姓名、性别、年龄、分数,请用 C 语言程序存储并处理一组学生的学籍。
单个学生学籍的数据结构:

  • 学号(num): int 型
  • 姓名(name) :char [ ] 型
  • 性别(sex):char 型
  • 年龄(age):int 型
  • 分数(score):float 型
    思考:如果有多个学生,该怎么定义,已学数据类型无法解决(已学的数据类型需要定义好多变量,不友好)。

概述

  • 正式:
    结构体是由一批数据组合而成的结构型数据。组成结构型数据的每个数据被称为结构型数据的 “成员” ,其描述了一块内存区间的大小及解释意义。
  • 通俗:
    结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。

在C语言中,定义结构体的语法格式如下:

struct 结构体名 {类型 成员1;类型 成员2;// ...
};

其中,结构体名是您自定义的结构体类型名称,可以根据需求进行命名。成员1成员2等表示结构体的成员变量,每个成员都有自己的类型和名称。

定义结构体后,可以使用该结构体类型创建结构体变量,并访问结构体的成员。访问结构体成员的语法是使用结构体变量名后跟成员名,中间使用点.进行连接。

结构体的使用

  • struct 结构体名 变量名

  • struct 结构体名 变量名 = {成员1值,成员2值…}

  • 定义结构体时顺便创建变量(这时候创建几个变量都可以,中间用逗号隔开,直接在创建的时候赋值也可以,例如:)

    struct student
    {int num; //学号char name[16];  //姓名float score;  //成绩
    }stu5 = {1002,"lihua",89},stu6;
    
  • 如果只想给一部分数据赋值的话:

    struct 结构体名 变量名 = {
    .name = "cuiyi",
    .num = 111,
    };
    

下面是一个更完整的示例:

#include <stdio.h>// 定义一个结构体
struct Person {char name[50];int age;float height;
};int main() {// 创建一个结构体变量struct Person person1;// 访问结构体的成员strcpy(person1.name, "John");person1.age = 25;person1.height = 1.75;// 输出结构体的成员printf("Name: %s\n", person1.name);printf("Age: %d\n", person1.age);printf("Height: %.2f\n", person1.height);return 0;
}

在上述示例中,我们定义了一个名为Person的结构体,它包含了姓名、年龄和身高三个成员变量。然后,我们创建了一个名为person1的结构体变量,并给它的成员赋值。最后,使用printf函数输出结构体的成员值。

结构体数组

  • 作用:将自定义的结构体放入数组中方便维护
  • 语法:
    struct 结构体名 数组名[元素个数] = {{}, {}, …{}}
    示例:
#include<stdio.h>struct stu
{char name[16];int age;float score;
}s[3];int main()
{struct stu s[3] = {{"zhangsan",18,500},{"lisi",18,530},{"wangwu",18,550}};int i;for (i = 0; i < 3; i++){printf("name=%s, age=%d, score=%f\n",s[i].name,s[i].age,s[i].score);}return 0;
}

结构体指针

  • 作用:通过指针访问结构体的成员
  • 语法:struct 结构体名 *指针名;
  • 利用操作符->可以通过结构体指针访问结构体属性(比如s.name 有一个指针 ps 指向 s ,那么可以用 ps->name 代替 s.name

示例:
结构体数组和结构体指针

#include<stdio.h>struct stu
{char name[16];int age;float score;
}s[3];int main()
{struct stu s[3] = {{"zhangsan",18,500},{"lisi",18,530},{"wangwu",18,550}};struct stu *ps = s;//定义一个指针指向结构体数组int i;for (i = 0; i < 3; i++){printf("name=%s, age=%d, score=%f\n",(*(ps+i)).name,(*(ps+i)).age,(*(ps+i)).score);}return 0;
}

在上面的代码中,一定要记得 (*(ps+i)) 才是一个大括号里面的值,这样才能 .name .age .score 。

结构体嵌套结构体

  • 含义
    结构体中的成员可以是另一个结构体
  • 语法
struct 结构体名 
{struct 结构体名 成员名;
};

示例:

#include <stdio.h>
#include <string.h>struct person
{char name[16];int age;char sex;
};struct  student
{struct person stu; float score;
};struct teacher
{struct person tea;char phone[12];
};int main(int argc, const char *argv[])
{struct student s;strcpy (s.stu.name,"zhangsan");s.stu.age = 12;s.stu.sex = 'm';s.score = 98;printf("name = %s,age = %d, sex = %c, score = %f\n", s.stu.name, s.stu.age, s.stu.sex, s.score);struct teacher t;struct teacher *p = &t;strcpy (p->tea.name, "lisi");p->tea.age = 54; //注意这里操作符 -> 的用法p->tea.sex = 'w';strcpy (p->phone, "13112341234");printf("name = %s,age = %d, sex = %c, score = %s\n", t.tea.name, t.tea.age, t.tea.sex, t.phone);return 0;
}

上述代码中,有一个部分用到了 p->tea.age 这种形式,这是因为p是指针,而 tea 只是一个普通变量,所以从 tea 出发不能用 ->,只有指针才可以使用这个操作符。

结构体大小

字节对齐

  • 含义
    字节对齐主要是针对结构体而言的,通常编译器会自动对其成员变量进行对齐,以提高数据存取的效率。(因为如果按照类型实际的大小来判断的话,那么需要判断很多次,这样对齐了以后有规律就不用判断了)
  • 作用
    • 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
    • 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

计算方法

  • 自身对齐 (这个数据类型大小是多少就是多少)
  • 默认对齐 (4字节)
  • 有效对齐 (在自身对齐和默认对齐之间选最小)
    在这里插入图片描述

规则(地址 / 有效地址) 必须是整数。

计算过程:

  1. 把结构体里每个变量的类型的自身对齐,默认对齐和有效对齐分别写出来;
  2. 以有效对齐为准写每个变量的地址,最开始那个变量的地址肯定是0,然后后面叠加,注意在这个过程中要遵从**<规则>**,比如图中的变量 b ,本来地址应该是 1 ,但是因为 1 / 4 不是整数,所以要扩充到 4 凑整,那么这个时候 变量 a 的地址浪费了 1 2 3 这三个地址,又因为 b 本身就是 4 个字节,所以它的地址是 4 5 6 7。 c 和 d 因为都可以整除有效对齐,所以每个都加 1 个字节就行;
  3. 最终看一下,有效对齐最大的是 4 ,所以每个都要以 4字节 对齐,则要在变量 d 的后面再补 2 个地址:10 和 11 (因为前面的 8 和 9 已经占了 2 个地址了,还差 2 个地址凑够 4 个地址)。
  4. 得出结果:结构体 A 的地址是 0~11 ,所以大小是 12 。

上述过程要注意:能不能整除只能决定每个变量开头的地址,具体要每一行的地址从开头的地址要写到几要看变量类型的 sizeof 是多少。比如有 double c,c的开头地址是 8 ,那么这个变量占的字节就是 8 9 10 11 12 13 14 15 这八个字节。

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

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

相关文章

常见网络通信协议(http、https、ws)及安全协议(SSL、TLS、XTLS)

文章内容删除了一大半不合适的内容&#xff0c;发不出来&#xff0c;你懂得。&#x1f970; 一、常见网络通信协议1.1、HTTP 协议1.11 HTTP 协议简介1.12 HTTP 协议的工作流程1.13 HTTP 协议的常用方法1.14 HTTP 协议的常见状态码1.15 HTTP 的缺点 1.2 HTTPS 协议1.21 HTTPS 协…

结合OB Cloud区别于MySQL的4大特性,规划降本方案

任何一家企业想要获得持续性的发展与盈利&#xff0c;“降本增效”都是难以绕开的命题。但是“一刀切”的降本影响往往不太可控&#xff0c;成本的快速收缩往往会给业务带来低效运营和增长缓慢的风险。所以我们所说的降本&#xff0c;是指在成本降低的同时&#xff0c;效率不降…

js 正则表达式 验证 :页面中一个输入框,可输入1个或多个vid/pid,使用英文逗号隔开...

就是意思一个输入框里面&#xff0c;按VID/PID格式输入,VID和PID最大长度是4,最多50组 1、页面代码 <el-form ref"ruleForm" :model"tempSet" :rules"rules" label-position"right"> <!-- 最多 50组&#xff0c;每组9个字符…

【C语言】字符函数,字符串函数,内存函数

大家好&#xff01;今天我们来学习C语言中的字符函数&#xff0c;字符串函数和内存函数。 目录 1. 字符函数 1.1 字符分类函数 1.2 字符转换函数 1.2.1 tolower&#xff08;将大写字母转化为小写字母&#xff09; 1.2.2 toupper&#xff08;将小写字母转化为大写字母&…

ZigBee案例笔记 -- RFID卡片读写(模拟饭卡)

RFID模拟饭卡应用 RFID&#xff08;射频识别技术&#xff09;RFID通讯协议RFID发展历史RFID操作流程说明RFID卡片读写流程RFID寻卡RFID防碰撞RFID选卡RFID卡密验证RFID读卡RFID写卡读写数据流程 RFID饭卡模拟案例驱动代码串口协议饭卡操作案例结果优化建议 RFID&#xff08;射频…

Cordova Android 生成的 APK 中添加代码混淆

要在 Cordova Android 生成的 APK 中添加代码混淆&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1. 在项目根目录下&#xff0c;找到 platforms/android/ 目录&#xff0c;进入该目录。 2. 打开 build.gradle 文件&#xff0c;并在 android { ... } 部分添加以下代码&…

关于两个不同数据库的两张表建立数据库链接,关联查询数据

一、数据库链接 数据库链接&#xff08;database link&#xff09;是用于跨不同数据库之间进行连接和数据传输的工具或方法。它允许在一个数据库中访问另一个数据库中的对象和数据。 二、具体操作 以Oracle数据库为例 --1.建立链接tjpt CREATE DATABASE LINK tjpt CONNECT…

go语言--锁

锁的基础&#xff0c;go的锁是构建在原子操作和信号锁之上的 原子锁 原子包实现协程的对同一个数据的操作&#xff0c;可以实现原子操作&#xff0c;只能用于简单变量的简单操作&#xff0c;可以把多个操作变成一个操作 sema锁 也叫信号量锁/信号锁 核心是一个uint32值&#…

基于单片机的串行通信发射机设计

一、项目介绍 串行通信是一种常见的数据传输方式&#xff0c;允许将数据以比特流的形式在发送端和接收端之间传输。当前实现基于STC89C52单片机的串行通信发射机&#xff0c;通过红外发射管和接收头实现自定义协议的数据无线传输。 二、系统设计 2.1 单片机选择 在本设计中&…

前端基础2——CSS样式

文章目录 一、使用方式1.1 内联方式1.2 内部方式1.3 外部导入方式&#xff08;推荐&#xff09; 二、选择器类型2.1 元素选择器2.2 ID选择器2.3 类选择器2.4 派生选择器 三、常用属性3.1 内边距和外边距3.2 文本3.3 边框3.4 背景3.5 定位3.6 浮动3.7 字体3.8 其他属性 四、案例…

MySQL 数据库常用命令大全(完整版)

文章目录 1. MySQL命令2. MySQL基础命令3. MySQL命令简介4. MySQL常用命令4.1 MySQL准备篇4.1.1 启动和停止MySQL服务4.1.2 修改MySQL账户密码4.1.3 MySQL的登陆和退出4.1.4 查看MySQL版本 4.2 DDL篇&#xff08;数据定义&#xff09;4.2.1 查询数据库4.2.2 创建数据库4.2.3 使…

DataTable扩展 列转行方法(2*2矩阵转换)

源数据 如图所示 // <summary>/// DataTable扩展 列转行方法&#xff08;2*2矩阵转换&#xff09;/// </summary>/// <param name"dtSource">数据源</param>/// <param name"columnFilter">逗号分隔 如SDateTime,PM25,PM10…

docker desktop安装es 并连接elasticsearch-head:5

首先要保证docker安装成功&#xff0c;打开cmd&#xff0c;输入docker -v&#xff0c;出现如下界面说明安装成功了 下面开始安装es 第一步&#xff1a;拉取es镜像 docker pull elasticsearch:7.6.2第二步&#xff1a;运行容器 docker run -d --namees7 --restartalways -p 9…

ShardingSphere——压测实战

摘要 Apache ShardingSphere 关注于全链路压测场景下&#xff0c;数据库层面的解决方案。 将压测数据自动路由至用户指定的数据库&#xff0c;是 Apache ShardingSphere 影子库模块的主要设计目标。 一、压测背景 在基于微服务的分布式应用架构下&#xff0c;业务需要多个服…

WebVR — 网络虚拟现实

推荐&#xff1a;使用 NSDT编辑器 快速搭建3D应用场景 虚拟现实设备 随着Oculus Rift和许多其他生产设备即将上市&#xff0c;未来看起来很光明——我们已经有足够的技术来使VR体验“足够好”&#xff0c;可以玩游戏。有许多设备可供选择&#xff1a;像Oculus Rift或HTC Vive这…

JasperReport定义变量后打印PDF变量为null以及整个pdf文件为空白

问题1: JasperReport打印出来的整个pdf文件为空白文件&#xff1b; 问题2&#xff1a;JasperReport定义变量后打印PDF变量为null&#xff1b; 问题1原因是因为缺少数据源JRDataSource JasperFillManager.fillReport(jasperReport, params,new JREmptyDataSource());如果你打印…

Go 使用 Gorm 将操作信息集成到链路跟踪 Jaeger,进行增删改查使用举例,并做可视化UI界面展示(附源码)

Go 使用 Gorm 将操作信息集成到链路跟踪 Jaeger,进行增删改查使用举例(附源码)。 为了增强程序的可观测性,方便问题定位,在发起数据库操作请求时我们也可以调用代码统一集成链路跟踪的能力,Jaeger 是当今比较流行的选择。使用 Gorm 来将操作信息集成到 Jaeger 中。 全面…

Autofac中多个类继承同一个接口,如何注入?与抽象工厂模式相结合

多个类继承同一个接口,如何注入&#xff1f;与抽象工厂模式相结合 需求: 原来是抽象工厂模式,多个类继承同一个接口。 现在需要使用Autofac进行选择性注入。 Autofac默认常识: Autofac中多个类继承同一个接口,默认是最后一个接口注入的类。 解决方案&#xff1a;(约定大于配…

使用远程桌面软件改善工作与生活的平衡

在当今高度互联的世界中&#xff0c;我们的工作和个人生活之间的界限变得越来越模糊。在本文中&#xff0c;我们将探讨像 Splashtop 这样的远程桌面工具如何成为实现和谐工作与生活平衡不可或缺的一部分。 在当今的背景下理解工作与生活的平衡 工作与生活的平衡不仅仅是一个时…

【C++进阶】模板进阶

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…