结构指针变量的说明和使用:
一个指针变量当用来指向一个结构变量时,称之为结构指针变量。结构指针变量中的值是所指向的结构变量的首地址。
通过结构指针可访问该结构变量,这与数组指针和函数指针的情况是相同的
结构指针变量的说明的一般形式:
struct 结构名 结构指针变量名
例如:
struct stu pstu;
结构指针变量是要先赋值后才能使用。赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。
结构指针变量的一般访问形式:
(*结构指针变量).成员名
或者:结构指针变量->成员名
例子1:
struct stu {
int num;
char *name;
char sex;
float score;
}boy1 = {102,"Zhang ping",'M',78.5},*pstu;
int main()
{
pstu = &boy1;//把结构变量的boy1的地址赋值给pstu
printf("Number=%d\nName=%s\n", boy1.num, boy1.name);
printf("Sex=%c\nScore=%f\n\n", boy1.sex, boy1.score);
printf("Number=%d\nName=%s\n", (*pstu).num, (*pstu).name);
printf("Sex=%c\nScore%f\n\n", (*pstu).sex, (*pstu).score);
printf("Number=%d\nName=%s\n", pstu->num, pstu->name);
printf("Sex=%c\nScore=%f", pstu->sex, pstu->score);
return 0;
}
结构指针变量作函数参数
在ANSI的c标准中,是允许使用结构变量作为函数参数进行整体传送的。但是这种传送要将全部成员逐个传送,但是会花费很
的时间和空间。因此使用指针来进行传送更好
例子2:计算一组学生的平均成绩和不及格人数。使用结构指针变量作函数参数编程
struct stu
{
int num;
char *name;
char sex;
float score;
}boy[5] = {
{101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He feng",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58}
};
int main()
{
struct stu *ps;
void ave(struct stu *ps);
ps = boy;
ave(ps);
return 0;
}
void ave(struct stu *ps)
{
int c = 0;
float ave = 0,s=0;
for (int i = 0; i < 5; i++, ps++)
{
s += ps->score;
if ((ps->score) < 60)
{
c += 1;
}
}
printf("s=%f\n", s);
ave = s / 5;
printf("average=%f\ncount=%d\n", ave, c);
}
动态存储分配;
在c语言中不允许动态数组类型
c语言提供了一些内存管理函数,主要有三种:
分配内存空间函数malloc:
调用的形式:(类型说明符)malloc(size)
功能:在内存的动态存储区中分配一块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。
“类型说明符”表示把该区域用于何种数据类型。
(类型说明符)表示把返回值强制转换为该类型指针。
分配内存空间函数:calloc
调用形式:(类型说明符)calloc(n,size)
功能:在内存动态存储区中分配n块长度为:“size”字节的连续区域。函数的返回值是为该函数的首地址。
(类型说明符)用于强制类型转换。
calloc函数与malloc函数的去区别仅在于一次可以分配n块区域。
例如:ps=(struct stu*)calloc(2,sizeof(struct stu);-->其中siezeof(struct stu)是求stu的结构长度。因此该语句的意思:
按照stu的长度分配2块连续区域,强制转换为stu类型,并把其首地址赋予指针变量ps.
释放内存空间函数free
调用形式:free(void *ptr):释放ptr所指向的一块内存空间,ptu是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区
应是由malloc或者calloc函数所分配的区域。
例子3:分配一个块区域,输入一个学生数据
int main()
{
struct stu
{
int num;
char *name;
char sex;
float score;
}*ps;//ps这个指针变量指向一个stu结构的首地址
ps = (struct stu*)malloc(sizeof(struct stu));
ps->num = 102;
ps->name = "Zhang ping";
ps->sex = 'M';
ps->score = 62.5;
printf("Num=%d\nName=%s\n", ps->num, ps->name);
printf("Sex=%c\nScore=%f\n", ps->sex, ps->score);
free(ps);
return 0;
}
链表的概念:即就是动态存储的办法,我们来存放一个学生的数据是,可以分配一块可用空间来存放,我也称之这样一块存储的空间为
一个节点,我们存放一个学生数据就分配一个节点,无须预先确定学生的准确人数,某学生退学,可以删去该节点,并释放该节点占用的空间
使用动态分配时,每个节点之间可以是不连续(结点内部之间是连续的),而结点之间的连续可以用指针来实现。即
在结点结构中定义一个成员项用来存放下一个结点的首地址,这个用于存放地址的成员,常把它称为指针域。
我们可以在结点的指针域中存放第二个结点的首地址,在第二个结点的指针域内与存放第三个结点的首地址,如此串联下去直到最后一个结点
最后一个结点因无后续结点连接,其指针域可赋值为0.这样的一种连接方式,在数据结构中称为:“链表”。
例如:一个存放学生学号和成绩的结点应为一下结构:
struct stu
{
int num;
int score;
struct stu *next;
};//前面两个成员组成数据域,后一个成员项next构成指针域,他是一个指向stu类型结构的指针变量。
链表的基本操作对链表的主要操作有一下几种:
1.建立链表;
2.结构的查找与输出;
3.插入一个结点;
4.删除一个结点;
例子4:建立一个三个结点的链表,存放学生的数据。为了简单起见,我们假定学生数据结构中只有学号和年龄两项。可编写一个建立链表的函数creat。
#define NULL 0
#define TYPE struct stu
#define LEN sizeof(struct stu)
struct stu
{
int num;
int age;
struct stu *next;
};
TYPE *creat(int n)
{
struct stu *head, *pf, *pb;
int i;
for (i = 0; i < n; i++)
{
pb = (TYPE*)malloc(LEN);
printf("input Number and Age:\n");
scanf_s("%d%d", &pb->num, &pb->age);
if (i == 0)
{
pf = head = pb;
}
else
{
pf->next = pb;
pb->next = NULL;
pf = pb;
}
return (head);
}
}
在函数外首先用宏定义对三个符号常量作了定义。这样用TYPE表示struct stu,用LEN表示sizeof(struct stu)主要的目的是为了
在一下程序内减少书写并使阅读更加方便。结构stu定义为外部类型,程序中的各个函数均可使用该定义。
creat函数用于建立一个有n 个结点的链表,它是一个指针函数,他返回的指针指向stu结构。在creat函数内定义了三个stu结构的指针变量
head为头指针,pf为指向两相邻结点的前一结点的指针变量,bp为后指针变量
创建一个有n个结点的链表
创建链表:
typedef struct student {
int score;
struct student *next;
}LinkList;
一般我们创建链表都用typedef struct,因为这样定义结构体变量时,我们就可以直接使用LinkList,*a定义的结构体类型变量了。
初始化一个链表,n为链表结点个数。
LinkList *creat(int n)
{
LinkList *head,*node ,*end;
head = (LinkList*)malloc(sizeof(LinkList));//为头结点分配内存空间
end = head;
for (int i = 0; i < n; i++)
{
node = (LinkList*)malloc(sizeof(LinkList));//为不同结点分配内存空间
scanf_s("%d\n", &node->score);
end->next = node;//把当前结点的地址给到下一个结构体结点end的指针域
end = node;
}
end->next = NULL;
return head;
}
修改链表的结点值:
void change(LinkList *list, int n)
{
LinkList *t = list;
int i = 0;
while (i < n && t!= NULL)
{
t = t->next;//通过这一步跳到下一个节点
i++;
}
if (t != NULL)
{
puts("输入要修改的值\n");
scanf_s("%d\n", &t->score);
}
else
{
puts("节点不存在");
}
}
删除链表结点:也就是把前节点的指针域越过要删除的节点指向下下一个节点。
void delet(LinkList *list, int n)
{
LinkList *t = list, *in;
int i = 0;
while (i < n && t != NULL)
{
in = t;
t = t->next;//通过这一步跳到下一个节点
i++;
}
if (t != NULL)
{
in->next = t->next;
free(t);
}
else
{
puts("结点不存在");
}
}
插入链表节点:就是把插入前的head指针域即head->next赋值给想要插入的这个结点的指针域(假设插入的这个节点为e,即e->next = head->next)
然后再把插入的结点的首地址赋值给在head的指针域:head->next = e;
void insert(LinkList *list, int n)
{
LinkList *t = list, *in;
int i = 0;
while (i < n && t != NULL)
{
t = t->next;
i++;
}
if (t != NULL)
{
in = (LinkList*)malloc(sizeof(LinkList));
puts("请输入要插入的值");
scanf_s("%d", &in->score);
in->next = t->next;
t->next = in;
}
else
{
puts("节点不存在");
}
}
输出链表:通过遍历输出链表
while (h->next != NULL)
{
h = h->next;
printf("%d\n", h->score);
}