实验4 构造数据类型分析.docx
《实验4 构造数据类型分析.docx》由会员分享,可在线阅读,更多相关《实验4 构造数据类型分析.docx(25页珍藏版)》请在冰点文库上搜索。
实验4构造数据类型分析
实验4构造数据类型
一、实验目的
1.掌握指针的定义和使用。
2.掌握指针在函数参数传递中的作用。
3.掌握引用的作用、定义及使用。
4.掌握结构体类型变量、结构体类型指针及结构体数组的定义和使用。
5.熟悉联合体的概念与应用,了解联合型、枚举型的定义与使用。
6.使用构造类型数据解决实际应用问题,优化程序设计。
二、知识要点
(一)指针
1.指针和指针变量
所谓指针就是变量、数组、函数等的存储地址。
指针变量就是用于存放指针的变量。
直接按变量地址存取变量值的方式为直接访问方式。
将变量地址存放在另一个变量(即指针变量)中,再通过指针变量来存取变量值的方式为间接访问方式。
指针变量可以存储各类标识符的地址,包括变量、数组、函数、对象,甚至是指针。
同类型的对象取地址的方式却不尽相同:
(1)一般变量的地址,用取地址符“&”加变量名;
(2)数组的地址用数组名表示;
(3)函数的地址用函数名表示。
2.指针变量的定义
C++规定指针变量必须先定义后引用,定义一个指针变量的一般形式为:
<类型名>*<指针变量名>;
3.两个重要的运算符
(1)“&”为取地址运算符
其作用是取出变量的内存地址。
若变量i的起始地址为2000,则&i的运算结果为2000。
注意:
指针变量中只能存放地址(指针),不能存放其他非地址类型的数据。
(2)“*”为指针运算符
其作用是返回指针变量所指向的变量的值。
4.指针变量的运算
(1)算术运算
指针变量的算术运算主要有指针变量的自加、自减、加n和减n操作。
这些算术运算的规则是:
<指针变量>=<指针变量>+/-sizeof(<指针变量类型>)*n
其中,n表示元素的个数,自加、自减操作相当于n=1。
(2)指针变量的关系运算
指针变量的关系运算是对指针变量值的大小比较,如大于(>)、小于(<)、等于(==)和不等于(!
=),它们都能对两个指针变量存储的地址进行比较,如果两个相同类型的指针变量相等,则说明这两个指针变量指向同一个地址。
*******************************************************************************
说明:
本部分在chapter9中讲述
(3)new运算符
用new可以动态的分配内存空间,并将分配内存的地址赋给指针变量,使用new为指针变量动态分配内存空间的语句有3种格式:
格式一:
<指针变量>=new<类型>;
该语句的作用是分配由类型确定大小的一片连续内存空间,并将内存空间的首地址赋给指针变量。
格式二:
<指针变量>=new<类型>(初值);
该语句的作用是除完成分配空间的功能外,还将初值存入所分配的内存空间中。
格式三:
<指针变量>=new<类型>[<常量表达式>];
该语句的作用是分配指定类型的数组空间,并将数组的首地址赋给指针变量。
(4)delete运算符
运算符delete用来将动态分配的内存空间归还给系统,有两种格式:
格式一:
delete<指针变量>;
该语句的作用是将指针变量所指的内存空间归还给系统。
格式二:
delete[]<指针变量>;
该语句的作用是将指针变量所指的一维数组内存空间归还给系统。
*******************************************************************************
5.指针变量作为函数参数
在C++中,有3种类型的指针可以作为函数的参数,它们是:
(1)一般对象(变量)的指针作为函数的参数;
(2)数组的指针(字符串的指针)作为函数的参数;
(3)函数的指针作为函数的参数。
第一种情况是地址参数。
第二种情况是指针作为参数非常多的一种情况,它体现出指针作为参数的优势就是简单、灵活、高效。
第三种情况较复杂,主要是为了设计出一个更通用的函数。
6.指向数组元素的指针
(1)指向一维数组元素的指针
定义一个指向数组元素的指针变量与定义一个指向变量的指针变量相同,但要注意指针变量类型必须与其指向的数组类型相同。
一般格式为:
<数组类型名>*<指针变量名>;
(2)指向二维数组元素的指针
定义指向二维数组元素的指针与定义指向一维数组元素的指针相同。
一般格式为:
<数组类型名>*<指针变量名>;
需要说明的是,在二维数组中有行地址的概念。
C++规定,二维数组a中第i行的地址用a+i或&a[i]表示,并且行地址的值与行首地址的值是相同的,即:
a+i=&a[i]=a[i]=&a[i][0]
但要注意它们的类型不同,前两个是行地址,后两个是数组元素地址。
行地址a+i与&a[i]只能用于指向一维数组的指针变量,而不能用于普通指针变量,例如:
inta[2][3];
int*p=a+0;//错误!
类型不同,应该为int*p=&a[0][0];
(3)指向一维数组的指针
二维数组名可以认为是指向一维数组的常指针,与该指针对应的指针变量定义一般格式为:
<数组类型名>(*<指针变量名>)[第2维数组长度]
第i行第j列元素a[i][j]的地址有下面4种表示方法:
a[i]+j、*(a+i)+j、&a[i][0]+j、&a[i][j]
或
p[i]+j、*(p+i)+j、&p[i][0]+j、&p[i][j]
7.通过指针引用数组元素
(1)通过指向数组元素的指针引用一维数组元素
通过指针引用一维数组元素,一般有以下两种方法:
下标法:
<数组名/指针变量名>[下标]
指针法:
*(数组名/指针变量名+下标)
在C++中,下标法实际上是指针法的一种缩写形式,在编译器编译程序时,首先需要将下标法转化成指针法,也就是说,从编译角度看,指针法引用数组元素要更快一些。
假设指针变量P指向一维数组a,则根据数组元素存储的连续性以及指针的运算规则,可以得出以下结论:
①p+1表示指向数组中的下一个元素;
②若p=&a[0],i是一个整数,那么
●p+i或a+i就是a[i]的地址(i不能超过数组的最大长度);
●*(p+i)或*(a+i)代表数据元素a[i];
●a[i]也可以写成p[i],两者是等价的;
(2)通过指向一维数组的指针引用二维数组元素
指向一维数组的指针基本等同于二维数组名,它们的区别是前者是变量,后者是常量。
C++规定,引用数组a的第i行第j列元素a[i][j]的值有下面的4种表示方法:
*(a[i]+j)、*(*(a+i)+j)、*(&a[i][0]+j)、a[i][j]
或
*(p[i]+j)、*(*(p+i)+j)、*(&p[i][0]+j)、p[i][j]
8.数组名作函数参数
数组名可以用作函数的实参和形参。
数组名代表数组首地址,因此当用数组名作实参调用函数时是把数组的首地址传递给形参,而不是把数组的值传递给形参,即形参接受的是实参传递过来的首地址,这样就使得实参数组与形参数组共同占用同一段内存。
在实际编程中,若想在函数中改变数组元素的值,实参与形参一般有如下几种对应情况:
(1)形参和实参都用数组名,例如:
main()
{inta[10];
…
f(a,10);
…
}
f(intx[],intn)
{
…
}
(2)实参用数组,形参用指针变量,例如:
main()
{inta[10];
…
f(a,10);
…
}
f(int*x,intn)
{
…
}
(3)实参、形参都用指针变量,例如:
main()
{inta[10],*p;
p=a;
…
f(p,10);
…
}
f(int*x,intn)
{
…
}
(4)实参为指针变量,形参为数组名,例如:
main()
{inta[10],*p;
p=a;
…
f(p,10);
…
}
f(intx[],intn)
{
…
}
9.字符串的表示形式(建议应用C++的string类)
在C++程序中,字符数组和字符型指针都可以实现对字符串的存储和运算。
用数组的形式表示一个字符串,例如:
charstring[]=“IloveBeijing”;
还可以用字符指针指向一个字符串,例如:
char*str=“IloveBeijing”;
其中str是一个字符指针变量,而“IloveBeijing”是一个字符串常量,str指向它的首地址。
字符数组与字符指针的区别是:
(1)字符数组由若干个元素组成,每个元素中存放一个字符,而字符指针变量中存放的是地址(字符串的首地址),而不是将字符串放到字符指针变量中。
(2)字符数组在编译的同时即已分配存储单元,有确定的地址,而字符指针在运行时才会存在。
10.字符串指针作函数参数
在函数之间传递一个字符串,可以使用地址传递的方法,即用字符数组作参数或用指向字符串的指针变量作参数。
在被调用的函数中可以改变字符串的内容,主调函数中可以得到改变了的字符串。
11.函数指针变量调用函数
经过编译后,每个函数都被分配给一个入口地址,这个入口地址就称为函数的指针,因此可以通过一个指针变量指向这个入口地址,从而达到通过指针变量调用函数的目的。
指向函数的指针变量的一般定义形式为:
<类型名>(*<指针变量名>)([形参表]);
12.指向函数的指针作函数参数
函数指针变量的作用通常是把指针作为函数传递给其他函数,通过这种方式传递函数的地址,从而实现了一个函数作为另一个函数的参数,目的是设计出更通用、更强大的函数。
13.指针数组的概念
指针数组也是数组,不同的是其数组元素不是一般的数据类型,而是指针,即内存单元的地址。
指针数组中的每一个元素都相当于一个指针变量,而且这些指针必须指向同一种类型的变量。
指针数组的声明方式和普通数组的声明方式类似。
声明一维指针数组的一般形式为:
<数据类型>*数组名[数组长度];
其中,数据类型确定指针数组中每个元素(指针变量)的类型,数组名是指针数组的名称,同时也是这个数组的首地址,数组长度用来确定数组元素的个数。
声明二维指针数组的一般方式为:
<数据类型><*数组名>[第1维数组长度][第2维数组长度];
14.指向指针的指针
指针可以指向任何类型,包括指针类型。
指针也是变量,当然也有地址,那么就可以用另外一个指针来存放某一指针的地址,这个存放某一指针地址的指针就是“指向指针的指针”。
定义指向指针的指针变量的一般形式为:
<数据类型>**<指针变量名>;
对于指向指针的指针来说,我们可以用“*”运算符得到其存储的内容,因为其内容仍然是一个地址,所以可再次应用“*”运算符,取得该地址存储的内容。
(二)引用
引用是某个变量或对象的别名,它的值和地址都与被引用的变量或对象的值和地址相同,对引用的所有操作实际上都是应用在被引用的变量或对象身上的。
当引用的变量变化的时候,原变量的值也会发生变化。
定义引用的一般形式为:
类型标识符&引用名=目标变量名;
引用的目的主要是作为函数的形参,在函数参数传递中解决大对象的传递效率和空间不如意的问题。
用引用传递函数的参数能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。
引用与指针的区别在于:
指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作,程序中使用指针使程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
(三)结构体、共用体和枚举
1.概念理解
数组是一组有序数据的集合,数组中的每一个元素都属于同一个数据类型。
用一个统一的数组名和下标来唯一的确定数组中的元素。
而结构体用于表示由固定多个不同类型的元素所构成的数据(相当于记录)。
联合体用于表示由固定多个不同类型的元素占用相同内存空间的元素所构成的数据。
枚举是指将变量的所有取值一一列举出来,变量的值只限于列举出来的值的范围内。
2.构造数据类型的定义
(1)结构体类型定义的一般形式为:
struct结构体类型名
{
类型标识符1成员1;
类型标识符2成员2;
…
类型标识符n成员n;
};
每个成员名前的类型标识符可以是已经定义了的任意类型,当然也可以是结构体类型标识符,即结构体成员可以是另一个结构体变量。
成员也可以是指向本结构体类型的指针变量。
(2)联合体和结构体的定义形式相似,使用了union关键字。
在作用域范围内,结构体对象的每个数据成员都有固定的存储位置,都可以随时被访问,而联合体对象的每个成员都从同一个位置(即对象的首地址)开始存储,在任一时刻只能保存一个数据成员,因而也只有该成员能够被访问,当然在不同的时刻可以用联合体对象存储不同的成员并进行相应的访问。
(3)枚举类型和枚举变量的定义形式为:
enum<枚举类型名>{<枚举表>};
或
enum{<枚举表>}<变量名表>;
枚举类型定义中每个枚举元素代表一个整数值。
如果定义时枚举元素未指定值,编译系统按定义顺序取默认值一次为0,1,2,3,……。
也可以给枚举值指定对应值。
3.构造类型变量的定义及初始化
(1)结构体变量的定义及初始化有3种形式:
①结构体类型名定义结构体变量
[struct]<结构体类型名><结构体变量名>[={<初始化数据>}|<结构变量名>],…;
②定义结构体类型的同时定义结构体变量,
struct结构体类型标识符
{
类型标识符1成员名1;
类型标识符2成员名2;
…
类型标识符n成员名n;
}<结构体变量名>[={<初始化数据>}|<结构变量名>],…;
③定义无名结构体类型的同时定义结构体变量
struct
{
类型标识符1成员名1;
类型标识符2成员名2;
…
类型标识符n成员名n;
}<结构体变量名>[={<初始化数据>}|<结构变量名>],…;
结构体类型变量的各个元素一次占用一块连续的内存空间,结构体变量所占的内存长度等于每个成员长度之和,结构体变量占用的内存大小可以用sizeof计算。
(2)联合体变量的定义同结构体变量一样,也有3种形式,但是使用的关键字为union。
联合体和结构体的定义形式相似,但含义不同,结构体变量所占的内存长度等于个成员所占内存长度之和(即每个成员分别占有自己的内存)。
联合体变量所占的内存长度等于最长的成员的长度(即每个成员共享同一段内存)。
不能直接引用联合体变量,也不能在自定义联合体变量的同时进行初始化,而只能引用联合体变量的成员。
(3)枚举类型的变量定义可以有以下形式。
①定义类型时定义,例如:
enumday{Sun,Mon,Tue,Wed,Thu,Fri,Sat}d1,d2,d3;
②直接定义枚举变量,例如:
enum{Sun,Mon,Tue,Wed,Thu,Fri,Sat}d1,d2,d3;
③使用枚举类型定义枚举变量,例如:
enumdayd1,d2,d3;
或
dayd1,d2,d3;
枚举变量只能取花括号中所列出的标识符,而取其他值都是非法的。
4.构造类型成员的访问
(1)结构体成员的访问
直接访问结构体的成员:
结构体变量名.成员名。
间接访问结构体的成员:
结构体指针->成员名。
也可以使用以下形式访问结构体变量:
(*p).num、(*p).name、(*p).score
当用“.”和“->”运算符访问嵌套结构体的成员时,先访问外层后访问内层,逐层处理,例如:
Student1LiYing,*p=&LiYing;
LiYing.birthday.year=2003;
p->birthday.day=26;
同类型的结构变量可以相互赋值,其运算功能是把右边的变量值复制到左边的变量中,即复制到左边变量对应的存储空间中,运算结果为左边的变量。
(2)联合体成员的访问
直接访问联合体的成员:
联合体变量.成员名。
间接访问联合体的成员:
联合体指针->成员名。
注意:
不能直接引用联合体变量,也不能在定义联合体变量时进行初始化,而只能引用联合体变量的成员。
(3)枚举元素的引用
枚举元素的引用不同于结构体成员和联合体成员。
它不需要使用“.”和“->”运算符,而是在枚举类型定义之外,可以直接引用。
例如:
enumcolor{red,yellow,green=3,blue}c1;
c1=blue;//枚举元素作为整型变量向枚举变量赋值,可以直接引用
对枚举元素按常量处理,但不能对它们赋值,例如:
red=0;//错误,不能给枚举常量赋值
5.构造体类型变量作为函数参数
(1)结构体类型作为函数参数
结构体是一种类型,它能够使用在允许简单类型使用的所有地方,也允许作为函数参数类型和返回值类型。
将一个结构体变量的值传递给另一个函数,有3种办法:
①用结构体变量的成员作实参:
将实参值传给形参,属于“值传递”方式,要求实参与形参的类型保持一致。
如,有函数定义
voidfun(intnum);
则有调用形式
fun(stu.num);
其中,stu.num是结构体变量stu的一个成员,其类型为int。
②用结构体变量作实参:
采用的是“值传递”方式,将结构体变量所占的内存单元的内容全部顺序传给形参。
要求形参也必须是同类型的结构体的变量。
如,有函数定义
voidfun(studenta);
则有调用形式
fun(x);
其中,x是结构体变量,其类型为student。
③用指向结构体变量(或数组)的指针作实参:
将结构体变量(或数组)的地址传给形参,属于传址调用,在函数中对形参的操作实际上是对实参的操作。
如,有函数定义
voidfun(student*P,intn);
voidfun1(student*S,intn);
则有调用形式
fun(&x,n);
fun1(stu,n);
其中,x是结构体类型student的结构体变量,stu是结构体类型student的结构体数组名。
fun函数调用将结构体变量x的地址传递给形参指针p,然后执行fun的函数体。
fun1函数调用将结构体数组stu[]的首地址传递给形参指针s,然后执行fun1的函数体。
注意:
函数也可以返回结构体变量、结构体变量的成员或结构体变量的指针。
由于用结构体变量作为函数参数和函数返回结构体变量在时间和空间上的开销都比较大,因此使用较少,多用于指向结构体变量的指针来替代。
(2)联合体作为函数的参数
可以使用联合体变量的成员和指向联合体变量的指针作为函数参数。
当使用指向联合体变量的指针时,指针访问的内容视指针指向的成员的类型决定。
不能把联合体变量作为函数参数,也不能让函数带回联合体变量。
(3)枚举作为函数的参数
枚举元素和枚举变量可以作为函数参数,函数的返回值也可以是枚举类型。
枚举类型的作用域与一般变量相同。
三、实验内容和步骤
【实例1】指针变量的定义和使用。
#include
usingnamespacestd;
voidmain()
{
inti=10;
int*ip;
ip=&i;
cout<<"i的地址是"<cout<<"i的值是"<<*ip<}
【实例2】指针的运算。
#include
usingnamespacestd;
voidmain()
{
inta=10,b=20;
int*pa,*pb;
pa=&a;
pb=&b;
cout<<"pa地址中的值是"<<*pa<cout<<"pa+1地址中的值是"<<*(pa+1)<if(pa!
=pb)
cout<<"pa和pb地址不相等"<}
【实例3】指向常量的指针变量的使用。
#include
usingnamespacestd;
voidmain()
{
constdouble*ip;
doublei1=123.456;
doublei2=234.567;
ip=&i1;
*ip=1.25;//错误,该地址的值不能改变
ip=&i2;//正确,对指针重新赋值
cout<<*ip<}
思考:
指针常量和指向常量的指针常量的使用。
【实例4】用指针法访问一维数组和二维数组。
#include
usingnamespacestd;
voidmain()
{inti,j,*p,a[5];
int(*q)[3],b[3][3]={{1,2,3},{4,5,6},{7,8,9}};
charstr[]="IloveChina!
";
char*r=str;
p=a;
q=b;
cout<<"输入五个整数:
";
for(i=0;i<5;i++)
cin>>*(p+i);
cout<<"一维数组:
";
for(i=0;i<5;i++)
cout<<*(p+i)<<"";
cout<cout<cout<<"二维数组:
"<for(i=0;i<3;i++)
{for(j=0;j<3;j++)
cout<<*(*(q+i)+j)<<"";
cout<}
cout<cout<<"字符串:
";
while(*r!
='\0')
{cout<<*r;
r=r+1;
}
cout<}
注意:
(1)指针与数组的关系。
(2)指针法的使用格式。
(3)指针使用的地址越界问题。
【实例5】用指针作函数的参数,完成三个整数值的循环交换。
#include
usingnamespacestd;
voidcirclechange(int*x,int*y,int*z);
voidmain()
{inta,b,c;
cout<<"Pleaseinputthreeintegernumbers:
"<cin>>a>>b>>c;
cout<<"beforecalling:
"<<"a="<circlechange(&a,&b,&c);
cout<<"aftercalling:
"<<"a="<}
voidcirclechange(int*x,int*y,int*z)
{inttemp;
temp=*x;
*x=*y;
*y=*z;
*z=temp;
}
注意:
形参是指针,实参必须是地址。
【实例6】引用的使用。
#include
usingnamespacestd;
voidmain()
{inta=20,b=40;
int&ra=a;//定义引用ra为a的别名
int*p=&b;//定义指针p指向b
int*&rp=p;