C语言——自定义类型结构体_学习笔记

news2024/12/24 3:19:07

结构体的基本概念

结构体是一种用户自定义的数据类型,可以包含多个不同类型的变量。通过使用结构体,我们可以将相关联的数据组织在一起,便于管理和使用。

结构体的声明

正常的结构体声明

在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型的一类。
结构体可以包含多个不同类型的数据成员,例如:int、float、char等。

结构体的声明方式如下:

struct 结构体名 {
    类型1 数据成员1;
    类型2 数据成员2;
    ...
    类型n 数据成员n;
};

例如:

struct student {
    int id;
    char name[50];
    int age;
};                  //注意,结构体定义后面的分号不要忘了

以上属于是正常的结构体声明↑↑↑↑↑↑↑↑↑↑↑↑

匿名结构体声明

既然有正常的,那就有特殊的结构体声明了↓↓↓↓↓↓↓↓↓↓↓↓↓↓
在声明结构体的时候,可以不完全的声明。(匿名结构体类型)
如下:

//匿名结构体类型
struct
{
	int a;
	char b;
	float c;
}x;
struct 
{
	int a;
	char b;
	float c;
}*p;
  • p是结构体指针,这里可以看到,上下两个结构体的成员内容都是一模一样的,
  • 能不能实现 p = &x 呢?
  • 答案是不能,编译器会把上面的两个声明当成完全不同的结构体类型,所以p不能放x的地址!!!

上面两个结构体在声明的时候省略掉了结构体标签,这样声明也是可以的,但是!匿名的结构体类型,只能在创建的时候定义结构体变量,如果没有对结构体类型重命名的话,基本上只能使用一次。

typedef 结构体起别名

可以使用typedef关键字为结构体起别名,这样一些名字很长的结构体名字就可以简化,这样当我们需要频繁创建结构体变量的时候就比较方便。
例如:

#include <stdio.h>

struct Chinese_Student {
    char name[50];
    int age;
    float score;
};

typedef struct Chinese_Student Student; // 为结构体起别名Student

int main() {
    Student stu = {"Tom", 20, 88.5}; // 创建并初始化结构体变量,其实就等价于Chinese_Student stu = {"Tom", 20, 88.5};
    printf("Name: %s\n", stu.name); // 访问结构体变量的成员
    printf("Age: %d\n", stu.age);
    printf("Score: %.1f\n", stu.score);
    return 0;
}

结构体的自引用

在结构体中包含一个类型为结构体本身的成员可以吗?
比如下面这段代码:

struct Node
{
	int data;
	struct Node next;
};                      //error报错

这段代码是会报错的,因为结构体中包含同一个类型的结构体作为成员,这样结构体变量的大小就会无穷的大,是不合理的。

正确的结构体自引用方式,使用结构体指针

struct Node
{
	int data;
	struct Node* next;
};  

结构体变量的创建和初始化

对于结构体变量的创建和初始化,用示例代码来进行说明:

#include <stdio.h>

//创建结构体变量前,要先对结构体进行声明↓↓↓↓↓↓
struct student {
    char name[50];  //这里声明一个student类型的结构体,有三个成员
    int age;        //分别是字符类型姓名,整型类型年龄,浮点型类型分数
    float score;
};

int main() {
   
    // 可以在创建结构体变量的时候同时按声明中的结构体成员顺序对结构体变量进行初始化操作↓↓
    struct student stu1 = {"Tom", 20, 88.5}; 
    
    //也可以用成员访问操作符,不按照成员顺序对结构体成员进行初始化操作↓↓↓↓↓
    struct student stu2 = {.age=18, .name="weil", .score=99.9}; 
    
	// 访问结构体变量的成员↓↓↓↓↓↓↓
    printf("Name: %s\n", stu2.name); 
    printf("Age: %d\n", stu2.age);
    printf("Score: %.1f\n", stu2.score);
    return 0;
}

结构体内存对齐

对于结构体的大小,我们可以用sizeof 运算符进行计算
如下:

struct S1
{
	char c1;       //1字节
	int i;         //4字节
	char c2;       //1字节
};
int main()
{
	printf("%zd\n", sizeof(struct S1));
	return 0;
}

运行结果为
在这里插入图片描述
上面代码的结构体占的字节数大小为什么会是12个字节呢?

这就涉及到结构体内存对齐的知识点了↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

结构体内存对齐规定

  1. 结构体的第一个成员对齐到相对结构体变量起始位置偏移量为0的地址处
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数=编译器默认的一个对齐数与该成员变量大小的较小值。
    VS中默认的值为8
    Linux中没有默认对齐数,对齐数就是成员自身的大小
  3. 结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

为什么存在内存对齐

大部分的参考资料都是这样说的:

1.平台原因(移植原因):

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

2.性能原因:

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

假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

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

举例解释

struct S1
{
    char c1;
    char c2;
    int i;
};
struct S2
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%zd\n", sizeof(struct S1));
	printf("%zd\n", sizeof(struct S2));
	return 0;
}

以上代码运行结果(VS 2022):
在这里插入图片描述
结构体中的成员类型相同,不同的排列顺序也会影响结构体变量的大小,这就是结构体内存对齐的奥妙,接下来画内存布局图来进行解释:
在这里插入图片描述

那在设计结构体的时候,我们既要满足对齐,又要节省空间,就要让占用空间小的成员尽量集中在一起

结构体数组

↓↓↓↓↓↓↓↓↓↓↓↓↓创建结构体数组 & 遍历访问结构体数组↓↓↓↓↓↓↓↓↓↓↓↓↓
代码示例如下:

#include <stdio.h>

// 定义一个结构体类型
struct Student {
    char name[20];
    int age;
    float score;
};

int main() {
    // 创建结构体数组并初始化
    struct Student students[] = {
        {"Tom", 20, 88.5},
        {"Jack", 21, 92.0},
        {"Alice", 19, 95.5}
    };

    // 遍历访问打印结构体数组中的所有内容
    int len = sizeof(students) / sizeof(struct Student);  // 计算数组长度
    for (int i = 0; i < len; i++) {
        printf("Name: %s\n", students[i].name);
        printf("Age: %d\n", students[i].age);
        printf("Score: %.1f\n", students[i].score);
    }

    return 0;
}

在这个示例中,我们首先定义了一个名为Student的结构体类型,包含三个成员变量:name、age和score。然后,我们在main函数中创建了一个Student类型的结构体数组students,并初始化其中的元素。接着,我们使用一个for循环遍历访问数组中的每个元素,并使用printf函数打印出每个学生的姓名、年龄和分数。

在访问结构体数组元素时,我们使用了结构体变量名加上索引的方式来访问,例如students[i].name表示访问第i个元素的name成员变量。

结构体指针

↓↓↓↓↓↓↓↓↓↓↓↓↓结构体指针的创建 & 初始化 & 成员访问↓↓↓↓↓↓↓↓↓↓↓↓↓

#include <stdio.h>

// 定义一个结构体类型
struct Student {
    char name[20];
    int age;
    float score;
};

int main() {
    // 创建结构体变量并初始化
    struct Student stu1 = {"Tom", 20, 88.5};
    struct Student stu2 = {"Jack", 21, 92.0};
    struct Student stu3 = {"Alice", 19, 95.5};

    // 创建结构体指针变量
    struct Student *stuPtr;

    // 将结构体变量的地址赋给结构体指针变量
    stuPtr = &stu1;

    // 遍历打印结构体指针中的内容
    printf("Name: %s\n", stuPtr->name);
    printf("Age: %d\n", stuPtr->age);
    printf("Score: %.1f\n", stuPtr->score);

    // 将结构体指针变量指向下一个结构体变量
    stuPtr = &stu2;

    // 遍历打印结构体指针中的内容
    printf("Name: %s\n", stuPtr->name);
    printf("Age: %d\n", stuPtr->age);
    printf("Score: %.1f\n", stuPtr->score);

    // 将结构体指针变量指向下一个结构体变量
    stuPtr = &stu3;

    // 遍历打印结构体指针中的内容
    printf("Name: %s\n", stuPtr->name);
    printf("Age: %d\n", stuPtr->age);
    printf("Score: %.1f\n", stuPtr->score);

    return 0;
}

在这个示例中,我们首先定义了一个Student结构体类型,包含了学生的姓名、年龄和分数。在main函数中,我们创建了三个Student类型的结构体变量,并分别初始化它们。

然后,我们创建了一个Student类型的指针变量stuPtr,用于指向这些结构体变量。我们将第一个结构体变量的地址赋给了stuPtr,并使用->运算符通过指针访问结构体中的数据,并打印出学生的信息。接着,我们将指针变量指向下一个结构体变量,重复上述操作,直到遍历完所有的结构体变量。

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

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

相关文章

【SA8295P 源码分析】97 - QNX AIS Camera 框架介绍 及 Camera 工作流程分析

【SA8295P 源码分析】97 - QNX AIS Camera 框架介绍 及 Camera 工作流程分析 一、QNX AIS Server 框架分析二、QNX Hypervisor / Android GVM 方案介绍三、Camera APP 调用流程分析四、QCarCam 状态转换过程介绍五、Camera 加串-解串 硬件链路分析六、摄像头初始化检测过程介绍…

Debian环境下搭建STM32开发环境

1. 安装交叉编译工具&#xff0c;解压gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2&#xff0c;并且把交叉编译环境添加到path路径。 2.安装下载工具驱动和下载工具 # 安装下载工具openocd sudo apt -y install openocd 3.下载测试 sudo openocd -f cmsis-dap.cfg -…

吴恩达ChatGPT《Finetuning Large Language Models》笔记

课程地址&#xff1a;https://learn.deeplearning.ai/finetuning-large-language-models/lesson/1/introduction Introduction 动机&#xff1a;虽然编写提示词&#xff08;Prompt&#xff09;可以让LLM按照指示执行任务&#xff0c;比如提取文本中的关键词&#xff0c;或者对…

postgresql|数据库|centos7下基于postgresql-12的主从复制的pgpool-4.4的部署和使用

前言&#xff1a; postgresql数据库只用自身的一些配置是无法做到最优的优化的&#xff0c;需要通过一些外置插件&#xff08;中间件&#xff09;来提高服务器的整体性能&#xff0c;通俗的说就是数据库仅仅依靠自身是无法达到性能最优的&#xff0c;很多时候需要更改数据库的…

网工证书选择,就业岗位等相关说明

1.网工就业岗位说明。 2.未来职业走向等。 -- 厂商认证 -- HCIE - Datacom HCIE - Secuurity CCIE - EI等等 -- 通信行业 组网 产品 搞技术 - 迭代速度快 -- 厂商&#xff1a; 设计 制造 生产网络设备的公司 - 思科 华为 华三 锐捷等 -- 目的&#xff1a;找…

flink的main方法和execute方法的关系

背景&#xff1a; 执行flink时&#xff0c;我们几种执行模式&#xff0c;比如在IDE本地执行模式以及远程YARN执行的模式等&#xff0c;你是否有疑问&#xff0c;为什么他们可以共用相同的代码呢&#xff1f;其实这就涉及到main方法和execute方法的关系了 flink的main方法和ex…

HUAWEI华为MateBook X Pro 2021款 i7 集显(MACHD-WFE9Q)原装出厂Win10系统20H2

华为笔记本电脑原厂系统自带指纹驱动、显卡驱动、声卡驱动、网卡驱动等所有驱动、出厂主题壁纸、系统属性华为专属LOGO标志、Office办公软件、华为电脑管家等预装程序 链接&#xff1a;https://pan.baidu.com/s/1oeSM0ciwyyRIKms5tR4SNA?pwdo2gq 提取码&#xff1a;o2gq

elasticsearch18-自动补全实战

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;三人行&#xff0c;必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》&#xff0c;SpringCloud…

国家网络安全宣传周知识竞赛活动小程序界面分享

国家网络安全宣传周知识竞赛活动小程序界面分享

解决因为修改SELINUX配置文件出错导致Faild to load SELinux poilcy无法进入CentOS7系统的问题

一、问题 最近学习Kubernetes&#xff0c;需要设置永久关闭SELINUX,结果修改错了一个SELINUX配置参数&#xff0c;关机重新启动后导致无法进入CentOS7系统&#xff0c;卡在启动进度条界面。 二、解决 多次重启后&#xff0c;在启动日志中发现 Faild to load SELinux poilcy…

基于直方图的增强显示

背景 由于需要经常分析浮点型的图像&#xff0c;而浮点型图像经常不能突出显示感兴趣的区域的&#xff0c;如下图所示&#xff1a; 而使用imagej软件&#xff0c;选中一个较小的感兴趣区域&#xff0c;调出其直方图&#xff0c;然后点击设置就可以增强整个图像对比度&#xff…

LabVIEW使用ModbusTCP协议构建分布式测量系统

LabVIEW使用ModbusTCP协议构建分布式测量系统 分布式测量系统主要用于监控远程物体。这种系统允许对系统用户获得的数据进行全面的数据收集、处理、存储和组织访问。它们可能包括许多不同类型的传感器。 在任何具有互联网接入的个人计算机上运行的软件都会发送来自传感器的测…

excel中 将一列数字按照,分割开

excel中 将一列数字按照,分割开 在 Excel 中&#xff0c;你可以使用文本分列功能将一列数字按照逗号分割开。以下是在 Excel 中执行该操作的步骤&#xff1a;1.打开 Excel 并导入包含要分列的数据的工作表。 2.选择要进行分列的列。例如&#xff0c;如果数据位于列 A&#xff…

Python批量统计pdf中“中文”字符的个数

之前的文章提供了批量识别pdf中英文的方法,详见【python爬虫】批量识别pdf中的英文,自动翻译成中文上。以及自动pdf英文转中文文档,详见【python爬虫】批量识别pdf中的英文,自动翻译成中文下。以及Python统计pdf中英文单词的个数。    本文实现Python统计pdf中中文字符的…

Qt/C++音视频开发54-视频监控控件的极致设计

一、前言 跌跌撞撞摸爬滚打一步步迭代完善到今天&#xff0c;这个视频监控控件的设计&#xff0c;在现阶段水平上个人认为是做的最棒的&#xff08;稍微自恋一下&#xff09;&#xff0c;理论上来说应该可以用5年不用推翻重写&#xff0c;推翻重写当然也是程序员爱干的事情&am…

XAPI项目架构:应对第三方签名认证的设计与调整

《XAPI项目》&#xff1a;GitHub仓库&#xff08;勿打&#x1f6ab;小破站一个&#xff09; 该项目是基于鱼皮的《API开发平台》项目的需求和架构设计上进行Golang版本开发的。 这篇文章&#xff0c;主要内容是记录在《XAPI项目》的原架构上&#xff0c;为了应对第三方签名认证…

SpringBoot 的版本、打包、Maven

一、SpringBoot 结构、集成 1.1、集成组件 Spring Core&#xff1a;Spring的核心组件&#xff0c;提供IOC、AOP等基础功能&#xff0c;是Spring全家桶的基础。 Spring Boot&#xff1a;一个基于Spring Framework的快速开发框架&#xff0c;可以快速创建独立的、生产级别的…

chatgpt综述和报告

ChatGPT究竟强在哪&#xff1f;复旦大学邱锡鹏教授《大型语言模型的能力分析与应用》_哔哩哔哩_bilibili2022年底&#xff0c;美国OpenA1公司发布了ChatGPT&#xff0c;一个可以与人类对话交互的千亿规模参数的大型语言模型。它可以根据用户输入的指令完成各种语言相关的任务&a…

elasticsearch14-高亮

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;三人行&#xff0c;必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》&#xff0c;SpringCloud…

HTML 学习笔记(基础)

它是超文本标记语言&#xff0c;由一大堆约定俗成的标签组成&#xff0c;而其标签里一般又有一些属性值可以设置。 W3C标准&#xff1a;网页主要三大部分 结构&#xff1a;HTML表现&#xff1a;CSS行为&#xff1a;JavaScript <!DOCTYPE html> <html lang"zh-…