当前位置: 首页 > article >正文

C语言结构体详解(一)(能看懂文字就能明白系列)

🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟个人主页:  古德猫宁-    🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟

                                                             专栏:​​​​​​深入理解计算机系统​​​​​

                                                                        C语言基础习题 

                                                                        C语言笔记

目录

结构的声明

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

结构体变量的创建

结构体的初始化

结构的特殊声明

 访问结构成员

结构体的自引用


引子:假如我们要设计一个程序来打印一份图书目录,打印每本书的各种信息:书名、作者、出版社、版权日期、页数和价格等。其中的一些项目(书名,作者)可以存储在字符数组中,其他项目需要一个int数组或者float数组来存储(价格,页数)。这样用七个不同的数组分别记录每一项的内容比较麻烦,且需要开辟许多空间,特别是要创建多份列表的时候。

所以,我们需要一种既可以包含字符串又能包含数字的数据形式,而且还要保持各信息的独立,C语言中的结构体就能满足你的需求,接下来让我来介绍结构体是什么,有什么用,怎么用。

结构的声明

结构声明描述了一个结构的组织布局,声明如下:

struct tag
{
   member- list ;
}variable- list ;

 假设要创建一个图书目录的结构:

struct book
{
	char title[20];//书名
	int value;//价格
	char author[20];//作者
	//…………
};

该声明描述了一个由俩个字符数组和一个int类型变量组成的结构。该声明并未创建实际的数据对象,只是描述了该对象由什么组成。(有时候我们把结构声明成为模板,因为它勾勒出结构是如何存储数据的)

接下来解释上面的一些细节,首先是关键字struct,它表明跟在其后的是一个结构,后面是一个可选的标记(上面是book),后面的程序中可以使用该标记引用该结构。所以,我们在后面的程序中可以这样声明:

struct book library

 这里libiary声明为一个使用book结构布局的结构变量。

在结构声明中,用一对花括号括起来的是成员列表。每个成员都用自己的声明来描述,成员可以是任意一种C语言的数据类型

最后括号后面的分号是必需的(不能省略),因为它表示结构布局定义结束。

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

1、结构体变量的创建

声明完结构之后,我们就要来创建结构体变量和初始化了

首先,结构有两层含义,一层含义是“结构布局”,结构布局告诉编译器如何表示数据,但是它并未让编译器为数据分配空间,所以接下来就是创建一个结构变量,即是结构的另一层含义。

程序中创建结构变量的一行是:

struct book library

 编译器执行这行代码便会创建一个结构变量library。编译器使用book模板为该变量分配空间:

一个内含20个元素的字符数组title,一个内含20个元素的字符数组author,一个int类型变量

这些存储空间都与一个名称library结合在一起。

例如下图:

在结构声明的变量中,struct book 所起的作用相当于一般声明中的int或float。

例如:可以定义两个struct book类型的变量,或者是指向struct book 类型结构的指针:

struct book 
{ //结构体成员
	char title[20];//书名
	int  value;//价格
	char author[20];//作者
};
struct book library1;//定义结构体变量1
struct book library2;//定义结构体变量2
struct book *pst;//定义指向结构体的指针
pst = &library;//将指针指向结构体变量

2、结构体的初始化

struct book
{
	char title[20];//书名
	int value;//价格
	char author[20];//作者
	//…………
};
int main()
{
    //按照结构体成员的顺序来初始化
	struct book library = { "C Primer Plus",99,"史蒂夫-普拉达" };
    //未按照结构体成员的顺序来初始化
struct book library = { .author = "史蒂夫 - 普拉达",.title = "C Primer Plus" ,.value = 99 };
	return 0;
}

 当然,我们也可以通过将指针指向结构体变量的地址,使用指针来访问和修改结构体的成员;

#include<stdio.h>
struct book 
{ //结构体成员
	char title[20];//书名
	int  value;//价格
	char author[20];//作者
};
int main()
{
	struct book library;//定义结构体变量
	struct book* pst = &library;//定义指向结构体的指针,将指针指向结构体变量
	pst->value = 30;//通过指针修改结构体成员
	return 0;
}

结构的特殊声明

在声明结构的时候,可以不完全声明

struct
{
 int a;
 char b;
 float c;
}x;


struct
{
 int a;
 char b;
 float c;
}s[20], *p;//这里表示创建了一个包含20个元素的结构体数组s,每个元素包含一个整数a,一个字符b,一个浮点数c
//同时定义了一个指向这个结构体类型的指针p


上面的结构体在声明的时候省略掉了结构体标签(tag),称为匿名结构体

注意:

匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。

编译器会把上面的两个声明当成完全不同的两个类型。

//所以在上面的两个结构体代码基础上,下面的代码是非法的

p=&x;

访问结构成员

结构类似于一个“超级数组”,这个超级数组中,可以是一个元素为char类型,下一个元素为float类型,下一个元素为int数组。我们可以通过数组下标单独访问数组中的各元素,那么,如何访问结构中的成员?

使用结构成员运算符——点(.)访问结构中的成员。

例如:

struct book
{
	char title[20];//书名
	int value;//价格
	char author[20];//作者
	//…………
};
int main()
{
    //按照结构体成员的顺序来初始化
	struct book library = { "C Primer Plus",99,"史蒂夫-普拉达" };
    //未按照结构体成员的顺序来初始化
    struct book library = { .author = "史蒂夫 - 普拉达",.title = "C Primer Plus" ,.value = 99 };
    printf("title:%s\n", library.title);
	printf("value:%d\n", library.value);
	printf("author:%s\n", library.author);
	return 0;
}

结构体的自引用

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

比如,定义一个链表的节点;

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

上述的代码是否正确,如果正确的话,那sizeof(struct Node)是多少

仔细分析,其实是不行的,因为一个结构体中再包含一个同类型的结构体变量,这样结构体变量的大小就会无穷的大,是不合理的。

正确的自引用方式:
 

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

在结构体自引用使用的过程中,夹杂了typedef对匿名结构体类型重命名,也容易引入问题,看看下面的代码有无错误:
 

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

答案是不行的,因为Node是对前面的匿名结构体类型的重命名产生的,但是在匿名结构体内部提前使用Node类型来创建成员变量,这是不行的。

解决方案如下:定义结构体不要使用匿名结构体了

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

本章是对于C语言结构体的简介,下篇将持续介绍有关结构体的内容

感谢各位观看


http://www.kler.cn/a/154300.html

相关文章:

  • 大数据-226 离线数仓 - Flume 优化配置 自定义拦截器 拦截原理 拦截器实现 Java
  • 结构化需求分析与设计
  • AntFlow:一款高效灵活的开源工作流引擎
  • 【第四课】rust声明式宏理解与实战
  • 【软件测试】一个简单的自动化Java程序编写
  • 代码随想录算法训练营第四十八天|Day48 单调栈
  • PyLMKit(5):基于网页知识库的检索增强生成RAG
  • 前端页面转pdf
  • Couchdb 命令执行漏洞复现 (CVE-2017-12636)
  • 常见场景题-接口重试策略如何设计?
  • Day41 使用listwidget制作简易图片播放器
  • 科研学习|论文解读——Open government research over a decade: A systematic review
  • 【android开发-06】android中textview,button和edittext控件的用法介绍
  • vue3-vite-ts:编写Rollup插件并使用 / 优化构建过程
  • 来CSDN一周年啦!!!
  • DBeaver 社区版(免费版)下载、安装、解决驱动更新出错问题
  • 初识短线交易
  • 华为OD机试真题-来自异国的客人-2023年OD统一考试(C卷)
  • 关于标准库中的vector - (涉及迭代器失效,深浅拷贝,构造函数,内置类型构造函数,匿名对象)
  • 解决因系统重装,导致QT编译器无法使用的办法
  • SickOs1.2
  • linux 下如何将/dev/nvme0n1符格式化为空盘符
  • 有点迷糊class和初始化参数的用法了
  • 基于llm的智能体-生命体
  • 进程的创建:fork()
  • 自动提交日志脚本(4) 时间管理部分