当前课程知识点:高级语言程序设计 > 结构体和共用体 > 9.1 结构体类型的定义 > 结构体类型的定义
大家好
本讲介绍结构体类型的定义
前面几讲中
学习了一些数据类型
整型
浮点型
字符型
数组类型
指针类型
本讲学习自定义类型
在实际应用中
有不少应用问题
如果只采用上面这几种类型
显得很不方便
先来看这样一个问题
比如说
我们希望编写一个学生信息管理程序
那我们如何表示
一个学生的学号 姓名 性别等信息呢
又如何表示多个学生的信息呢
假如我们有一个班的学生信息如下表
如何将这个表格数据
用计算机存储并实现对这些数据的管理呢
到目前为止
我们唯一学习过的一种数据结构就是数组
所以自然可能会想到用数组来解决这个问题
假如这个表格中有30个学生的信息的话
一个学生的学号用一个整型变量来表示
30个学生的学号呢
就可以定义一个长度为30的整型数组来表示
姓名呢
一个学生的姓名
可以用长度为10的一维字符数组来表示
而现在要表示30个学生的姓名
那么就应该定义一个
30X10的二维字符数组
来存放30个学生的姓名
每个学生的姓名不超过10个字符
一个学生的性别
用一个字符型变量来表示
那30个学生的性别呢
就可以用一个长度为30的字符数组来表示
后面那以此类推
在定义完这些数组之后
要将这个表格中的数据
保存到这个数组元素当中去
就需要对这些数组进行初始化
我们可以在定义这些数组的同时
对这些数组进行初始化
但是用数组来存储这个表
布局是否合适呢
由于数组是具有相同类型的数据的集合
所以在保存这些学生的信息的时候
你不得不将每个学生的
学号 姓名 性别 出生年
学习成绩等等这些信息拆开来
分别用多个不同类型的数组来表示
例如所有的学号放在一个数组里
所有的姓名放在一个数组里
那现在我们要查找第1个学生的信息
在num这个数组里查到学号是403101
在name数组里查到姓名武小帝
在sex数组里查到性别等等
这是用数组保存学生信息的内存管理的示意图
有8个数组
每个数组存放学生的一个属性
这样造成分配的内存不集中
结构是零散的
查找一个学生的信息
要把所有的数组都读一遍
选址效率也不高
此外呢
易发生错位
这种内存分配方式就好比是
将多台机器设备的零部件
拆下来分开入库一样
这个时候如果你想知道哪个零部件
是哪台机器上的
可能会很难
如果每台机器都能够作为一个独立的整体
来单独存储
那就方便多了
那么有没有这样的一种数据类型
支持我们将多个逻辑相关
但类型不同的数据放在一起来存储吗
也就是将一个人的信息存放在一起
能否将不同类型的数据
放在一起定义一种新的数据类型呢
C语言允许用户自定义数据类型
典型的代表就是结构体
结构体类型和数组一样
都属于构造数据类型
它们之间的不同点就在于
数组是将相同类型的数据
放在一起来存储
而结构体允许逻辑上相关
但类型不同的数据放在一起来存储
例如在学生信息管理程序中
学生学号 姓名 性别 年龄
考试成绩等数据
都是与某一学生紧密联系的整体
那么如何来定义一个结构体类型呢
这是定义一个结构体类型的一般形式
这是针对学生数据的结构体类型
定义的具体实例
struct是C语言提供的定义结构体类型的关键字
struct后面的这个标识符student的
就是我们定义的这个结构体类型的名字
花括号里呢
是结构体类型的成员
有一个int型成员num
用来存放学生的学号
有一个长度为10的字符数组name
用来存放学生的姓名
一个字符型成员sex
用来存放学生的性别
year成员用来存放学生出生的年份
还有四个float型成员
用来存放学生的四门课的成绩
分号不能掉
表示结构体类型定义的结束
后面的四个结构体成员
我们还可以用这样的一个结构体成员来代替
就是将这四个float变量
改用一个float型数组来表示
用这种方式更具有扩展性
因为如果不是四门课的成绩
而是十门课的成绩
只要我们修改一下这个数组的长度就可以了
既可以用数组作为结构体成员的类型
也可以用结构体类型作为其成员的类型
这里year只能记录学生出生的年份
如果我们希望记录每个学生出生日期
精确到月到日
那么我们可以定义一个日期类型
日期类型有年月日三个int型成员
将学生类型的year成员的类型
修改成日期类型
将成员year的名字改为birthday
一个结构体也可以嵌套在另一个结构体内部
像这种在一个结构体内
又包含了另外一个结构体
作为其成员的结构体
我们称它为嵌套的结构体
当然这个日期类型
我们也可以这样来定义
就是将月份用一个字符型数组来表示
像 int float char 等
是由C语言本身提供的数据类型
不能再进行分拆
我们称之为基本数据类型
而结构体可以包含多个基本类型的数据
也可以包含其他的结构体
我们将它称为复杂数据类型
或构造数据类型
我们可以根据实际问题的需要来定义类型
把一组逻辑上相关的信息
作为一个整体来处理
我们知道在Visual C++中
int型占4个字节
char型占1个字节
像这样基本类型占几个字节我们都知道
现在我们自己定义的类型占几个字节呢
也就是如何计算结构体类型
占用内存的字节数呢
结构体中的成员
可以是不同的数据类型
成员按照定义时的顺序
依次存储在连续的内存空间
而数组的大小
是所有数组元素大小简单的相加
那结构体是不是和数组一样
结构体类型占内存的字节数
是不是它的所有成员
占内存字节数的总和呢
这是struct datatype1类型的定义
那么这个类型所占内存大小是1+4+1=6吗
计算类型占内存的字节数
不能想当然
而是应该用sizeof来获取
也就是用sizeof来获得一个类型
或变量所占内存的字节数
请看这个示例程序
定义了三个结构体类型
这是类型datatype1的三个成员
成员a是整型
成员b和c都是字符型
类型datatype2和类型datatype3
也各有三个成员
用sizeof来计算这三个类型所占字节数
编写这样的一个程序来计算
来看运行结果
三个类型占用内存的字节数都不相同
struct datatype1
和struct datatype3的成员类型相同
只是定义的顺序不同
struct datatype1 占12个字节
而struct datatype3占8个字节
再来看struct datatype2占6个字节数
根据运行结果
可以发现
结构体的大小不是所有成员大小简单的相加
那么是为什么呢
系统在存储结构体变量时
有一个地址对齐问题
结构体内存地址对齐是指
元素是按照定义顺序
一个一个放到内存中去的
但并不是紧密排列的
从结构体存储的首地址开始
每个元素放置在内存中时
它都会认为内存
是按照自己的大小来划分的
因此元素放置的位置
一定会在自己宽度的整数倍上开始
例如struct datatype1类型
为了与第二个基本整型的成员变量a对齐
第一个成员b和最后一个成员c
就需要在其后面添加三个字节的补位
这样就会使得这个结构体类型
实际所占内存的字节数是12
struct datatype2类型
跟struct datatype1相比
这个结构体类型的第二个成员的数据类型
是短整型
只占2个字节
那么这个结构体类型在内存中
占据的字节数就变成6个字节了
这是因为为了与第二个short型成员
变量a的内存地址对齐
第一个成员和最后一个成员的后面
只需要添加一个字节的补位
就可以实现与第二个成员的内存地址对齐了
因此这个结构体类型
在内存中占据的是6个字节的存储单元
再来看struct datatype3类型
跟struct datatype1相比
第一个成员和第二个成员都是char型
第三个成员是int型
第一个成员和第二个成员连续存放
并且在后面添加两个字节的补位
就可以与第三个int型的成员变量a
进行内存地址对齐了
因此struct datatype3这个结构体类型
在内存中占的字节数是8个字节
所以结构体类型在内存中
所占的字节数
不仅与结构体成员的数量及类型有关的
并且还与结构体成员的定义顺序相关
实际上
所有数据类型在内存中
都是从偶数地址开始存放的
且结构所占的实际空间
一般是按照机器字长对齐的
不同的编译器 平台
内存对齐方式会有变化
一个结构体在内存中的存储格式
也是与机器有关的
在使用结构体类型的时候
我们不需要关注这个细节
我们编程者无需了解
计算机内部的这种存放形式
能使用sizeof()运算符
计算结构体类型所占内存的字节数
就可以了
c语言提供了一种方法
就是用typedef
给已有的类型
定义一个新的名字
或者称为别名
例如我们可以定义给float类型
定义一个新的名字REAL
注意没有定义一种新的数据类型
而是给float类型取了一个别名
别名通常用全部大写字母的英文单词来表示
这两条语句功能相同
都是定义了两个float型变量
注意要强调的是
typedef 没有创造新数据类型
typedef 只是定义类型的别名
既不是定义新类型
也不是定义变量
typedef 还可以定义数组类型的别名
请看这是给一个长度为100的int型数组
取了一个别名ARRAY
第1条语句
是我们以前定义数组时经常用的格式
在定义了别名以后
我们定义长度为100的整型数组时
可以按第3条语句的格式写
右边呢
这是给指向char的指针类型
定义了一个别名STRING
第1条语句是我们以前定义指针变量p
和指针数组s时经常用的格式
在定义了别名以后
可以按第3条语句格式定义
指针变量p和指针数组s
再来看
typedef 还可以给结构体类型取别名
有两种方式
一是分两步
先定义结构体类型
再给这个类型取别名
第二种方式呢
可以在定义结构体类型的同时取别名
大写DATE与struct date是同义词
这两条语句是等价的
小结一下
本讲学习了结构体类型的定义
结构体中的成员可以是不同的数据类型
成员按照定义时的顺序
依次存储在连续的内存空间
结构体类型和基本类型的功能是一致的
只是告诉编译器该如何表示数据
但是它没有让计算机为它分配空间
如果我们要使用结构体变量存储数据
那么就需要先定义变量 后使用
好了 这就是我们这一讲的内容
谢谢大家
-1.1 计算机程序和计算机语言
-1.2 C程序的构成
--C程序的构成
-1.3 C语言编辑、编译、运行(VC++2010学习版)
-练习题
-2.1 变量的定义和使用
--变量的定义和使用
-2.2 数据类型
--数据类型简介
--整型数据
--实型数据
-2.3 格式输出和格式输入
-2.4 运算符和表达式
--运算符和表达式
-2.5 结构化程序设计入门
-练习题
-3.1 为什么要用选择结构解决问题
-3.2 如何正确表示选择结构中的条件
-3.3 用if语句及if语句嵌套实现选择结构
-3.4 用switch语句实现选择结构
-练习
-4.1 循环的引入
-4.1 循环的引入--作业
-4.2 用while语句实现循环
-4.3 用do-while语句实现循环
-4.4 用for语句实现循环
-- 用for语句实现循环
-4.4 用for语句实现循环--作业
-4.5 何时需要用循环的嵌套
-4.6 如何提前终止循环和提前结束本次循环
-习题--作业
-5.1 数组类型的引入
--数组类型的引入
-5.2 一维数组的定义和初始化
-5.3 一维数组的使用
--一维数组的使用
-5.4 二维数组的定义和初始化
-5.5 二维数组的使用
--二维数组的使用
-习题--作业
-6.1 什么是指针
--什么是指针
-6.2 指针类型及相关概念
-6.3 指针变量的定义、赋值与使用
-6.4 指针的运算
--指针的运算
-6.5 指针与一维数组
--指针与一维数组
-6.6 指针的指针
--指针的指针
-6.7 指针与二维数组
--指针与二维数组
-习题--作业
-7.1 为什么要自定义函数
-7.2 函数的定义
--函数的定义
-7.3 函数的调用
--函数的调用
-7.4 函数参数的传递
--函数参数的传递
-7.5 函数的嵌套调用和递归调用
-7.6 变量的作用域和存储类型
-习题--作业
-8.1 字符串的存储及输入输出
-8.2 系统字符串处理函数
-8.3 自定义字符串处理函数
-习题--作业
-9.1 结构体类型的定义
--结构体类型的定义
-9.2 结构体变量及指针变量的定义及使用
-9.3 结构体变量和指针变量作函数的参数
-9.4 结构体数组的定义和使用
-9.5 共用体类型和枚举类型
--共用体和枚举类型
-习题--作业
-动态内存分配--习题
-习题--作业