结构体和数据结构--从基本数据类型到抽象数据类型、结构体的定义

news2024/9/23 5:28:08

        在冯-诺依曼体系结构中,程序代码和数据都是以二进制存储的,因此对计算机系统和硬件本身而言,数据类型的概念其实是不存在的。

        在高级语言中,为了有效的组织数据,规范数据的使用,提高程序的可读性,方便用户使用,引入了整型、实型等基本数据类型。

        仅使用几种基本数据类型显然是不够的,所以,根本方法就是允许用户自定义数据类型(User-Defined DataType)。于时,后来的高级语言在发展中就出现了,构造数据类型(也称为复合数据类型)

        抽象数据类型(Abstract Data Type,ADT)是指这样一种数据类型,他不再单纯是一组值的集合,还包括作用在值集上的操作的集合,即在构造数据类型的基础上增加了对数据的操作,且类型的表示细节及操作的实现细节对外是不可见的。C++中的类(Class)是抽象数据类型的一种具体实现,也是面向对象(Object-Oriented)程序设计语言中的一个重要概念。

目录

一、为什么要定义结构体

二、结构体变量的定义

三、用typedef定义数据类型

四、结构体变量的初始化

五、嵌套的结构体

六、结构体变量的引用

七、结构体所占用的字节数


一、为什么要定义结构体类型

        数组是由相同类型的数据构成的一种数据结构,适合于对具有相同属性的数据进行批处理。

        结构体(Structure)是将不同类型的数据成员组织到统一的名字之下,适合于对关系紧密,逻辑相关、具有相同或者不同属性的数据进行处理,尤其在数据库管理中得到了广泛的应用;

        共用体(Union)虽然也能表示逻辑相关的不同类型的数据集合,但其数据成员是情形相互排斥的,每一时刻只有一种数据成员起作用。

二、结构体变量的定义

        定义结构体的第一步是声明一个结构体模板(Structure Template) ,其格式如下:

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

        结构体模板是由关键字struct及其后的结构体名组成的。分号(;)是结构体声明的结束标志,不能省略。结构体的名字,称为结构体标签(Structure Tag),作为用户自定义的结构体类型的标志,用于与其他结构体类型相区别。结构体中的各信息项是在结构体标签后面的花括号{和}内声明的。构成结构体的变量,称为结构体成员(Structure Member)。每个结构体成员都有一个名字和相应的数据类型。结构体成员的命名必须遵从变量的命名规则。

        声明结构体模板的主要目的是利用已有的数据类型定义一个新的数据类型。例如

struct student
{
    long studentID;
    char studentName[10];
    char studentSex;
    int  yearOfBirth;
    int scoreMath;
    int scoreEnglish;
    int scoreComputer;
    int scoreProgramming;
};

 注意:结构体模板只是声明了一种数据类型,定义了数据的组织形式,并未声明结构体数据类型的变量,因而编译器不为其分配内存,正如编译器不为int型分配内存一样。

        定义结构体的第二部是利用已经定义好的结构体数据类型来定义结构体变量。C语言允许按如下两种方式来定义结构体变量。

(1)先声明结构体模板,在定义结构体变量。

例如,下面语句定义一个具有struct student类型的结构体变量stu1:

struct student stu1;

(2)在声明结构体模板的同时定义结构体变量。

例如,下面语句在声明结构体类型的同时定义了struct student类型的结构体变量stu1.

struct student
{
    long studentID;
    char studentName[10];
    char studentSex;
    int  yearOfBirth;
    int  score[4];
}stu1;

注意:选择第二种方法时,可以不出现结构体名,但该方法未指定结构体标签,不能在程序的其他处定义结构体变量。因此并不常见。

三、用tyepdef定义数据类型

        关键字typedef用于为系统固有的或程序员自定义的数据类型定义一个别名。数据类型的别名通常使用大写字母,目的是为了与已有的数据类型相区分。

例如语句:typedef  int   integer;

为int 定义了一个性的名字integer,也就是说integer与int同意。

        当然,也可以为一个结构体定义一个别名。例如:

typedef struct student STUDENT;

typedef struct student
{
    long studentID;
    char studentName[10];
    char studentSex;
    int  yearOfBirth;
    int  score[4];
}STUDENT;

 是等价的。二者都是为struct student 结构体类型定义了一个新的名字STUDENT,即STUDENT与struct student 是同义词。因此下列两题哦啊语句是等价的:

STUDENT stu1,stu2;

struct student stu1,stu2;

四、结构体变量的初始化

        结构体变量的成员可以通过将成员的初值置于花括号之内来进行初始化。例如:

STUDENT stu1={100310121,"王刚",'M',1991,{72,83,90,82}};

等价于

struct student stu1 = {100310121,"王刚",'M',1991,{72,83,90,82}};

 可以再命名一个:

struct student stu2 = {…………};

两个变量就都具有STUDENT(即struct student)类型的结构体,相当于用结构体模板生成了两个独立的与结构体类型结构一致的变量。

五、嵌套的结构体

        嵌套的结构体(Nested Structure)就是在一个结构体内包含了另一个结构体作为其成员。

例如,需要将先前的出生年修改为包含更具体地年、月、日信息地日期,则需要先定义一个具有年、月、日成员地结构体类型,即先声明一个日期结构体模板:

typedef struct date

{

        int year;

        int month;

        int day;

}DATE;

然后根据这个DATE结构体模板来声明STUDENT结构体模板。

typedef struct student

{

        long studentID;

        char studentName[10];

        char studentSex;

        DATA birthday;

        int   score[4];

} STUDENT;

这里,在结构体地定义中出现了“嵌套”,因为STUDENT结构体内包含了另一个DATE结构体类型的变量birthday作为其成员,因此它是一个嵌套的结构体。

六、结构体变量的引用

        在定义了一个结构体变量以后,该如何引用他呢?

        C语言规定,不能将一个结构体变量作为一个整体进行输入、输出操作,只能对每个具体的成员进行输入、输出操作。

        访问结构体变量的成员必须使用成员选择符(也称为圆点运算符)。其访问格式如下:

stu1.studentID = 100310121;

对于结构体成员,可以像其他普通变量一样进行赋值等运算。

        当出现结构体嵌套时,必须以级联方式访问结构体成员,即通过成员选择运算符逐级找到最顶层的成员时在引用。例如,生日:

stu1.birthday.year = 1991;

stu1.birthday.mouth = 5; 

stu1.birthday.day  = 19;

例题:下列程序用于演示结构体变量的赋值和引用法。

#include <stdio.h>

typedef struct date
{
	int year;
	int month;
	int day;
 }DATE;
 
typedef struct student
{
	long studentID;
	char studentName[10];
	char studentSex;
	DATE birthday;
	int  score[4];
}STUDENT;

int main()
{
	STUDENT stu1= {100310121,"王刚",'M',{1991,5,19},{72,83,90,82}};
	STUDENT stu2;
	stu2 = stu1;
	printf("stu2:%10ld%8s%3c%6d/%02d/%02d/%4d%4d%4d%4d\n",
 stu2.studentID,stu2.studentName,stu2.studentSex,stu2.birthday.year,
 stu2.birthday.month,stu2.birthday.day,stu2.score[0],stu2.score[1],
stu2.score[2],stu2.score[3]); 
}

 %02d中的0表示输出数据时若左边有多余位则补0,于是输出日期为“1991/05/19”。

C语言允许对具有相同结构体类型的变量进行整体赋值。在对两个同类型的结构体变量进行赋值时,实际上是按结构体的成员顺序逐一对相应成员进行赋值的,赋值后的结果就是两个结构体变量的成员具有相同的内容。

        结构体的声明既可以放在所有函数体的外部,也可以放在函数体内部。在函数体外声明的结构体类型可为所有函数使用,称为全局声明;放在函数体内声明称为局部声明。

例题:要求从键盘输入结构体变量stu1的内容,那么程序的主函数可修改为:

int main()
{
	STUDENT stu1,stu2;
	int i;
	printf("Input a record:\n");
	scanf("%ld",&stu1.studentID);
	scanf("%s",stu1.studentName);
	scanf(" %c",&stu1.studentSex);
	scanf("%d",&stu1.birthday.year);
	scanf("%d",&stu1.birthday.month);
	scanf("%d",&stu1.birthday.day);
	for(i=0;i<4;i++)
	{
		scanf("%d",&stu1.score[i]);
	}
	stu2 = stu1;
	printf("&stu1 = %p\n",&stu1);
	printf("&stu2 = %p\n",&stu2);
	printf("stu2:%10ld%8s%3c%6d/%02d/%02d/%4d%4d%4d%4d\n",
			stu2.studentID,stu2.studentName,stu2.studentSex,stu2.birthday.year,
stu2.birthday.month,stu2.birthday.day,stu2.score[0],stu2.score[1],
stu2.score[2],stu2.score[3]); 	
}

七、结构体所占内存的字节数

        如何计算系统为结构体变量分配的内存的大小,即如何计算结构体类型所占内存的字节数呢?

        能否用结构体的每个成员类型所占的内存字节数的“和”作为一个结构体实际所占的字节数呢?

我们可以先看下述例题:

例题:结构体所占字节数的计算方法。

#include <stdio.h>
typedef struct sample
{
	char m1;
	int  m2;
	char m3;
}SAMPLE;

int main(void)
{
	SAMPLE s = {'a',2,'b'};//C99 可以写成 SAMPLE s = {.m1='a',.m2=2,.m3 = 'b'}; 
	printf("bytes =  %d\n",sizeof(s));
	return 0; 
 } 

int 类型数据占4个字节,char 类型数据占1个字节。

        对于多数计算机系统而言,为了提高内存寻址的效率,很多处理器体系结构为特定的数据类型引入了特殊的内存对齐(Memory-Alignment)需求。不同的系统和编译器,内存对其的方式有所不同,为了满足处理器的对其要求,可能会在较小的成员后加入补位。从而导致结构体实际所占内存的字节数会比我们想象的多出一些字节。

        那编译器是如何处理底层体系结构的对齐限制的呢?32位体系结构中,short型数据要求从偶数地址开始存放,而int型数据则被对齐在4个字节地址边界,这样就保证了一个int型数据总能通过依次内存操作被访问导,每次内存访问是在4个字节对齐的地址读取或存入32位数据。而读取存储在没有对其的地址处的32位整数,则需两次读取操作,从两次读取得到的64位数中提取相关的32位整数还需要额外的操作,这样导致系统性能下降。

        若是想将结构体变量s的第2个成员变成m2的数据类型改成短整型,则程序的输出结构将变为:        bytes = 6

        总之,系统位结构体变量分配内存的大小,或者说结构体类型所占内存的字节数,并非是所有成员所占内存字节数的总和,它不仅与所定义的结构体类型有关,和与计算机系统本身有关。由于结构体变量的成员的内存对齐方式和数据类型所占内存的大小都是与机器相关的,因此结构体在内存中所占的字节数也是与机器相关的。

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

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

相关文章

使用Streamlit和Matplotlib创建交互式折线图

大家好&#xff0c;本文将介绍使用Streamlit和Matplotlib创建一个用户友好的数据可视化Web应用程序。该应用程序允许上传CSV文件&#xff0c;并为任何选定列生成折线图。 构建Streamlit应用程序 在本文中&#xff0c;我们将指导完成创建此应用程序的步骤。无论你是专家还是刚刚…

three.js利用点材质打造星空

最终效果如图&#xff1a; 一、THREE.BufferGeometry介绍 这里只是做个简单的介绍&#xff0c;详细的介绍大家可以看看THREE.BufferGeometry及其属性介绍 THREE.BufferGeometry是Three.js中的一个重要的类&#xff0c;用于管理和操作几何图形数据。它是对THREE.Geometry的一…

leetcode 226. 翻转二叉树

2023.7.1 这题依旧可以用层序遍历的思路来做。 在层序遍历的代码上将所有节点的左右节点进行互换即可实现二叉树的反转。 下面上代码&#xff1a; class Solution { public:TreeNode* invertTree(TreeNode* root) {queue<TreeNode*> que;if(root nullptr) return{};que…

gradio库中的Dropdown模块:创建交互式下拉菜单

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

2020年全国硕士研究生入学统一考试管理类专业学位联考逻辑试题——纯享题目版

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;考取过HCIE Cloud Computing、CCIE Security、CISP等证书。&#x1f433; &#x1f495;兴趣爱好&#xff1a;b站天天刷&#xff0c;题目常常看&a…

编译原理期末复习简记(更新中~)

注意&#xff1a;该复习简记只是针对我校期末该课程复习纲要进行的&#xff0c;仅供参考 第一章 引论 编译程序是什么&#xff1f; 编译程序是一个涉及分析和综合的复杂系统 编译程序组成 编译程序通常由以下内容组成 词法分析器 输入 组成源程序的字符串输出 记号/单词序列语法…

Jenkins+Gitlab+Springboot项目部署Jar和image两种方式

Springboot环境准备 利用spring官网快速创建springboot项目。 添加一个controller package com.example.demo;import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;RestController public class…

新华三眼中的AI天路

ChatGPT的火爆&#xff0c;在全球范围内掀起了新一轮的AI风暴。如今&#xff0c;各行各业都在讨论AI&#xff0c;各个国家都在密集进行新一轮的AI基础设施建设与技术投入。 但眼前的盛景并非突然到来&#xff0c;就拿这一轮大模型热潮来说&#xff0c;谷歌早在2018年底就发布了…

协议速攻 IIC协议详解

介绍 IIC是一种 同步 半双工 串行 总线 同步 指的是同一根时钟线(SCL) 半双工 可以进行双向通信&#xff0c;但是收发不能同时进行&#xff0c;发的时候禁止接收&#xff0c;接的时候禁止发送 串行 数据是一位一位发送的 总线 两根线(SCL SDA)可以接多个IIC类型器件&#…

《统计学习方法》——逻辑斯蒂回归和最大熵模型

参考资料&#xff1a; 《统计学习方法》李航通俗理解信息熵 - 知乎 (zhihu.com)拉格朗日函数为什么要先最大化&#xff1f; - 知乎 (zhihu.com) 1 逻辑斯蒂回归 1.1 逻辑斯蒂回归 输入 x ( x ( 1 ) , x ( 2 ) , ⋯ , x ( n ) , 1 ) T x(x^{(1)},x^{(2)},\cdots,x^{(n)},1…

【动态规划算法练习】day11

文章目录 一、1312. 让字符串成为回文串的最少插入次数1.题目简介2.解题思路3.代码4.运行结果 二、1143. 最长公共子序列1.题目简介2.解题思路3.代码4.运行结果 三、1035. 不相交的线1.题目简介2.解题思路3.代码4.运行结果 总结 一、1312. 让字符串成为回文串的最少插入次数 1…

DevOps系列文章之 设计一个简单的DevOps系统

前置条件 gitlab gitlab-runner k8s docker 1. gitlab创建群组 创建群组的好处是,对项目进行分组,群组内的资源可以共享,这里创建了一个tibos的群组 2. 在群组创建一个项目 这里创建一个空白项目,项目名为Gourd.Test,将项目克隆到本地,然后在该目录下创建一个.net core3.1的w…

Spring Cloud Alibaba Seata源码分析

目录 一、Seata源码分析 1、Seata源码入口 1.1、2.0.0.RELEASE 1.2、2.2.6.RELEASE 2、Seata源码分析-2PC核心源码 3、Seata源码分析-数据源代理 3.1、数据源代理DataSourceProxy 4、Seata源码分析- Seata服务端&#xff08;TC&#xff09;源码 一、Seata源码分析 Sea…

P1dB、IIP3、OIP3、IMD定义及关系

P1dB 1分贝压缩输出功率。放大器有一个线性动态范围&#xff0c;在这个范围内&#xff0c;放大器的输出功率随输入功率线性增加。随着输入功率的继续增加&#xff0c;放大器进入非线性区&#xff0c;其输出功率不再随输入功率的增加而线性增加&#xff0c;也就是说&#xff0c;…

【新星计划·2023】Linux文件权限讲解

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 前言 这篇文章&#xff0c;将带你详细的了解一下 Linux 系统里面有哪些重要的文件&#xff1f;。 不过&#xff0c;每个文件都有相当多的属性…

ROS学习篇之传感器(二)IMU(超核IMU HI266)

文章目录 一.确定IMU型号二.安装驱动1.找到驱动的包2.解压该压缩包3.安装步骤说明4.具体安装5.检查IMU的usb接口是否插到电脑 三.在RVIZ中的显示1.复制示例下的src里的文件复制到自己的src下2.自己的文件目录3.尝试编译一下4.示例的文件说明5.运行Demo6.配置Rviz 四.查看IMU的实…

【深入了解系统性能优化】「实战技术专题」全方面带你透彻探索服务优化技术方案(系统服务调优)

全方面带你透彻探索服务优化技术方案&#xff08;服务器系统性能调优&#xff09; 调优意义计划分析 流程相关分析优化分析Nginx请求服务日志将请求热度最高的接口进行优化异步调用优化方式注意要点 分析调用链路追踪体系建立切面操作分析性能和数据统计存储相关的调用以及耗时…

Pycharm中画图警告:MatplotlibDeprecationWarning

前言&#xff1a; \textcolor{Green}{前言&#xff1a;} 前言&#xff1a; &#x1f49e;这是由于在python中画图出现的问题&#xff0c;一般不会有错。因为它只是个警告&#xff0c;但是我们也可以知道解决这个问题的方法&#xff0c;防止后面出问题的时候知道怎么解决。 前因…

窗口函数 OVER(PARTITION BY ...)

开窗函数的语法结构&#xff1a;分析函数() over(partition by 分组列名 order by 排序列名 rows between 开始位置 and 结束位置) over()函数中包括三个函数&#xff1a;分区partition by 列名、排序order by 列名、指定窗口范围rows between 开始位置 and 结束位置 rows bet…

Win10共享文件|文件夹 电脑之间快速传输文件的方法

一、设置共享的文件或文件夹 1、A电脑上的文件要共享 进行如下设置&#xff1a; 右击要共享的就文件或文件夹->属性->"共享 "选项卡->"共享"按扭->选择共享的用户 下拉选择"Everyone"->单击"添加"按扭->根据需要设…