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

news2024/12/24 2:08:12

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

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

                                                                        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.coloradmin.cn/o/1277697.html

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

相关文章

re:Invent大会,亚马逊云科技为用户提供端到端的AI服务

11月末&#xff0c;若是你降落在拉斯维加斯麦卡伦国际机场&#xff0c;或许会在大厅里看到一排排AI企业和云厂商相关的夸张标语。走向出口的路上&#xff0c;你的身边会不断穿梭过穿着印有“AI21Lab”“Anthropic”等字样的AI企业员工。或许&#xff0c;你还会被机场工作人员主…

6大关键词:尝新/随心/低忠诚···,全面解读食品饮料行业发展趋势与消费者洞察|徐礼昭

内容&#xff1a;重构零售实验室&商派 《2023食品饮料行业零售数字化洞察报告》节选 作者&#xff1a;徐礼昭&#xff08;商派市场负责人&#xff0c;重构零售实验室负责人&#xff09; 如今品牌的影响力不再止于资本与业绩数字&#xff0c;更多是在产品核心价值以及消费…

Django HMAC 请求签名校验与 Vue.js 实现安全通信

概要 在 Web 应用的开发过程中&#xff0c;确保数据传输的安全性和完整性是一个不容忽视的问题。使用 HMAC&#xff08;Hash-based Message Authentication Code&#xff09;算法对请求内容进行签名校验&#xff0c;是一种常见且有效的安全策略。本文将详细介绍如何在 Django …

Git 简介及异常场景处理

一、简介 介绍Git之前&#xff0c;还得先介绍下 版本控制系统&#xff08;VCS&#xff09;&#xff0c; 和它的发展历史 纵观版本控制系统的发展历史&#xff0c;广义上讲&#xff0c;版本控制工具的历史可以分为三代&#xff1a; 第一代 第一代版本控制系统被称为本地版本控…

组合(回溯+剪枝、图解)

77. 组合 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 样例输入 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],…

LORA概述: 大语言模型的低阶适应

LORA概述: 大语言模型的低阶适应 LORA: 大语言模型的低阶适应前言摘要论文十问实验RoBERTaDeBERTaGPT-2GPT-3 结论代码调用 LORA: 大语言模型的低阶适应 前言 LoRA的核心思想在于优化预训练语言模型的微调过程&#xff0c;通过有效地处理权重矩阵的变化&#xff08;即梯度更新…

Docker中部署并启动RabbitMQ

目的 由于最近频繁更换云服务器&#xff0c;导致环境啥的都需要重新配置&#xff0c;关于RabbitMQ&#xff0c;我在看其他博主的文章时&#xff0c;总是不能第一时间找到想要的配置方法&#xff08;也不是没有&#xff0c;只是花的时间太久&#xff09;&#xff0c;于是打算自己…

接口响应时长几十秒问题排查以及解决

目录 背景 解决方案 总结 背景 线上系统运行几年后&#xff0c;被项目上提bug&#xff0c;有些接口响应很慢&#xff0c;加载页面要几十秒 解决方案 1、步骤一&#xff0c;加索引 性能优化成本高&#xff0c;需要开发周期&#xff0c;临时方案先分析慢sql&#xff0c;通过增…

C语言——深入理解指针(4)

目录 1.回调函数 2. qsort 函数的使用 2.1 排序整型数据 2.2 排序结构体数据 3. qsort 函数的模拟实现 1.回调函数 回调函数就是通过一个函数指针调用的函数。 你把函数的地址作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其所指向的函数时&#xff0c;被调…

Linux:查看端口占用的进程

命令 netstat -tunlp可以从图中看到&#xff0c;端口被那个进程占用&#xff0c;对应进程的pid是多少。

软件测试工程师如何面试?

首先作为HR的角度&#xff1a; 一般我们面试的时候都会问应聘者一些问题&#xff0c;但是问什么&#xff1f;怎么问&#xff1f;每个HR都会有不同的做法。 有的HR问的比较广泛&#xff0c;有的HR比较注重专业度&#xff0c;还有的HR喜欢问一些开放性的问题&#xff0c;没有标…

版本控制系统Git学习笔记-Git分支操作

文章目录 概述一、Git分支简介1.1 基本概念1.2 创建分支1.3 分支切换1.4 删除分支 二、新建和合并分支2.1 工作流程示意图2.2 新建分支2.3 合并分支2.4 分支示例2.4.1 当前除了主分支&#xff0c;再次创建了两个分支2.4.2 先合并test1分支2.4.3 合并testbranch分支 2.5 解决合并…

智慧工地一体化解决方案(里程碑管理)源码

智慧工地为管理人员提供及时、高效、优质的远程管理服务&#xff0c;提升安全管理水平&#xff0c;确保施工安全提高施工质量。实现对人、机、料、法、环的全方位实时监控&#xff0c;变被动“监督”为主动“监控”。 一、建设背景 施工现场有数量多、分布广&#xff0c;总部统…

C++11 左值 右值

什么是左值&#xff1f;什么是左值引用&#xff1f; 左值是一个表示数据的表达式(如变量名或解引用的指针)&#xff0c;我们可以获取它的地址可以对它赋 值&#xff0c;左值可以出现赋值符号的左边&#xff0c;右值不能出现在赋值符号左边。定义时const修饰符后的左 值&am…

亚马逊云科技re:Invent大会,助力安全构建规模化生成式AI应用

2023亚马逊云科技re:Invent全球大会进入第三天&#xff0c;亚马逊云科技数据和人工智能副总裁Swami Sivasubramanian博士在周三的主题演讲中&#xff0c;为大家带来了关于亚马逊云科技生成式AI的最新能力、面向生成式AI时代的数据战略以及借助生成式AI应用提高生产效率的精彩分…

变量和引用

变量和引用 2.1.深入认识变量 2.1.1.什么是变量 变量是在程序中保存用户数据的一段内存存储空间&#xff0c;变量名是内存空间的首地址 变量三要素&#xff1a;名称、类型、值 2.1.2.变量的名称 组成: 字母、数字、下划线组成&#xff0c;不能以数字开头 变量名称的长…

Android 获取应用签名

Android 获取应用签名 本文主要讲下在android中如何获取应用签名. 也方便平时用来区分一个应用是不是原包应用. 1: 通过PackageManager获取签名信息 首先,通过packageManager获取到指定应用的PackageInfo. 这里需要传入的flag是PackageManager.GET_SIGNATURES /*** {link P…

scrapyd及gerapy的使用及docker-compse部署

一、scrapyd的介绍 scrapyd是一个用于部署和运行scrapy爬虫的程序&#xff0c;它允许你通过JSON API(也即是web api)来部署爬虫项目和控制爬虫运行&#xff0c;scrapyd是一个守护进程&#xff0c;监听爬虫的运行和请求&#xff0c;然后启动进程来执行它们 scrapyd的安装 scr…

蓝桥第一期模拟总结

文章目录 1.动态的 Tab 栏2.地球漫游3.迷惑的this4.燃烧卡路里5.魔法失灵了6.年龄统计 所有题目链接 1.动态的 Tab 栏 本题要实现一个tab栏固定效果&#xff0c;看见题目就想到css中的 position: fixed; 尝试了很久都没能实现效果&#xff0c;后来又想到了粘性定位 position: …

网络编程之套接字

端口 && IP 在学习套接字编程之前&#xff0c;我们必须了解一下前缀知识。首先是IP和端口的作用。 在这之前&#xff0c;我们要明白一件事。那就是把数据从一台主机发送到另一台主机&#xff0c;是目的吗&#xff1f;&#xff1f;&#xff1f;当然不是&#xff01;&a…