在冯-诺依曼体系结构中,程序代码和数据都是以二进制存储的,因此对计算机系统和硬件本身而言,数据类型的概念其实是不存在的。
在高级语言中,为了有效的组织数据,规范数据的使用,提高程序的可读性,方便用户使用,引入了整型、实型等基本数据类型。
仅使用几种基本数据类型显然是不够的,所以,根本方法就是允许用户自定义数据类型(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文章来源:https://www.toymoban.com/news/detail-514265.html
总之,系统位结构体变量分配内存的大小,或者说结构体类型所占内存的字节数,并非是所有成员所占内存字节数的总和,它不仅与所定义的结构体类型有关,和与计算机系统本身有关。由于结构体变量的成员的内存对齐方式和数据类型所占内存的大小都是与机器相关的,因此结构体在内存中所占的字节数也是与机器相关的。文章来源地址https://www.toymoban.com/news/detail-514265.html
到了这里,关于结构体和数据结构--从基本数据类型到抽象数据类型、结构体的定义的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!