C语言进阶指南(16)(自定义数据类型——结构体)

欢迎来到博主的专栏——C语言进阶指南
博主id:reverie.ly

文章目录

    • 结构体类型
      • 结构体类型的声明
      • 结构体变量的声明
    • 结构体变量的初始化
    • 结构体变量
      • 结构体变量的赋值
      • 结构体变量的成员
      • 结构体变量的使用
      • 结构体变量的内存存储

前面使用的变量都是简单类型的变量,以及这些相同类型变量的集合——数组。
但是在日常生活中,很多物体都是复杂类型变量的集合体,只使用单一类型的话是很难概括出这个物体的。

结构体类型

以书本为例,一个书本通常具有以下要素:(1)、价格(2)、书名(3)、作者(4)、页数
以生活经验来看,这些要素要用C语言的类型来描述的话,价格是一个float类型变量,书名和作者是一串字符串,页数是一个int类型的变量。

由此可见,书本是由一个int类型的变量,两个字符串和一个float类型的变量组合在一起的集合,我们能从C语言规定的类型中找到描述书本的声明吗?

显然是不能的,因此为了解决这个问题,C语言推出了三种自定义类型的方式来帮助程序员实现复杂类型变量的声明。分别是结构体,枚举和联合体

结构体类型的声明

声明一个结构体类型需要用到结构体关键字struct
struct tag
{
struct number;
}struct variable;
比如创建一个用于声明书本的结构体。

struct book
{float price;char bookname[20];char writer[20];int page;
};

这个结构体类型名是book
price,bookname,writer,page是book类型的结构体的结构体成员。这些结构体成员构成一个不同类型的变量的集合。这个集合就是结构体变量

结构体变量的声明

结构体变量的声明和一般变量的声明规则是一致的,既然int i;是声明一个int类型的i。那么我要声明一个book类型的结构体变量,就需要用结构体声明来声明结构体。

struct book
{float price;char bookname[20];char writer[20];int page;
};
struct book book1;

除此之外,变量还可以声明在结构体声明创建的后面

struct book
{float price;char bookname[20];char writer[20];int page;
}book1,book2;

结构体变量的初始化

完成变量的声明之后,就可以给结构体的变量进行初始化了,结构体变量的初始化与数组的初始化类似,以上述的book1变量为例

	struct book book1 = { 22.5,"myreverie","LY",199 };

结构体变量的内容需要按照成员顺序来进行初始化的。以book类型的结构体的成员顺序为例

	float price;char bookname[20];char writer[20];int page;

那么变量初始化的内容就要按照这个顺序依次给成员进行赋值。比如book1中的初始化就是先将price赋值成22.5,bookname初始化成“myreverie”,writer初始化成“LY”,page初始化成199

没有被初始化的部分被初始化成0

	struct book book2 = { 33.5,"myreverie","LY"};

这里book2只被初始化了price,bookname和writer.而page是未被初始化的成员,因此page被初始化成0。

结构体变量可以指定成员进行初始化

struct book book3 = { .page = 199,.writer = "hello,world",
.price = 258.5,.name = "brother" };

按照这种方式进行初始化时可以忽略成员的顺序,未被初始化的成员默认是0.

结构体变量

结构体变量会在内存中按照成员的顺序以及类型为各个成员开辟一个空间
在这里插入图片描述
所以为结构体变量赋值不能以一整个结构体变量的方式进行赋值。正确的赋值方式是采用访问结构体内部成员的方式进行赋值。

访问结构体成员的符号有两个,一个是 ‘.’ (点),一个是“->”(一个减号‘-’加上一个大于‘>’组成的箭头符号)。这两个符号虽然都起到访问结构体成员的作用,但是使用的场景却不一样

'.'用于结构体类型的变量

struct book book3 = { .page = 199,.writer = "hello,world",
book3.name

而‘->’用于结构体指针类型的变量

struct book* pbook = &book3;
pbook->name;

结构体变量的赋值

当成功访问了结构体变量的成员之后,就能对这个变量进行赋值了,赋值的表达式需要符合这个结构体变量的成员的类型
以book类型的结构体为例

struct book book4 = { 0 };
book4.page=199;
book4.price = 256.5;
book4.name = "myreverie";//err
strcpy(&(book4.name), "myreverie");//right

book4.name是一个字符数组,而数组是不能对整体进行赋值的,因此想要给book4.name赋值一个字符串,需要用到字符串函数了。
或者对book4.name这个字符数组里的元素进行单个赋值

	book4.name[2] = 'w';

如果是对结构体指针变量里的成员进行赋值则要用(->)对成员先进行访问,再对访问后的结构体变量的成员进行赋值。

struct book book4 = { 0 };
struct book *book5 =&book4;
book5->name[4] = 'w';
book5->page = 1024;
book5->price = 3.14;
return 0;

C语言可以让两个相同类型结构体变量进行赋值计算。

struct book book6 = { 74.4,"myreverie","LY",114514 };
struct book book7;
book7 = book6;

此时结构体变量book7的成员与结构体变量book6的成员的值是一致的。
在这里插入图片描述

结构体变量的成员

结构体变量的成员被访问后,就变成了和成员一样类型的变量了。比如

book5.name;
book5.page;

book5.name是一个char类型的数组,那么这个成员被使用时就应该被当成一个char[20]类型的数组使用。

	scanf("%c", &book5.name[4]);printf("%c", book5.name[4]);printf("%s", book5.name);

以此类推,book5.page的使用也被当成int类型的变量来使用

	printf("%d", book5.page);scanf("%d", &book5.page);

包括作为函数的实参以及算术计算时,都会被当做声明时的成员的类型来使用。

结构体变量的使用

以book这个结构体类型为例,我们来写一个记录书本数据的程序。
首先考虑一个书本都有哪些数据,这些数据又是什么类型的。首先是价格,书名和作者名,以及页数。再考虑这些数据分别是什么类型的:价格是小数,float类型,书名和作者名是字符串,页数是整数,用int。那么创建出一个书本的结构体

struct book 
{float price;char name[20];char writer[20];int page;
};

接着就是对这个程序的实现了,这个程序的作用是将一本书的数据记录下来,那么就是将这个书的内容存进变量中,因此需要用到输入数据的程序。记录在书中的数据需要将其展示出来,这就需要用到输出数据的程序,因此我们将程序分为两部分,一个是用作输入的程序,一个是用作输出的程序。而这个程序不止用到一个书籍,所以需要将这个书籍的数据存放在一个结构体数组中

struct book booklib[10];

创建一个头文件library.h,用来声明函数原型

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#define book_max 10
int book_num;
struct book
{float price;char name[20];char writer[20];int page;
}booklib[book_max];
void menu(void);
void read_date(void);
void delite_date(void);
void print_date(void);

创建一个源文件library.c用来定义函数

#include"library.h"
extern book_num;
static void initname(char* name)
{for (int i = 0; i < 20; i++){name[i] = 0;}return;
}
void menu(void)
{printf("1.   增加数据    2.删除数据    3.打印数据     0退出\n");printf("请选择你要进行的操作:");
}
void read_date(void)
{if (book_num == book_max){printf("数据已满\n");return;}printf("请输入书名:");scanf("%s", booklib[book_num].name);printf("请输入作者名:");scanf("%s", booklib[book_num].writer);printf("请输入价格:");scanf("%g", &booklib[book_num].price);printf("请输入页数:");scanf("%d", &booklib[book_num].page);book_num++;
}
void delite_date(void)
{int No = -1;printf("请选择要删除的序号");scanf("%d", &No);if (No >= 0 && No < book_num){initname(booklib[No].name);initname(booklib[No].writer);booklib[No].page = 0;booklib[No].price = 0.0;for (int i = No; i < book_num; i++){if (No == book_num - 1)break;booklib[i] = booklib[i + 1];}book_num--;}else{printf("你选择的书籍不存在\n");}return;
}
void print_date(void)
{if (book_num == 0){printf("数据还未输入\n");return;}int i = 0;for (i = 0; i < book_num; i++){printf("%d\n", i);printf("%s\n", booklib[i].name);printf("%s\n", booklib[i].writer);printf("%f\n", booklib[i].price);printf("%d\n", booklib[i].page);printf("\n");}return ;
}

再创建一个源文件main.c,用来输出主菜单

#include"library.h"
int main()
{int input = 1;void (*pf[4])(void) = {NULL,read_date,delite_date,print_date};while (input){menu();scanf("%d", &input);if(input)pf[input]();}return 0;
}

运行结结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结构体变量的内存存储

结构体类型的内存存储是具有以下对其规则的

结构体的内存对齐规则如下:
1)第一个成员在与结构体变量偏移量为0的地址处。
2)每个成员变量都需要对齐到对其数的整数倍偏移量的地址处去。
对齐数是每个变量类型的大小与编译器默认对齐数的较小值。 vc中的默认对齐数位8,gcc没有设置默认对齐数,因此对齐数位该成员的大小。
3)结构体的总大小为最大对齐数的整数倍。
4)如果嵌套了一个结构体成员变量,那么嵌套的结构体的偏移量为该结构体中最大的对齐数的倍数。该结构体的大小为该结构体中最大的对齐数的整数倍。且符合上述规

结构体的内存计算。如
在这里插入图片描述

对齐数设置的原因为:
1)平台方面:某些硬件不支持访问任意地址上的数据,因此设置对齐数来圈定访问的空间。
2)数据结构应该尽可能的对齐。原因在于当访问未对齐的数据时,处理器需要进行二次访问才能得到数据。而对其的数据只需要访问1次。
因此对齐数的设定是牺牲内存空间来换取读取的便利性。

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

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

相关文章

浅学指针(3)

系列文章目录 文章目录 系列文章目录前言系列文章目录前言1. 字符指针变量2. 数组指针变量那数组指针变量应该是&#xff1a;存放的应该是数组的地址&#xff0c;能够指向数组的指针变量。2.2 数组指针变量怎么初始化总结&#xff1a;函数名就是地址&#xff0c;&函数名和直…

Debian arm系统安装wxPython

一、系统版本 二、安装wxPython-4.0.4.tar.gz 1、下载依赖 >sudo apt update >sudo apt-get install build-essential libgtk-3-dev libwebkit2gtk-4.0-dev libssl-dev libcurl4-openssl-dev libgstreamer-plugins-base1.0-dev libnotify-dev freeglut3 freeglut3-dev …

openGauss学习笔记-134 openGauss 数据库运维-例行维护-检查操作系统参数

文章目录 openGauss学习笔记-134 openGauss 数据库运维-例行维护-检查操作系统参数134.1 检查办法134.2 异常处理 openGauss学习笔记-134 openGauss 数据库运维-例行维护-检查操作系统参数 134.1 检查办法 通过openGauss提供的gs_checkos工具可以完成操作系统状态检查。 前提…

C语言——实现一个计算m~n(m<n)之间所有整数的和的简单函数。

#include <stdio.h>int sum(int m, int n) {int i;int sum 0;for ( i m; i <n; i){sum i;}return sum;}int main() { int m, n;printf("输入m和n&#xff1a;\n");scanf("%d,%d", &m, &n);printf("sum %d\n", sum(m, n)…

Windows 11的新功能不适用于所有人,但对将要使用的人来说非常酷

正如一个新的预览版本所示&#xff0c;Windows 11即将为那些使用手写笔的人添加一些智能功能&#xff0c;以及其他改进。 这是预览版22635.2776&#xff08;也称为KB5032292&#xff09;&#xff0c;已推出Beta频道&#xff0c;这是发布预览版之前的最后一个测试方法&#xff…

一文概括AxureRP的优缺点和替代软件

AxureRP是目前流行的设计精美的用户界面和交互软件。AxureRP根据其应用领域提供了一组丰富的UI控制。 Axure是什么软件&#xff1f; Axure是目前流行的设计精美的用户界面和交互软件。Axure已经存在了近十年&#xff0c;让UX设计师轻松了解创建软件原型的细节。作为一种原型设…

zabbix 6.0 原理与部署

一、zabbix简介&#xff1a; zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。 zabbix 能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff1b;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 zabbi…

最佳软件配置管理工具(16款SCM工具)

配置管理&#xff08;CM&#xff09;是一种系统工程方法&#xff0c;用于在产品的整个生命周期内建立和维持产品的性能&#xff0c;功能和物理属性与其设计&#xff0c;要求和操作信息的一致性。 它们为您的组织带来了成本效益和更好的时间管理。 当今市场充斥着各种配置管理工…

天鹅湖国家旅游度假区 | 展柜OLED透明屏:创新展示提升互动体验

天鹅湖国家旅游度假区 | 展柜OLED透明屏 产品&#xff1a;一块55寸OLED透明屏嵌入玻璃安装 应用场景&#xff1a;用在天鹅湖国家旅游度假区——三门峡城市文化客厅展馆中的一个透明展示柜&#xff0c;用一块55寸OLED透明屏嵌入展示柜的玻璃&#xff0c;让观众即可以看到展柜里…

自定义注解的定义及使用场景

文章目录 1. 自定义注解如何使用2. 自定义注解使用场景2.1 自定义注解使用AOP做权限校验2.2 自定义注解使用AOP记录用户操作日志2.3 自定义注解使用AOP记录接口请求时长 1. 自定义注解如何使用 需要使用interface修饰&#xff0c;加上三个元注解 Documented&#xff1a;生成API…

【小聆送书第一期】让架构师的成神之路温暖你这个不景气的冬天

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言 书籍一览 ⛳️书籍一⛳️书籍二⛳️书籍三⛳️书籍四⛳️书籍五⛳️书籍六⛳️书…

C++中的类型转换和异常

C类型转换 类型转换(cast) 是将一种数据类型转换成另一种数据类型。例如&#xff0c;如果将一个整型 值赋给一个浮点类型的变量&#xff0c;编译器会暗地里将其转换成浮点类型。 转换是非常有用的&#xff0c;但是它也会带来一些问题&#xff0c;比如在转换指针时&#xff0c…

java使用poi读写excel(处理上下标和科学计数法)

Background 要读写如下图所示的excel&#xff0c;符号和单位中包含上下标&#xff0c;在读写时需要特殊处理&#xff1b;取值列中是科学计数法&#xff0c;读写时需要特殊处理&#xff1b;excel中包含多个sheet&#xff0c;读的时候把所有sheet的数据读出来&#xff0c;写的时候…

OSCP系列靶场-Esay保姆级

总结 getwebshell : ftp可匿名登录 → 发现隐藏文件夹 → 发现ssh密钥 → 猜解ssh用户名 → ssh密钥登录 提 权 思 路 : 发现suid权限文件 → cpulimit提权 准备工作 启动VPN 获取攻击机IP → 192.168.45.191 启动靶机 获取目标机器IP → 192.168.179.130 信息收集-端口扫…

虚拟机指定开放数据库3306端口

1、查看当前防火墙状态&#xff1a; sudo firewall-cmd --state 2、开放指定端口 sudo firewall-cmd --zonepublic --add-port3306/tcp --permanent 3、重新加载防火墙配置 sudo firewall-cmd --reload 4、检查端口是否开放成功 sudo firewall-cmd --zonepublic --list-por…

行情分析——加密货币市场大盘走势(11.29)

大饼已经形成了底背离&#xff0c;即MACD往下走&#xff0c;而价格还在往上走&#xff0c;这种后续往往会大跌。继续把空单拿好&#xff0c;已经持仓的无需加仓。多次上涨却一直不能突破&#xff0c;说明多空和空军力量都很强&#xff0c;等待后续出方向。在笔者看来&#xff0…

HotSpot 虚拟机中的对象

1、对象的创建 Java 是一门面向对象的编程语言&#xff0c;程序运行过程中无时无刻都有对象被创建出来。在语言层面上&#xff0c;创建对象通常仅仅是一个 new 关键字&#xff0c;而虚拟机中&#xff0c;对象&#xff08;仅限于普通 Java 对象&#xff0c;不包括数组和 Class …

统计元音字母c语言

以下是一个简单的C语言程序&#xff0c;用于统计一段文本中的元音字母数量&#xff1a; #include <stdio.h>#include <string.h>int main() { char str[1000]; int vowels 0; printf("请输入一段文本&#xff1a;\n"); fgets(str, siz…

关于神经网络,你不得不知的三大要点

什么是神经网络&#xff1f; 神经网络是一个具有相连节点层的计算模型&#xff0c;其分层结构与大脑中的神经元网络结构相似。神经网络可通过数据进行学习&#xff0c;因此&#xff0c;可训练其识别模式、对数据分类和预测未来事件。 神经网络将您的输入细分为多个抽象层。比…

JavaScript编程进阶 – Return语句

JavaScript编程进阶 – Return语句 JavaScript Programming Advanced – Return Statement By JacksonML 就像人们习惯的函数一样&#xff0c;总觉得在函数体最后需要一个return语句&#xff0c;标志着函数的结束,就像下面这个函数 theFunc() 那样。 function theFunc() { re…