前面的课程我们学习了一些简单数据类型(整型、实型、字符型)的定义和应用,还学习了数组(一维、二维)的定义和应用,这些数据类型的特点是:当定义某一特定数据类型,就限定该类型变量的存储特性和取值范围。对简单数据类型来说,既可以定义单个的变量,也可以定义数组。而数组的全部元素都具有相同的数据类型,或者说是相同数据类型的一个集合。
在日常生活中,我们常会遇到一些需要填写的登记表,如住宿表、成绩表、通讯地址等。
在这些表中,填写的数据是不能用同一种数据类型描述的,在住宿表中我们通常会登记上姓名、性别、身份证号码等项目;在通讯地址表中我们会写下姓名、邮编、邮箱地址、电话号码、E - m a i l等项目。这些表中集合了各种数据,无法用前面学过的任一种数据类型完全描述,因此C引入一种能集中不同数据类型于一体的数据类型—结构体类型。结构体类型的变量可以拥有不同数据类型的成员,是不同数据类型成员的集合。
在上面描述的各种登记表中,让我们仔细观察一下住宿表、成绩表、通讯地址等。
住宿表由下面的项目构成:
这些登记表用C提供的结构体类型描述如下:
住宿表:
struct accommod
{
char name[20]; / *姓名* /
char sex; / *性别* /
char job[40]; / *职业* /
int age; / *年龄* /
long number; / *身份证号码* /
} ;
成绩表:
struct score
{
char grade[20]; / * 班级* /
long number; / * 学号* /
char name[20]; / *姓名* /
float os; / *操作系统* /
float datastru; / * 数据结构* /
float compnet; / * 计算机网络* /
} ;
通讯地址表:
struct addr
{
char name[20];
char department[30];/ * 部门* /
char address[30]; / *住址* /
long box; / * 邮编* /
long phone; / * 电话号码* /
char email[30]; / * E m a i l * /
};
这一系列对不同登记表的数据结构的描述类型称为结构体类型。由于不同的问题有不同的数据成员,也就是说有不同描述的结构体类型。我们也可以理解为结构体类型根据所针对的问题其成员是不同的,可以有任意多的结构体类型描述。
下面给出C对结构体类型的定义形式:
struct 结构体名
{
成员项表列
};
有了结构体类型,我们就可以定义结构体类型变量,以对不同变量的各成员进行引用。
7.1.1 结构体类型变量的定义
结构体类型变量的定义与其它类型的变量的定义是一样的,但由于结构体类型需要针对问题事先自行定义,所以结构体类型变量的定义形式就增加了灵活性,共计有三种形式,分别介绍如下:
1) 先定义结构体类型,再定义结构体类型变量:
struct stu / *定义学生结构体类型* /
{
char name[20]; / * 学生姓名* /
char sex; / * 性别* /
long num; / *学号* /
float score[3]; / * 三科考试成绩* /
};
struct stu student1,student2;/ * 定义结构体类型变量* /
struct stu student3,student4;
用此结构体类型,可以定义更多的该结构体类型变量。
2 ) 定义结构体类型同时定义结构体类型变量:
struct data
{
int day;
int month;
int year;
} time1,time2;
也可以再定义如下变量:
struct data time3,time4;
用此结构体类型,同样可以定义更多的该结构体类型变量。
3) 直接定义结构体类型变量:
struct
{
char name[20]; / *学生姓名* /
char sex; / *性别* /
long num; / *学号* /
float score[3]; / *三科考试成绩* /
} person1,person2; / *定义该结构体类型变量* /
该定义方法由于无法记录该结构体类型,所以除直接定义外,不能再定义该结构体类型变量
什么是结构体?
简单的来说,结构体就是一个可以包含不同数据类型的一个结构,它是一种可以自己定义的数据类型,它的特点和数组主要有两点不同,首先结构体可以在一个结构中声明不同的数据类型,第二相同结构的结构体变量是可以相互赋值的,而数组是做不到的,因为数组是单一数据类型的数据集合,它本身不是数据类型(而结构体是),数组名称是常量指针,所以不可以做为左值进行运算,所以数组之间就不能通过数组名称相互复制了,即使数据类型和数组大小完全相同。
定义结构体使用struct修饰符,例如:
struct test
{
float a;
int b;
};
上面的代码就定义了一个名为test的结构体,它的数据类型就是test,它包含两个成员a和b,成员a的数据类型为浮点型,成员b的数据类型为整型。
由于结构体本身就是自定义的数据类型,定义结构体变量的方法和定义普通变量的方法一样。
test pn1;
这样就定义了一test结构体数据类型的结构体变量pn1,结构体成员的访问通过点操作符进行,pn1.a=10 就对结构体变量pn1的成员a进行了赋值操作。
注意:结构体生命的时候本身不占用任何内存空间,只有当你用你定义的结构体类型定义结构体变量的时候计算机才会分配内存。
结构体,同样是可以定义指针的,那么结构体指针就叫做结构指针。
结构指针通过->符号来访问成员,下面我们就以上所说的看一个完整的例子:
#include <iostream>
#include <string>
using namespace std;
struct test//定义一个名为test的结构体
{
int a;//定义结构体成员a
int b;//定义结构体成员b
};
void main()
{
test pn1;//定义结构体变量pn1
test pn2;//定义结构体变量pn2
pn2.a=10;//通过成员操作符.给结构体变量pn2中的成员a赋值
pn2.b=3;//通过成员操作符.给结构体变量pn2中的成员b赋值
pn1=pn2;//把pn2中所有的成员值复制给具有相同结构的结构体变量pn1
cout<<pn1.a<<"|"<<pn1.b<<endl;
cout<<pn2.a<<"|"<<pn2.b<<endl;
test *point;//定义结构指针
point=&pn2;//指针指向结构体变量pn2的内存地址
cout<<pn2.a<<"|"<<pn2.b<<endl;
point->a=99;//通过结构指针修改结构体变量pn2成员a的值
cout<<pn2.a<<"|"<<pn2.b<<endl;
cout<<point->a<<"|"<<point->b<<endl;
cin.get();
}
#include <iostream>
#include <string>
using namespace std;
struct test
{
char name[10];
float socre;
};
void print_score(test pn)//以结构变量进行传递
{
cout<<pn.name<<"|"<<pn.socre<<endl;
}
void print_score(test *pn)//一结构指针作为形参
{
cout<<pn->name<<"|"<<pn->socre<<endl;
}
void main()
{
test a[2]={{"marry",88.5},{"jarck",98.5}};
int num = sizeof(a)/sizeof(test);
for(int i=0;i<num;i++)
{
print_score(a[i]);
}
for(int i=0;i<num;i++)
{
print_score(&a[i]);
}
cin.get();
}
void print_score(test *pn)的效率是要高过void print_score(test pn)的,因为直接内存操作避免了栈空间开辟结构变量空间需求,节省内存。
下面我们再说一下,传递结构引用的例子。
利用引用传递的好处很多,它的效率和指针相差无几,但引用的操作方式和值传递几乎一样,种种优势都说明善用引用可以做到程序的易读和易操作,它的优势尤其在结构和大的时候,避免传递结构变量很大的值,节省内存,提高效率。
#include <iostream>
#include <string>
using namespace std;
struct test
{
char name[10];
float socre;
};
void print_score(test &pn)//以结构变量进行传递
{
cout<<pn.name<<"|"<<pn.socre<<endl;
}
void main()
{
test a[2]={{"marry",88.5},{"jarck",98.5}};
int num = sizeof(a)/sizeof(test);
for(int i=0;i<num;i++)
{
print_score(a[i]);
}
cin.get();
}
#include <iostream>
#include <string>
using namespace std;
struct test
{
char name[10];
float socre;
};
void print_score(test &pn)
{
cout<<pn.name<<"|"<<pn.socre<<endl;
}
test get_score()
{
test pn;
cin>>pn.name>>pn.socre;
return pn;
}
void main()
{
test a[2];
int num = sizeof(a)/sizeof(test);
for(int i=0;i<num;i++)
{
a[i]=get_score();
}
cin.get();
for(int i=0;i<num;i++)
{
print_score(a[i]);
}
cin.get();
}
//-------------------------------------例程2---------------------------------
#include <iostream>
#include <string>
using namespace std;
struct test
{
char name[10];
float socre;
};
void print_score(test &pn)
{
cout<<pn.name<<"|"<<pn.socre<<endl;
}
void get_score(test &pn)
{
cin>>pn.name>>pn.socre;
}
void main()
{
test a[2];
int num = sizeof(a)/sizeof(test);
for(int i=0;i<num;i++)
{
get_score(a[i]);
}
cin.get();
for(int i=0;i<num;i++)
{
print_score(a[i]);
}
cin.get();
}
在上一个教程中我们已经简单的阐述了什么是结构体了,为了进一部的学习结构体这一重要的知识点,我们今天来学习一下链表结构。
结构体可以看做是一种自定义的数据类型,它还有一个很重要的特性,就是结构体可以相互嵌套使用,但也是有条件的,结构体可以包含结构体指针,但绝对不能在结构体中包含结构体变量。
struct test
{
char name[10];
float socre;
test *next;
};//这样是正确的!
struct test
{
char name[10];
float socre;
test next;
};//这样是错误的!
利用结构体的这点特殊特性,我们就可以自己生成一个环环相套的一种射线结构,一个指向另一个。
链表的学习不像想象的那么那么容易,很多人学习到这里的时候都会碰到困难,很多人也因此而放弃了学习,在这里我说,一定不能放弃,对应它的学习我们要进行分解式学习,方法很重要,理解需要时间,不必要把自己逼迫的那么紧,学习前你也得做一些最基本的准备工作,你必须具备对堆内存的基本知识的了解,还有就是对结构体的基本认识,有了这两个重要的条件,再进行分解式学习就可以比较轻松的掌握这一节内容的难点。
下面我们给出一个完整的创建链表的程序,不管看的懂看不懂希望读者先认真看一下,想一想,看不懂没有关系,因为我下面会有分解式的教程,但之前的基本思考一定要做,要不即使我分解了你也是无从理解的。
代码如下,我在重要部分做了注解:
#include <iostream>
using namespace std;
struct test
{
char name[10];
float socre;
test *next;
};
test *head;//创建一个全局的引导进入链表的指针
test *create()
{
test *ls;//节点指针
test *le;//链尾指针
ls = new test;//把ls指向动态开辟的堆内存地址
cin>>ls->name>>ls->socre;
head=NULL;//进入的时候先不设置head指针指向任何地址,因为不知道是否一上来就输入null跳出程序
le=ls;//把链尾指针设置成刚刚动态开辟的堆内存地址,用于等下设置le->next,也就是下一个节点的位置
while(strcmp(ls->name,"null")!=0)//创建循环条件为ls->name的值不是null,用于循环添加节点
{
if(head==NULL)//判断是否是第一次进入循环
{
head=ls;//如果是第一次进入循环,那么把引导进入链表的指针指向第一次动态开辟的堆内存地址
}
else
{
le->next=ls;//如果不是第一次进入那么就把上一次的链尾指针的le->next指向上一次循环结束前动态创建的堆内存地址
}
le=ls;//设置链尾指针为当前循环中的节点指针,用于下一次进入循环的时候把上一次的节点的next指向上一次循环结束前动态创建的堆内存地址
ls=new test;//为下一个节点在堆内存中动态开辟空间
cin>>ls->name>>ls->socre;
}
le->next=NULL;//把链尾指针的next设置为空,因为不管如何循环总是要结束的,设置为空才能够在循环显链表的时候不至于死循环
delete ls;//当结束的时候最后一个动态开辟的内存是无效的,所以必须清除掉
return head;//返回链首指针
}
void showl(test *head)
{
cout<<"链首指针:"<<head<<endl;
while(head)//以内存指向为null为条件循环显示先前输入的内容
{
cout<<head->name<<"|"<<head->socre<<endl;
head=head->next;
}
}
void main()
{
showl(create());
cin.get();
cin.get();
}
上面的代码我们是要达到一个目的:就是要存储你输入的人名和他们的得分,并且以链状结构把它们组合成一个链状结构
程序种有两个组成部分
test *create()
和
void showl(test *head)
这两个函数,create是用来创建链表的 ,showl是用来显示链表的。
create函数的返回类型是一个结构体指针,在程序调用的时候我们用了showl(create());,而不用引用的目的原因是引导指针是一个全局指针变量,我们不能在showl()内改变它,因为showl()函数内有一个移动操作head=head->next;,如果是引用的话我们就破坏了head指针的位置,以至于我们再也无法找会首地址的位置了。
下面我们来分解整个程序,以一个初学者的思想来思考整个程序,由浅入深的逐步解释。
首先,我们写这个程序,要考虑到由于是一个链表结构,我们不可能知道它的大小到底是多大,这个问题我们可以用动态开辟堆内存来解决,因为堆内存在程序结束前始终是有效的,不受函数栈空间生命期的限制,但要注意的是我们必须有一个指针变量来存储这一链状结构的进入地址,而在函数内部来建立这一指针变量显然是不合适的,因为函数一旦退出,这个指针变量也随之失效,所以我们在程序的开始声明了一个全局指针变量。
test *head;//创建一个全局的引导进入链表的指针
好解决了这两个问题,我们接下去思考
有输入就必然有输出,由于输出函数和输入函数是相对独立的,为了不断测试程序的正确性好调试我们先写好输出函数和main函数捏的调用,创建函数我们先约定好名为create。
我们先写出如下的代码:
#include <iostream>
using namespace std;
struct test
{
char name[10];
float socre;
test *next;
};
test *head;//创建一个全局的引导进入链表的指针
test *create()
{
return head;//返回链首指针
}
void showl(test *head)
{
cout<<"链首指针:"<<head<<endl;
while(head)//以内存指向为null为条件循环显示先前输入的内容
{
cout<<head->name<<"|"<<head->socre<<endl;
head=head->next;
}
}
void main()
{
showl(create());
cin.get();
cin.get();
}
程序写到这里,基本形态已经出来,输入和调用我们已经有了。
下面我们来解决输入问题,链表的实现我们是通过循环输入来实现的,既然是循环我们就一定得考虑终止循环的条件,避免死循环和无效循环的发生。
在create()函数内部我们先写成这样:
test *create()
{
test *ls;//节点指针
test *le;//链尾指针
ls = new test;//把ls指向动态开辟的堆内存地址
cin>>ls->name>>ls->socre;
head=NULL;//进入的时候先不设置head指针指向任何地址,因为不知道是否一上来就输入null跳出程序
le=ls;//把链尾指针设置成刚刚动态开辟的堆内存地址,用于等下设置le->next,也就是下一个节点的位置
le->next=NULL;//把链尾指针的next设置为空,因为不管如何循环总是要结束的,设置为空才能够在循环显链表的时候不至于死循环
delete ls;//当结束的时候最后一个动态开辟的内存是无效的,所以必须清除掉
return head;//返回链首指针
}
在循环创建之前我们必须考虑一个都不输入的情况。
程序一单进入create函数我们首先必然要创建一个节点,我们先创建一个节点指针,后把者个节点指针指向到动态开辟的test类型的动态内存地址位置上。
所以我们有了
test *ls;
ls = new test;
程序既然是循环输入,而结构成员test *next又是用来存储下一个接点的内存地址的,每次循环我们又要动态创建一个新的内存空间,所以我们必须要有一个指针来存储上一次循环动态开辟的内存地址,于是就有了
test *le;
接下来在进入循环前我们要创建链表的第一个节点,第一个节点必然是在循环外创建,于是就有了
cin>>ls->name>>ls->socre;
程序执行者的情况是位置的,所以我们必然要考虑,一上来就不想继续运行程序的情况,所以我们一开始先把head引导指针设置为不指向任何地址也就是
head=NULL;
为了符合le也就是链尾指针的设计思路,我们在循环前一定要保存刚刚动态开辟的内存地址,好在下一次循环的时候设置上一个节点中的next成员指向,于是我们便有了:
le=ls;
为了实现循环输入我们又了下面的代码:
while(strcmp(ls->name,"null")!=0)
{
if(head==NULL)
{
head=ls;
}
else
{
le->next=ls;
}
le=ls;
ls=new test;
cin>>ls->name>>ls->socre;
}
程序是循环必然要有终止循环的条件,所以我们的循环条件是:
while(strcmp(ls->name,"null")!=0)
输入的名字是null的时候就停止循环。
为了保证第一次进入循环,也就是在循环内准备创建第二个节点前,设置引导指针的指向我们有了如下的判断代码:
if(head==NULL)
{
head=ls;
}
else
{
le->next=ls;
}
代码中的else条件是为了设置前一个节点next指向而写的,这点我们记住先看下面的代码,稍后大家回过头想就明白了
le=ls;
ls=new test;
cin>>ls->name>>ls->socre;
le=ls;这么写就是为了保存上一次循环指针的位置而设的,正是为了上面的else代码而做的预先保留
ls=new test;
cin>>ls->name>>ls->socre;
这两行代码的意思就是继续开辟下一个节点空间,和输入节点内容!
循环一旦结束也就结束了程序,为了保持程序不出错,也就是最后一个节点的next成员指向为空我们有了下面的代码
le->next=NULL;
程序的思路始终是以先开辟后判断为思路的,所以到最后一个不成立的时候总会有一个多开辟的内存空间,为了删除掉它,我们有了下面的代码
delete ls;
程序到最后由于返回head指针
return head;
显示链表的函数没有什么太多特别的也只需要注意下面这样就可以了!
head=head->next;
我们之所以不用head+=1;来写就是因为链表是我们动态开辟的,而每一个节点的位置并不是相连的,next成员指针的意义也就是下一个节点的内存地址。
到这里整个创建函数的设计思路也都说完了,笔者不一定说的很好,但基本思路是这样的,希望读者多思考,多对比,相信此教程还是对大家有帮助的,程序设计就是利用逐步思考的方式进行的,写好的代码往往直接看看不懂就是因为中间的细节并不是一次都能够想到的。
下面我们来说一下链表节点的删除!
我们以上面的程序为基础,但为了我们方便学习删除我们休整结构体为
struct test
{
int number;
float socre;
test *next;
};
number为唯一的编号每一个节点的。
删除的我就不多说了,里面重要部分有注解。
#include <iostream>
using namespace std;
struct test
{
int number;
float socre;
test *next;
};
test *head;//创建一个全局的引导进入链表的指针
test *create()
{
test *ls;//节点指针
test *le;//链尾指针
ls = new test;//把ls指向动态开辟的堆内存地址
cin>>ls->number>>ls->socre;
head=NULL;//进入的时候先不设置head指针指向任何地址,因为不知道是否一上来就输入null跳出程序
le=ls;//把链尾指针设置成刚刚动态开辟的堆内存地址,用于等下设置le->next,也就是下一个节点的位置
while(ls->number!=0)//创建循环条件为ls->number的值不是null,用于循环添加节点
{
if(head==NULL)//判断是否是第一次进入循环
{
head=ls;//如果是第一次进入循环,那么把引导进入链表的指针指向第一次动态开辟的堆内存地址
}
else
{
le->next=ls;//如果不是第一次进入那么就把上一次的链尾指针的le->next指向上一次循环结束前动态创建的堆内存地址
}
le=ls;//设置链尾指针为当前循环中的节点指针,用于下一次进入循环的时候把上一次的节点的next指向上一次循环结束前动态创建的堆内存地址
ls=new test;//为下一个节点在堆内存中动态开辟空间
cin>>ls->number>>ls->socre;
}
le->next=NULL;//把链尾指针的next设置为空,因为不管如何循环总是要结束的,设置为空才能够在循环显链表的时候不至于死循环
delete ls;//当结束的时候最后一个动态开辟的内存是无效的,所以必须清除掉
return head;//返回链首指针
}
void showl(test *head)
{
cout<<"链首指针:"<<head<<endl;
while(head)//以内存指向为null为条件循环显示先前输入的内容
{
cout<<head->number<<"|"<<head->socre<<endl;
head=head->next;
}
}
void deletel(test *&head,int number)//这里如果参数换成test *head,意义就完全不同了,head变成了复制而不是原有链上操作了,特别注意,很多书上都不对这里
{
test *point;//判断链表是否为空
if(head==NULL)
{
cout<<"链表为空,不能进行删除工作!";
return;
}
if(head->number==number)//判删除的节点是否为首节点
{
point=head;
cout<<"删除点是链表第一个节点位置!";
head=head->next;//重新设置引导指针
delete point;
return;
}
test *fp=head;//保存连首指针
for(test *&mp=head;mp->next;mp=mp->next)
{
if(mp->next->number==number)
{
point=mp->next;
mp->next=point->next;
delete point;
head=fp;//由于head的不断移动丢失了head,把进入循环前的head指针恢复!
return;
}
}
}
void main()
{
head=create();//调用创建
showl(head);
int dp;
cin>>dp;
deletel(head,dp);//调用删除
showl(head);
cin.get();
cin.get();
}
最后我学习一下如何在已有的链表上插入节点
我们要考虑四中情况,
1.链表为空!
2.插入点在首节点前
3.插入点找不到的情况我们设置放在最后!
4.插入点在中间的情况!
今天的程序在昨天的基础上做了进一步的修改,可以避免删除点找不到的情况,如果找不到删除点就退出函数!
#include <iostream>
using namespace std;
struct test
{
int number;
float socre;
test *next;
};
test *head;//创建一个全局的引导进入链表的指针
test *create()
{
test *ls;//节点指针
test *le;//链尾指针
ls = new test;//把ls指向动态开辟的堆内存地址
cout<<"请输入第一个节点number和节点score,输入0.0跳出函数"<<endl;
cin>>ls->number>>ls->socre;
head=NULL;//进入的时候先不设置head指针指向任何地址,因为不知道是否一上来就输入null跳出程序
le=ls;//把链尾指针设置成刚刚动态开辟的堆内存地址,用于等下设置le->next,也就是下一个节点的位置
while(ls->number!=0)//创建循环条件为ls->number的值不是null,用于循环添加节点
{
if(head==NULL)//判断是否是第一次进入循环
{
head=ls;//如果是第一次进入循环,那么把引导进入链表的指针指向第一次动态开辟的堆内存地址
}
else
{
le->next=ls;//如果不是第一次进入那么就把上一次的链尾指针的le->next指向上一次循环结束前动态创建的堆内存地址
}
le=ls;//设置链尾指针为当前循环中的节点指针,用于下一次进入循环的时候把上一次的节点的next指向上一次循环结束前动态创建的堆内存地址
ls=new test;//为下一个节点在堆内存中动态开辟空间
cout<<"请下一个节点number和节点score,输入0.0跳出函数"<<endl;
cin>>ls->number>>ls->socre;
}
le->next=NULL;//把链尾指针的next设置为空,因为不管如何循环总是要结束的,设置为空才能够在循环显链表的时候不至于死循环
delete ls;//当结束的时候最后一个动态开辟的内存是无效的,所以必须清除掉
return head;//返回链首指针
}
void showl(test *head)
{
cout<<"链首指针:"<<head<<endl;
while(head)//以内存指向为null为条件循环显示先前输入的内容
{
cout<<head->number<<"|"<<head->socre<<endl;
head=head->next;
}
}
void deletel(test *&head,int number)//这里如果参数换成test *head,意义就完全不同了,head变成了复制而不是原有链上操作了,特别注意,很多书上都不对这里
{
test *point;//判断链表是否为空
if(head==NULL)
{
cout<<"链表为空,不能进行删除工作!";
return;
}
int derror=1;//设置找不到的情况的条件,预先设置为真
test *check=head;
while(check)//利用循环进行查找
{
if (check->number==number)
{
derror=0;//条件转为假
}
check=check->next;
}
if(derror)//如果为假就跳出函数
{
return;
}
if(head->number==number)//判删除的节点是否为首节点
{
point=head;
cout<<"删除点是链表第一个节点位置!";
head=head->next;//重新设置引导指针
delete point;
return;
}
test *fp=head;//保存连首指针
for(test *&mp=head;mp->next;mp=mp->next)
{
if(mp->next->number==number)
{
point=mp->next;
mp->next=point->next;
delete point;
head=fp;//由于head的不断移动丢失了head,把进入循环前的head指针恢复!
return;
}
}
}
void insterl(int number)
{
test *point=new test;
cout<<"请输入节点number和节点score"<<endl;
cin>>point->number>>point->socre;
if(head==NULL)//链表为空的情况下插入
{
head=point;
point->next=NULL;
return;
}
int ierror=1;//设置找不到的情况的条件,预先设置为真
test *le;
test *check=head;
while(check)//利用循环进行查找
{
if (check->number==number)
{
ierror=0;//条件转为假
}
le=check;
check=check->next;
}
if(ierror)
{
cout<<le->number;
le->next=point;
point->next=NULL;
return;
}
if(head->number==number)//检测是否是在第一个节点处插入
{
point->next=head;
head=point;
return;
}
for(test *&mp=head;mp->next;mp=mp->next)//在链表中间插入
{
if(mp->next->number==number)
{
point->next=mp->next;
mp->next=point;
return;
}
}
}
void main()
{
head=create();//调用创建
showl(head);
int dp;
cout<<"请输入删除点如果找不到就跳出函数"<<endl;
cin>>dp;
deletel(head,dp);//调用删除
showl(head);
int ip;
cout<<"请输入插入点如果找不到就在链尾添加"<<endl;
cin>>ip;
insterl(ip);
showl(head);
cin.get();
cin.get();
}
到此关于结构体的内容已经全部讨论结束,链表的建立删除插入操作可以很好的对前面所学知识进行一个总结,它既考察了程序员对内存大理解(堆内存操作、指针操作)也考察了对结构化编程掌握的熟悉程序。
结构体
作者:佚名 来源:本站原创 点击: 更新时间:2004-6-17
一、目的要求
1 .掌握结构体变量、结构体数组的定义和引用。
2 .掌握用结构体作为数据结构的程序设计方法。
3 .掌握用指向结构体类型数据的指针变量作为函数参数时的应用。
4 .深刻体会用结构体作数据结构的优越性。
二、启事与范例
1 .结构体类型变量的定义及引用
定义结构体变量的方法有三种:
① 先定义结构体类型再定义结构体变量;
② 在定义结构体类型的同时定义结构体变量;
③ 直接定义结构体变量。
建议最好使用第一种方法,并且将结构体类型的定义放在文件头部(或放在头文件中),这样,就可以在该文件的各函数中用这一类型去定义局部的结构体变量,也可以用此类型去定义外部的结构体变量,甚至于定义结构体类型的函数。
例 1 输入 10 个学生的学号、姓名及数学、英语、计算机三门课的分数,输出总分最高及总分最低的学生的全部信息。
struct student
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
} ;
/ * 以上是对结构体类型 student 的定义 * /
main ( )
{
int i;
struct student st, stmax, stmin;
/ * 以上是对结构体变量 st, stmax, stmin 的定义 * /
stmax. total = -32768;
stmin. total = 32767;
for (i = 0; i < 10; i++)
{
printf ( ″ /n Enter data : ″ ) ;
scanf ( ″%d %s %d %d %d″,&st.number,st.name,&st.math,&st.english,&st.computer);
st.total = st. math + st. english+st.computer;
if (st.total>stmax. total) stmax = st;
if (st. total
}
printf ( ″ /n max :%5d %15 %4d %4d %4d %4d ″,
stmax.number, stmax.name, stmax.math, stmax.english, stmax.computer, stmax.total);
printf ( ″ /n min :%5d %15s %4d %4d %4d %4d ″,
stmin.number, stmin.name, stmin.math, stmin.english, stmin.computer, stmin.total);
}
2 .结构体类型数组的定义及引用
对于结构体类型数组的定义,同样先定义结构体类型,然后用此类型去定义结构体数组。
例 2 输入 10 个学生的数据,然后按总分从高到低的顺序排序后输出(每个学生的数据同例 1 )。
为方便起见,可用宏定义指令将 struct student 用一个宏 STU 代替 ( 见程序第 1 行)。
#define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
}
main ( )
{
int i, j;
STU st [10], t;
/ * 以上是定义结构体数组 st 及结构体变量 t * /
printf ( ″ /n Enter data : ″ ) ;
for (i = 0 ; i<10 ; i++)
{
scanf ( ″ %d%s%d%d%d ″ , & st [i].number, st [i].name, & st [i].math,
& st [i].english, & st [i].computer);
st [i].total = st [i].math + st [i].english + st [i].computer;
}
for (i = 0; i<10; i++)
for (j = i + 1; j<10; j++)
if (st [i].total < st [j]. total)
{ t = st [i]; st [i]=st [j]; st [j]=t;}
for ( i= 0; i<10; i++)
printf ( ″ /n %5d%15s%4d%4d%4d ″ ,
st[i]. number,st[i].name,st[i].math
st[i].english,st[i].computer,st[i].total);
}
请详细阅读以上程序,掌握结构体数组的用法,体会用结构体作数据结构的优越性。
3 .用指向结构体的指针作函数参数
如果直接用结构体变量作函数参数,则数据传递是一种值传递方式,不能在函数中通过形参变量(结构体变量)改变实参变量(结构体变量)的值。用指向结构体的指针作函数参数可解决此问题。
用指向结构体的指针变量作函数参数,最常见的用法有两种:
① 用于接受实参变量(结构体变量)的地址,从而在函数中可以通过指针变量间接地访问实参变量所在的内存单元,以达到双向传递的效果(见例 3 )。
② 用于接受实参数组(结构体数组)的首地址,从而在函数中可以通过移动该指针变量的指向(或通过指针运算)间接地访问实参数组各元素所在的内存单元(见例 4 )。
例 3 将例 1 改为用函数实现。
# define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
};
void max _ min (STU st[ ], int n, STU *stmax, STU *stmin)
{
int i;
* stmax = st [0];
* stmin = st [0];
for (i = 1; i
{
if (st [i]. total > stmax - > total) *stmax = st [i];
if (st [i]. total < stmin - > total) *stmin = st [i];
}
}
main ( )
{
int i;
STU st [10], stmax, stmin;
for (i = 0; i<10; i++)
{
scanf ( … ); / * 此输入语句与例 2 相同 * /
st [i]. total = st [i]. math + st [i]. english+st[i]. computer;
}
max_min(st, 10, & stmax, &stmin);
printf ( … ) ;
printf ( … );
/ * 以上两个输出语句与例 1 相同 * /
}
注:在函数 max_min 中,访问 stmax 和 stmin 所指向的结构体变量中的成员用“ - > ”而不能用“ . ”。
例 4 将例 2 改为用函数实现。
void sort (STU *st, int n)
{
int i, j;
STU t;
for (i = 0; i
for (j = i+1; j
if ((st + i)->total<(st + j)->total)
{
t = * (st + i);
* (st + i) = * (st + j);
* (st + j) = t;
}
}
有了此函数的定义,即可将例 2 中的排序部分语句改为调用此函数。
4 .用动态分配内存空间存放结构体类型数据
如果将例 2 中的学生人数改为 n 个,则 st 不宜定义为结构体数组(因长度未定),应使用动态分配内存空间的方法,即将 st 定义为指向结构体类型的指针变量,然后用 malloc 函数申请一片内存空间,并将其首地址赋给 st ,即相应部分语句改为:
# include
…
main ( )
{
int i, n;
STU *st;
scanf ( ″ %d ″ , &n) ;
st = malloc (n * sizeof (STU)) ;
if (! st ) exit (0);
…
free (st) ;
}
结构体
作者:佚名 来源:本站原创 点击: 更新时间:2004-6-17
stmin. total = 32767;
for (i = 0; i < 10; i++)
{
printf ( ″ /n Enter data : ″ ) ;
scanf ( ″%d %s %d %d %d″,&st.number,st.name,&st.math,&st.english,&st.computer);
st.total = st. math + st. english+st.computer;
if (st.total>stmax. total) stmax = st;
if (st. total
}
printf ( ″ /n max :%5d %15 %4d %4d %4d %4d ″,
stmax.number, stmax.name, stmax.math, stmax.english, stmax.computer, stmax.total);
printf ( ″ /n min :%5d %15s %4d %4d %4d %4d ″,
stmin.number, stmin.name, stmin.math, stmin.english, stmin.computer, stmin.total);
}
2 .结构体类型数组的定义及引用
对于结构体类型数组的定义,同样先定义结构体类型,然后用此类型去定义结构体数组。
例 2 输入 10 个学生的数据,然后按总分从高到低的顺序排序后输出(每个学生的数据同例 1 )。
为方便起见,可用宏定义指令将 struct student 用一个宏 STU 代替 ( 见程序第 1 行)。
#define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
}
main ( )
{
int i, j;
STU st [10], t;
/ * 以上是定义结构体数组 st 及结构体变量 t * /
printf ( ″ /n Enter data : ″ ) ;
for (i = 0 ; i<10 ; i++)
{
scanf ( ″ %d%s%d%d%d ″ , & st [i].number, st [i].name, & st [i].math,
& st [i].english, & st [i].computer);
st [i].total = st [i].math + st [i].english + st [i].computer;
}
for (i = 0; i<10; i++)
for (j = i + 1; j<10; j++)
if (st [i].total < st [j]. total)
{ t = st [i]; st [i]=st [j]; st [j]=t;}
for ( i= 0; i<10; i++)
printf ( ″ /n %5d%15s%4d%4d%4d ″ ,
st[i]. number,st[i].name,st[i].math
st[i].english,st[i].computer,st[i].total);
}
请详细阅读以上程序,掌握结构体数组的用法,体会用结构体作数据结构的优越性。
3 .用指向结构体的指针作函数参数
如果直接用结构体变量作函数参数,则数据传递是一种值传递方式,不能在函数中通过形参变量(结构体变量)改变实参变量(结构体变量)的值。用指向结构体的指针作函数参数可解决此问题。
用指向结构体的指针变量作函数参数,最常见的用法有两种:
① 用于接受实参变量(结构体变量)的地址,从而在函数中可以通过指针变量间接地访问实参变量所在的内存单元,以达到双向传递的效果(见例 3 )。
② 用于接受实参数组(结构体数组)的首地址,从而在函数中可以通过移动该指针变量的指向(或通过指针运算)间接地访问实参数组各元素所在的内存单元(见例 4 )。
例 3 将例 1 改为用函数实现。
# define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
};
void max _ min (STU st[ ], int n, STU *stmax, STU *stmin)
{
int i;
* stmax = st [0];
* stmin = st [0];
for (i = 1; i
{
if (st [i]. total > stmax - > total) *stmax = st [i];
if (st [i]. total < stmin - > total) *stmin = st [i];
}
}
main ( )
{
int i;
STU st [10], stmax, stmin;
for (i = 0; i<10; i++)
{
scanf ( … ); / * 此输入语句与例 2 相同 * /
st [i]. total = st [i]. math + st [i]. english+st[i]. computer;
}
max_min(st, 10, & stmax, &stmin);
printf ( … ) ;
printf ( … );
/ * 以上两个输出语句与例 1 相同 * /
}
注:在函数 max_min 中,访问 stmax 和 stmin 所指向的结构体变量中的成员用“ - > ”而不能用“ . ”。
例 4 将例 2 改为用函数实现。
void sort (STU *st, int n)
{
int i, j;
STU t;
for (i = 0; i
for (j = i+1; j
if ((st + i)->total<(st + j)->total)
{
t = * (st + i);
* (st + i) = * (st + j);
* (st + j) = t;
}
}
有了此函数的定义,即可将例 2 中的排序部分语句改为调用此函数。
4 .用动态分配内存空间存放结构体类型数据
如果将例 2 中的学生人数改为 n 个,则 st 不宜定义为结构体数组(因长度未定),应使用动态分配内存空间的方法,即将 st 定义为指向结构体类型的指针变量,然后用 malloc 函数申请一片内存空间,并将其首地址赋给 st ,即相应部分语句改为:
# include
…
main ( )
{
int i, n;
STU *st;
scanf ( ″ %d ″ , &n) ;
st = malloc (n * sizeof (STU)) ;
if (! st ) exit (0);
…
free (st) ;
}
结构体
作者:佚名 来源:本站原创 点击: 更新时间:2004-6-17
stmin. total = 32767;
for (i = 0; i < 10; i++)
{
printf ( ″ /n Enter data : ″ ) ;
scanf ( ″%d %s %d %d %d″,&st.number,st.name,&st.math,&st.english,&st.computer);
st.total = st. math + st. english+st.computer;
if (st.total>stmax. total) stmax = st;
if (st. total
}
printf ( ″ /n max :%5d %15 %4d %4d %4d %4d ″,
stmax.number, stmax.name, stmax.math, stmax.english, stmax.computer, stmax.total);
printf ( ″ /n min :%5d %15s %4d %4d %4d %4d ″,
stmin.number, stmin.name, stmin.math, stmin.english, stmin.computer, stmin.total);
}
2 .结构体类型数组的定义及引用
对于结构体类型数组的定义,同样先定义结构体类型,然后用此类型去定义结构体数组。
例 2 输入 10 个学生的数据,然后按总分从高到低的顺序排序后输出(每个学生的数据同例 1 )。
为方便起见,可用宏定义指令将 struct student 用一个宏 STU 代替 ( 见程序第 1 行)。
#define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
}
main ( )
{
int i, j;
STU st [10], t;
/ * 以上是定义结构体数组 st 及结构体变量 t * /
printf ( ″ /n Enter data : ″ ) ;
for (i = 0 ; i<10 ; i++)
{
scanf ( ″ %d%s%d%d%d ″ , & st [i].number, st [i].name, & st [i].math,
& st [i].english, & st [i].computer);
st [i].total = st [i].math + st [i].english + st [i].computer;
}
for (i = 0; i<10; i++)
for (j = i + 1; j<10; j++)
if (st [i].total < st [j]. total)
{ t = st [i]; st [i]=st [j]; st [j]=t;}
for ( i= 0; i<10; i++)
printf ( ″ /n %5d%15s%4d%4d%4d ″ ,
st[i]. number,st[i].name,st[i].math
st[i].english,st[i].computer,st[i].total);
}
请详细阅读以上程序,掌握结构体数组的用法,体会用结构体作数据结构的优越性。
3 .用指向结构体的指针作函数参数
如果直接用结构体变量作函数参数,则数据传递是一种值传递方式,不能在函数中通过形参变量(结构体变量)改变实参变量(结构体变量)的值。用指向结构体的指针作函数参数可解决此问题。
用指向结构体的指针变量作函数参数,最常见的用法有两种:
① 用于接受实参变量(结构体变量)的地址,从而在函数中可以通过指针变量间接地访问实参变量所在的内存单元,以达到双向传递的效果(见例 3 )。
② 用于接受实参数组(结构体数组)的首地址,从而在函数中可以通过移动该指针变量的指向(或通过指针运算)间接地访问实参数组各元素所在的内存单元(见例 4 )。
例 3 将例 1 改为用函数实现。
# define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
};
void max _ min (STU st[ ], int n, STU *stmax, STU *stmin)
{
int i;
* stmax = st [0];
* stmin = st [0];
for (i = 1; i
{
if (st [i]. total > stmax - > total) *stmax = st [i];
if (st [i]. total < stmin - > total) *stmin = st [i];
}
}
main ( )
{
int i;
STU st [10], stmax, stmin;
for (i = 0; i<10; i++)
{
scanf ( … ); / * 此输入语句与例 2 相同 * /
st [i]. total = st [i]. math + st [i]. english+st[i]. computer;
}
max_min(st, 10, & stmax, &stmin);
printf ( … ) ;
printf ( … );
/ * 以上两个输出语句与例 1 相同 * /
}
注:在函数 max_min 中,访问 stmax 和 stmin 所指向的结构体变量中的成员用“ - > ”而不能用“ . ”。
例 4 将例 2 改为用函数实现。
void sort (STU *st, int n)
{
int i, j;
STU t;
for (i = 0; i
for (j = i+1; j
if ((st + i)->total<(st + j)->total)
{
t = * (st + i);
* (st + i) = * (st + j);
* (st + j) = t;
}
}
有了此函数的定义,即可将例 2 中的排序部分语句改为调用此函数。
4 .用动态分配内存空间存放结构体类型数据
如果将例 2 中的学生人数改为 n 个,则 st 不宜定义为结构体数组(因长度未定),应使用动态分配内存空间的方法,即将 st 定义为指向结构体类型的指针变量,然后用 malloc 函数申请一片内存空间,并将其首地址赋给 st ,即相应部分语句改为:
# include
…
main ( )
{
int i, n;
STU *st;
scanf ( ″ %d ″ , &n) ;
st = malloc (n * sizeof (STU)) ;
if (! st ) exit (0);
…
free (st) ;
}
结构体
作者:佚名 来源:本站原创 点击: 更新时间:2004-6-17
stmin. total = 32767;
for (i = 0; i < 10; i++)
{
printf ( ″ /n Enter data : ″ ) ;
scanf ( ″%d %s %d %d %d″,&st.number,st.name,&st.math,&st.english,&st.computer);
st.total = st. math + st. english+st.computer;
if (st.total>stmax. total) stmax = st;
if (st. total
}
printf ( ″ /n max :%5d %15 %4d %4d %4d %4d ″,
stmax.number, stmax.name, stmax.math, stmax.english, stmax.computer, stmax.total);
printf ( ″ /n min :%5d %15s %4d %4d %4d %4d ″,
stmin.number, stmin.name, stmin.math, stmin.english, stmin.computer, stmin.total);
}
2 .结构体类型数组的定义及引用
对于结构体类型数组的定义,同样先定义结构体类型,然后用此类型去定义结构体数组。
例 2 输入 10 个学生的数据,然后按总分从高到低的顺序排序后输出(每个学生的数据同例 1 )。
为方便起见,可用宏定义指令将 struct student 用一个宏 STU 代替 ( 见程序第 1 行)。
#define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
}
main ( )
{
int i, j;
STU st [10], t;
/ * 以上是定义结构体数组 st 及结构体变量 t * /
printf ( ″ /n Enter data : ″ ) ;
for (i = 0 ; i<10 ; i++)
{
scanf ( ″ %d%s%d%d%d ″ , & st [i].number, st [i].name, & st [i].math,
& st [i].english, & st [i].computer);
st [i].total = st [i].math + st [i].english + st [i].computer;
}
for (i = 0; i<10; i++)
for (j = i + 1; j<10; j++)
if (st [i].total < st [j]. total)
{ t = st [i]; st [i]=st [j]; st [j]=t;}
for ( i= 0; i<10; i++)
printf ( ″ /n %5d%15s%4d%4d%4d ″ ,
st[i]. number,st[i].name,st[i].math
st[i].english,st[i].computer,st[i].total);
}
请详细阅读以上程序,掌握结构体数组的用法,体会用结构体作数据结构的优越性。
3 .用指向结构体的指针作函数参数
如果直接用结构体变量作函数参数,则数据传递是一种值传递方式,不能在函数中通过形参变量(结构体变量)改变实参变量(结构体变量)的值。用指向结构体的指针作函数参数可解决此问题。
用指向结构体的指针变量作函数参数,最常见的用法有两种:
① 用于接受实参变量(结构体变量)的地址,从而在函数中可以通过指针变量间接地访问实参变量所在的内存单元,以达到双向传递的效果(见例 3 )。
② 用于接受实参数组(结构体数组)的首地址,从而在函数中可以通过移动该指针变量的指向(或通过指针运算)间接地访问实参数组各元素所在的内存单元(见例 4 )。
例 3 将例 1 改为用函数实现。
# define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
};
void max _ min (STU st[ ], int n, STU *stmax, STU *stmin)
{
int i;
* stmax = st [0];
* stmin = st [0];
for (i = 1; i
{
if (st [i]. total > stmax - > total) *stmax = st [i];
if (st [i]. total < stmin - > total) *stmin = st [i];
}
}
main ( )
{
int i;
STU st [10], stmax, stmin;
for (i = 0; i<10; i++)
{
scanf ( … ); / * 此输入语句与例 2 相同 * /
st [i]. total = st [i]. math + st [i]. english+st[i]. computer;
}
max_min(st, 10, & stmax, &stmin);
printf ( … ) ;
printf ( … );
/ * 以上两个输出语句与例 1 相同 * /
}
注:在函数 max_min 中,访问 stmax 和 stmin 所指向的结构体变量中的成员用“ - > ”而不能用“ . ”。
例 4 将例 2 改为用函数实现。
void sort (STU *st, int n)
{
int i, j;
STU t;
for (i = 0; i
for (j = i+1; j
if ((st + i)->total<(st + j)->total)
{
t = * (st + i);
* (st + i) = * (st + j);
* (st + j) = t;
}
}
有了此函数的定义,即可将例 2 中的排序部分语句改为调用此函数。
4 .用动态分配内存空间存放结构体类型数据
如果将例 2 中的学生人数改为 n 个,则 st 不宜定义为结构体数组(因长度未定),应使用动态分配内存空间的方法,即将 st 定义为指向结构体类型的指针变量,然后用 malloc 函数申请一片内存空间,并将其首地址赋给 st ,即相应部分语句改为:
# include
…
main ( )
{
int i, n;
STU *st;
scanf ( ″ %d ″ , &n) ;
st = malloc (n * sizeof (STU)) ;
if (! st ) exit (0);
…
free (st) ;
}
结构体
作者:佚名 来源:本站原创 点击: 更新时间:2004-6-17
stmin. total = 32767;
for (i = 0; i < 10; i++)
{
printf ( ″ /n Enter data : ″ ) ;
scanf ( ″%d %s %d %d %d″,&st.number,st.name,&st.math,&st.english,&st.computer);
st.total = st. math + st. english+st.computer;
if (st.total>stmax. total) stmax = st;
if (st. total
}
printf ( ″ /n max :%5d %15 %4d %4d %4d %4d ″,
stmax.number, stmax.name, stmax.math, stmax.english, stmax.computer, stmax.total);
printf ( ″ /n min :%5d %15s %4d %4d %4d %4d ″,
stmin.number, stmin.name, stmin.math, stmin.english, stmin.computer, stmin.total);
}
2 .结构体类型数组的定义及引用
对于结构体类型数组的定义,同样先定义结构体类型,然后用此类型去定义结构体数组。
例 2 输入 10 个学生的数据,然后按总分从高到低的顺序排序后输出(每个学生的数据同例 1 )。
为方便起见,可用宏定义指令将 struct student 用一个宏 STU 代替 ( 见程序第 1 行)。
#define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
}
main ( )
{
int i, j;
STU st [10], t;
/ * 以上是定义结构体数组 st 及结构体变量 t * /
printf ( ″ /n Enter data : ″ ) ;
for (i = 0 ; i<10 ; i++)
{
scanf ( ″ %d%s%d%d%d ″ , & st [i].number, st [i].name, & st [i].math,
& st [i].english, & st [i].computer);
st [i].total = st [i].math + st [i].english + st [i].computer;
}
for (i = 0; i<10; i++)
for (j = i + 1; j<10; j++)
if (st [i].total < st [j]. total)
{ t = st [i]; st [i]=st [j]; st [j]=t;}
for ( i= 0; i<10; i++)
printf ( ″ /n %5d%15s%4d%4d%4d ″ ,
st[i]. number,st[i].name,st[i].math
st[i].english,st[i].computer,st[i].total);
}
请详细阅读以上程序,掌握结构体数组的用法,体会用结构体作数据结构的优越性。
3 .用指向结构体的指针作函数参数
如果直接用结构体变量作函数参数,则数据传递是一种值传递方式,不能在函数中通过形参变量(结构体变量)改变实参变量(结构体变量)的值。用指向结构体的指针作函数参数可解决此问题。
用指向结构体的指针变量作函数参数,最常见的用法有两种:
① 用于接受实参变量(结构体变量)的地址,从而在函数中可以通过指针变量间接地访问实参变量所在的内存单元,以达到双向传递的效果(见例 3 )。
② 用于接受实参数组(结构体数组)的首地址,从而在函数中可以通过移动该指针变量的指向(或通过指针运算)间接地访问实参数组各元素所在的内存单元(见例 4 )。
例 3 将例 1 改为用函数实现。
# define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
};
void max _ min (STU st[ ], int n, STU *stmax, STU *stmin)
{
int i;
* stmax = st [0];
* stmin = st [0];
for (i = 1; i
{
if (st [i]. total > stmax - > total) *stmax = st [i];
if (st [i]. total < stmin - > total) *stmin = st [i];
}
}
main ( )
{
int i;
STU st [10], stmax, stmin;
for (i = 0; i<10; i++)
{
scanf ( … ); / * 此输入语句与例 2 相同 * /
st [i]. total = st [i]. math + st [i]. english+st[i]. computer;
}
max_min(st, 10, & stmax, &stmin);
printf ( … ) ;
printf ( … );
/ * 以上两个输出语句与例 1 相同 * /
}
注:在函数 max_min 中,访问 stmax 和 stmin 所指向的结构体变量中的成员用“ - > ”而不能用“ . ”。
例 4 将例 2 改为用函数实现。
void sort (STU *st, int n)
{
int i, j;
STU t;
for (i = 0; i
for (j = i+1; j
if ((st + i)->total<(st + j)->total)
{
t = * (st + i);
* (st + i) = * (st + j);
* (st + j) = t;
}
}
有了此函数的定义,即可将例 2 中的排序部分语句改为调用此函数。
4 .用动态分配内存空间存放结构体类型数据
如果将例 2 中的学生人数改为 n 个,则 st 不宜定义为结构体数组(因长度未定),应使用动态分配内存空间的方法,即将 st 定义为指向结构体类型的指针变量,然后用 malloc 函数申请一片内存空间,并将其首地址赋给 st ,即相应部分语句改为:
# include
…
main ( )
{
int i, n;
STU *st;
scanf ( ″ %d ″ , &n) ;
st = malloc (n * sizeof (STU)) ;
if (! st ) exit (0);
…
free (st) ;
}
结构体
作者:佚名 来源:本站原创 点击: 更新时间:2004-6-17
stmin. total = 32767;
for (i = 0; i < 10; i++)
{
printf ( ″ /n Enter data : ″ ) ;
scanf ( ″%d %s %d %d %d″,&st.number,st.name,&st.math,&st.english,&st.computer);
st.total = st. math + st. english+st.computer;
if (st.total>stmax. total) stmax = st;
if (st. total
}
printf ( ″ /n max :%5d %15 %4d %4d %4d %4d ″,
stmax.number, stmax.name, stmax.math, stmax.english, stmax.computer, stmax.total);
printf ( ″ /n min :%5d %15s %4d %4d %4d %4d ″,
stmin.number, stmin.name, stmin.math, stmin.english, stmin.computer, stmin.total);
}
2 .结构体类型数组的定义及引用
对于结构体类型数组的定义,同样先定义结构体类型,然后用此类型去定义结构体数组。
例 2 输入 10 个学生的数据,然后按总分从高到低的顺序排序后输出(每个学生的数据同例 1 )。
为方便起见,可用宏定义指令将 struct student 用一个宏 STU 代替 ( 见程序第 1 行)。
#define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
}
main ( )
{
int i, j;
STU st [10], t;
/ * 以上是定义结构体数组 st 及结构体变量 t * /
printf ( ″ /n Enter data : ″ ) ;
for (i = 0 ; i<10 ; i++)
{
scanf ( ″ %d%s%d%d%d ″ , & st [i].number, st [i].name, & st [i].math,
& st [i].english, & st [i].computer);
st [i].total = st [i].math + st [i].english + st [i].computer;
}
for (i = 0; i<10; i++)
for (j = i + 1; j<10; j++)
if (st [i].total < st [j]. total)
{ t = st [i]; st [i]=st [j]; st [j]=t;}
for ( i= 0; i<10; i++)
printf ( ″ /n %5d%15s%4d%4d%4d ″ ,
st[i]. number,st[i].name,st[i].math
st[i].english,st[i].computer,st[i].total);
}
请详细阅读以上程序,掌握结构体数组的用法,体会用结构体作数据结构的优越性。
3 .用指向结构体的指针作函数参数
如果直接用结构体变量作函数参数,则数据传递是一种值传递方式,不能在函数中通过形参变量(结构体变量)改变实参变量(结构体变量)的值。用指向结构体的指针作函数参数可解决此问题。
用指向结构体的指针变量作函数参数,最常见的用法有两种:
① 用于接受实参变量(结构体变量)的地址,从而在函数中可以通过指针变量间接地访问实参变量所在的内存单元,以达到双向传递的效果(见例 3 )。
② 用于接受实参数组(结构体数组)的首地址,从而在函数中可以通过移动该指针变量的指向(或通过指针运算)间接地访问实参数组各元素所在的内存单元(见例 4 )。
例 3 将例 1 改为用函数实现。
# define STU struct student
STU
{
int number;
char name [20];
int math;
int english;
int computer;
int total;
};
void max _ min (STU st[ ], int n, STU *stmax, STU *stmin)
{
int i;
* stmax = st [0];
* stmin = st [0];
for (i = 1; i
{
if (st [i]. total > stmax - > total) *stmax = st [i];
if (st [i]. total < stmin - > total) *stmin = st [i];
}
}
main ( )
{
int i;
STU st [10], stmax, stmin;
for (i = 0; i<10; i++)
{
scanf ( … ); / * 此输入语句与例 2 相同 * /
st [i]. total = st [i]. math + st [i]. english+st[i]. computer;
}
max_min(st, 10, & stmax, &stmin);
printf ( … ) ;
printf ( … );
/ * 以上两个输出语句与例 1 相同 * /
}
注:在函数 max_min 中,访问 stmax 和 stmin 所指向的结构体变量中的成员用“ - > ”而不能用“ . ”。
例 4 将例 2 改为用函数实现。
void sort (STU *st, int n)
{
int i, j;
STU t;
for (i = 0; i
for (j = i+1; j
if ((st + i)->total<(st + j)->total)
{
t = * (st + i);
* (st + i) = * (st + j);
* (st + j) = t;
}
}
有了此函数的定义,即可将例 2 中的排序部分语句改为调用此函数。
4 .用动态分配内存空间存放结构体类型数据
如果将例 2 中的学生人数改为 n 个,则 st 不宜定义为结构体数组(因长度未定),应使用动态分配内存空间的方法,即将 st 定义为指向结构体类型的指针变量,然后用 malloc 函数申请一片内存空间,并将其首地址赋给 st ,即相应部分语句改为:
# include
…
main ( )
{
int i, n;
STU *st;
scanf ( ″ %d ″ , &n) ;
st = malloc (n * sizeof (STU)) ;
if (! st ) exit (0);
…
free (st) ;
}