结构体和结构体指针

指针类型一
一个结构体变量的指针就是该变量所占据的内存段的起始地址(如结构体Student变量name的指针就是name这个变量所占据的内存段的起始地址,在定义结构体的时候已经为name分配好了内存空间)。可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址(也就是说你可能有很多个变量,但是这些变量有一个其实地址,那么此时这个结构体指针变量指向的就是这个起始地址)。
指针变量也可以用来指向结构体数组中的元素。
第一种类型是比较简单的,不涉及链表:
指向结构体变量的指针变量引用结构体变量中的成员。

//例 指向结构体变量的指针的应用。

#include <iostream>
#include <string>
using namespace std;
int main( )
{
struct Student//声明结构体类型student
{
int num;
char sex;
float score;
}Student stu; 
//定义Student类型的变量stu
Student *p=&stu;//定义p为指向Student类型数据的指针变量并指向stu
stu.num=10301;//对string变量可以直接赋值(通过这个结构体变量的指针)
stu.sex=′f′;
stu.score=89.5;
cout<<stu. num<<″ ″<<″ ″<<stu.sex<<″ ″<<
stu.score<<endl;
cout<<**p -> num**<<″ ″<<**(*p).name**<<″ ″<<(*p).sex<<″ ″<<(*p).score<<endl;//*p就是说指针本来存的是地址,但是*p就是这个地址的内容了  p->num表示指针p当前指向的结构体变量中的成员num
return 0;
}

类型二
这是和链表结合起来使用的
结构体变量指向结构体变量的指针构成链表

链表是一种常见的重要的数据结构

链表有一个“头指针”变量,以head表示,它存放一个地址,该地址指向一个元素(可以简单的理解为该地址的内容是一个元素)。
链表的每一个元素成为“结点”,每一个结点都应该包括两个部分:
1:用户需要用的实际的数据(即内容)
2:是下一个结点的地址
可以看到链表中的各个元素在内存中的存储单元可以是不连续的,要找到某一个元素,可以先找到上一个元素,根据它提供的下一个元素的地址(即下一个结点的位置)找到下一个元素。
可以看到,这种链表的数据结构,必须利用结构变量和指针才能实现。
可以声明一个结构体类型,包括两种成员,就是上面提到的,一种就是用户需要用的实际数据,一种就是用来存放下一个结点地址的指针。
例如,我们现在这样设计:

struct Student
{
int num;
float score;
Student *next;//next指向Student结构体变量
};```
其中成员num和score是用户需要用到的数据,next就是指针类型的成员,它指向Student类型的数据。用这种方法来建立链表。
每一个结点都属于Student类型,在它的成员next中存放下一个结点的地址,程序设计者不必知道各个结点的具体地址,只要保证能将下一个结点的地址放在前一个结点的成员next中就可以
下面通过一个简单的例子来说明如何建立和输出一盒简单的链表。

```cpp
#define NUll 0
#include<iostream>
struct Student
{
long num;
float score;
struct Student *next;
};
int main()
{
Student a,b,c,*head,*p;
a.num=21001;
a.score=89.5;
b.num=21002;
b.score=95.8;
c.num=21005;
c.score=85.8;
//接下来是非常重要的一步,就是保证将下一个结点的地址放在前一个结点的成员next中
head=&a;//将结点a的起始地址赋给头指针head
a.next=&b;//将结点b的起始地址赋给a结点的next成员,这样就可以保证链表的实现了
b.next=&c;
c.next=NULL;
p=head;//使p指针指向a结点,即第一个结点。
do
{
cout<<p->num<<""<<p->score<<endl;
p=p->next;//使p指向下一个结点
}while(p!=NUll);//在这里利用了do-while语句,我觉得在这里用这个语句特别的棒
return 0;
}



函数参数
将一个结构体变量中的数据传递给另一个函数,有下列三种方法:
(1)用结构体变量名作参数,一般很少用这种方法
(2)用指向结构体变量的指针作实参,将结构体变量(如Student stu, Student *p=&stu)的地址传给形参。
(3)用结构体变量的引用变量作函数参数。
下面通过一个简单的例子来说明,并对它们进行比较。
例有一个结构体变量stu,内含学生学号、姓名和3门课的成绩。要求在main函数中为各成员赋值,在另一函数print中将它们的值输出。
方法一
用结构体变量作函数参数

#include <iostream>
#include <string>
using namespace std;
{ int num;
float score[3];
};
int main( )
{
void print(Student);//函数声明,形参类型为结构体Student
Student stu;//定义结构体变量
stu.num=12345;//以下5行对结构体变量各成员赋值
stu.score[0]=67.5;
stu.score[1]=89;
stu.score[2]=78.5;
print(stu);//调用print函数,输出stu各成员的值
return 0;
}
void print(Student st)
{
cout<<st.num<<″ ″<<″ ″<<st.score[0]
<<″ ″ <<st.score[1]<<″ ″<<st.score[2]<<endl;
}

运行结果为 12345 67.5 89 78.5

方法二
用指向结构体变量的指针作实参

#include <iostream>
#include <string>

using namespace std;

struct Student

{

int num; string name;//用string类型定义字符串变量

float score[3];

}stu={12345,″Li Fung″,67.5,89,78.5};//定义结构体student变量stu并赋初值

int main( )

{

void print(Student *);//函数声明,形参为指向Student类型数据的指针变量

Student *pt=&stu;//定义基类型为Student的指针变量pt,并指向stu

print(pt);//实参为指向Student类数据的指针变量

return 0;

}

//定义函数,形参p是基类型为Student的指针变量

void print(Student *p)

{

cout<<p->num<<″ ″<<p->name<<″ ″<<p->score[0]<<″ ″ <<

p->score[1]<<″ ″<<p->score[2]<<endl;

} 

调用print函数时,实参指针变量pt将stu的起始地址传送给形参p(p也是基类型为student的指针变量)。这样形参p也就指向stu.
在print函数中输出p所指向的结构体变量的各个成员值,它们也就是stu的成员值。在main函数中也可以不定义指针变量pt,而在调用print函数时以&stu作为实参,把stu的起始地址传给实参p。

方法三
用结构体变量的引用作函数参数

#include <iostream>
#include <string>
using namespace std;
struct Student
{
int num;
string name;
float score[3];
}stu={12345,″Li Li″,67.5,89,78.5};
void main( )
{

void print(Student &);

//函数声明,形参为Student类型变量的引用

print(stu);

//实参为结构体Student变量

}

//函数定义,形参为结构体Student变量的引用

void print(Student &stud)

{

cout<<stud.num<<″ ″<<″ ″<<stud.score[0]

<<″ ″ <<stud.score[1]<<″ ″<<stud.score[2]<<endl;

} 

程序(1)用结构体变量作实参和形参,程序直观易懂,效率是不高的。
程序(2)采用指针变量作为实参和形参,空间和时间的开销都很小,效率较高。但程序(2)不如程序(1)那样直接。
程序(3)的实参是结构体Student类型变量,而形参用Student类型的引用,虚实结合时传递的是stu的地址,因而效率较高。它兼有(1)和(2)的优点。
引用变量主要用作函数参数,它可以提高效率,而且保持程序良好的可读性。在本例中用了string方法定义字符串变量,在某些C++系统中不能运行这些程序,读者可以修改程序,使之能在自己所用的系统中运行。

分配
在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除。
在C语言中是利用库函数malloc和free来分配和撤销内存空间
sizeof
C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。

注意: new和delete是运算符,不是函数,因此执行效率高。

虽然为了与C语言兼容,C++仍保留malloc和free函数,但建议用户不用malloc和free函数,而用new和delete运算符。

new运算符的例子:
new int;//开辟一个存放整数的存储空间,返回一个指向该存储空间的地址(即指针)
new int(100);//开辟一个存放整数的空间,并指定该整数的初值为100,返回一个指向该存储空间的地址
new char[10];//开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址
new int[5][4];//开辟一个存放二维整型数组(大小为5*4)的空间,返回首元素的地址
float *p=new float (3.14159);//开辟一个存放单精度数的空间,并指定该实数的初值为//3.14159,将返回的该空间的地址赋给指针变量p [1]
new运算符使用的一般格式为
new 类型 [初值]

用new分配数组空间时不能指定初值。如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL,用户可以根据该指针的值判断分配空间是否成功。

delete运算符使用的一般格式为
delete [ ] 指针变量
例如要撤销上面用new开辟的存放单精度数的空间(上面第5个例子),应该用
delete p;
前面用“new char[10];”开辟的字符数组空间,如果把new返回的指针赋给了指针变量pt,则应该用以下形式的delete运算符撤销该空间
delete [] pt;//在指针变量前面加一对方括号,表示是对数组空间的操作
例 开辟空间以存放一个结构体变量。

#include <iostream>
#include <string>
using namespace std;
struct Student //声明结构体类型Student
{ 
string name;
int num;
char sex;
};
int main( )
{ 
Student *p; //定义指向结构体类型Student的数据的指针变量
p=new Student; //用new运算符开辟一个存放Student型数据的空间
p->name=″Wang Fun″; //向结构体变量的成员赋值
p->num=10123;
p->sex='m';
cout<<p->name<<endl<<p->num
<<endl<<p->sex<<endl;//输出各成员的值
delete p;//撤销该空间
return 0;
}

运行结果为

Wang Fun 10123 m

在动态分配/撤销空间时,往往将这两个运算符和结构体结合使用,是很有效的。可以看到:
要访问用new所开辟的结构体空间,无法直接通过变量名进行,只能通过指针p进行访问。如果要建立一个动态链表,必须从第一个结点开始,逐个地开辟结点并输入各结点数据,通过指针建立起前后相链的关系。

  • 4
    点赞
  • 0
    评论
  • 17
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值