chapter 9用户自己建立数据类型(中)

简介: chapter 9用户自己建立数据类型

9.3 结构体指针


每个结构体都有一个地址,定义一个指针变量,他只能储存结构体类型的地址的话,我称之为结构体指针。结构体指针可以指向结构体。


9.3.1指向结构体变量的指针


首先咱们看一个例子;


/*通过指向结构体变量的指针变量输出结构体变量中的相关信息*/
#include <stdio.h>
#include <string.h>
int main()
{
  struct student
  {
  long num;
  char name[20];
  char sex;
  float  score;
  };//定义一个结构体;
  struct student stu_1; //定义一个结构体变量
  struct student *p;//定义一个指针变量指向结构体
  p=&stu_1;
  stu_1.num=10101;
  strcpy(stu_1.name,"li lin");//字符串赋值函数;把"li lin"赋值stu.1.name;
  stu_1.sex='M';
  stu_1.score=89.5;
  printf("no.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n",stu_1.num,stu_1.name,stu_1.sex,stu_1.score);
  printf("no.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n",(*p).num,(*p).name,(*p).sex,(*p).score);//
  return 0;
} 
(*p).num;p是变量名字,包含地址,*p意思是指向结构体()优先级大于.num,
所以说先指向某一个结构体,然后就是找到里面对应的num;
 如果没有括号,就是.num优先于*运算符,所以显示p.num;p是变量名字;
 所以p.num没有意义;所以说必须加括号;


以下三种情况等价:

**

stu.num stu;本身相当于一个抽象的变量,所以就不代表地址的意思,直接就是数值

(*p).num;

p->num;->指向运算符。前面是地址,后面是具体那个元素;

**


9.3.2指向结构体数组的指针


#include <stdio.h>
struct student 
{
  int num;
  char name [20];
  char sex;
  int age;
};
struct student stu[3]={{10101,"lilin",'M',18},{10102,"ZHANG FANG",'M',19},{10104,"WANGMIN",'F',20}};
int main()
{
  struct student*p;
  printf("no.name                                 sex age\n");
  for(p=stu;p<stu+3;p++)
  printf("%5d %-20s %2c %4d\n",p->num,p->name,p->sex,p->age);
  return 0;
}


9.4 用指针处理链表


9.4.1 什么是链表


在前面的学习中,当我们处理数据,比如ABCD苏格版每个班级的人数都不一样,我们处理数据的方法是建立一个数组,但是也存在一定的问题,我们建立一个数组的时候,只能建立最长的那个班级人数的数组,这样相对于其他班级人数少的时候,是不是对于数组来说就造成储存空间的一种浪费。那么我们能不能建立一种动态储存空间,能随班级的人数来动态分配内存,能够需要来动态建立储存单元的结构叫做链表。

1670499401791.jpg

如图所示,链表必须要有一个

头指针 :只用来存放地址;

表尾:存放一个数据,而且其中存放指针地址的区域存放空指针,不知想任何类型。


看这个链表,去掉一头一尾部,剩下的中间那些称之为一个又一个的节点。

看这些节点,它包括两个东西:

a.用户需要用的实际数据

b.下一个节点的地址;


所以我们看一看链表有什么特点:

1.看上图,链表各节点之间的地址是可以不连续的。

2.结构体变量,用它去建立链表是最合适的。

3.在结构体中定义的某一个指针,根据定义的在指针类型,可以指向自己所在的结构体数据,也可以指向其他类型的结构体数据。

我们看一下:

struct student
{
  int num;
  float score;
  struct student *next;//定义的结构体上面两行就是所谓的实际数据,下面指针定义的就是只能只想自己的数据类型。
}


9.4.2建立简单的静态链表


我们看一个例子,建立一个静态链表:

/*建立一个简单链表,有三个同学的数据节点组成,要求输出节点的数据*/ 
#include <stdio.h>
struct student
{
   int num;
   float score;
   struct student *next;//地址指向下一个节点的关键。
} ;//建立一个结构体,可以用来充当各个节点了。
int main()
{
  struct student a,b,c,*head,*p;
   a.num=10101;a.score=89.5;
   b.num=10103;b.score=90;
   c.num=10107;c.score=85;
   head=&a;
   a.next=&b;
   b.next=&c;
   c.next=NULL;
   p=head;
   do
   {
    printf("%ld%5.1f\n",p->num,p->score);//p是地址,->只想运算符,p->意思就是指向b这个结构体了,然后·取num的数值。
    p=p->next;//p是地址,->只想运算符,p->意思就是指向b这个结构体了,然后取next值
    } while(p!=NULL);
   return 0;
 }

1670499452130.jpg

1670499460437.jpg


9.4.3 建立动态链表


1670499472516.jpg

1670499487955.jpg

/*写一个函数建立一个有三个学生数据的单向动态链表*/
#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct student) //测量字节
struct student
{
 long num;
 float score;
 struct student*next;
};
int n;
struct student *creat(void)
{
 struct student*head;
 struct student*p1,*p2;
 n=0;
 p1=p2=(struct student*)malloc(LEN);//建立一个动态储存区返回一个地址,这个动态储存区的长度为LEN;
 scanf("%ld,%f",&p1->num,&p1->score);
 head=NULL;
 while(p1->num!=0)
 {
  n=n+1;
  if(n==1)head=p1;
  else p2->next=p1; 
  p2=p1;
  p1=(struct student*)malloc(LEN);
  scanf("%ld,%f",&p1->num,&p1->score);
  } 
  p2->next=NULL;
  return(head);
}
int main()
{
 struct student *pt;
 pt=creat();
 printf("\nnum:%ld\nscore:%5.1f\n",pt->num,pt->score);
 return 0;
}

里面出现了n.一开始n=0,然后n=1;n=2,3,4;

n=0;还没有建立链表;

n=1;建立了一个链表节段;赋值时把p1给head,让p2等于p1;

n=2;建立了第二个链表节段;赋值时让p1等于上一个变量中的next,p2等于此时的p1;

所以n可以作为建立链数的标志。


9.4.4输出链表


首先按一个例子,编出一个输出链表的函数。

#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct student)//用sizeof 测量结构体的长度字节,让LEN恒等于这个字节。
struct student
{
   long num;
   float score;
   struct student *next;
};//定义一个结构体。包含两个数据,和一个指针。
int n;
void print(struct student *head)//定义一个函数。形参定义为 struct student *head .输入一个结构体变量的地址。让他传递到这个函数。
{
   struct student *p;//定义一个为结构体变量
   printf("\nnow,these.%d records are :\n",n);//输出n
   p=head;
   if(head!=NULL)
   {
    do
    {
      printf("%ld %d5.1f\n",p->num,p->score);
      p=p->next;
    }while(p!=NULL); 
   }
}
整体将这个链表输出。直到遇到NULL为止。

也就是说在print 函数里面

do
    {
      printf("%ld %d5.1f\n",p->num,p->score);
      p=p->next;
    }while(p!=NULL);


这就是输出链表的关键,用do while 循环。也可以用while 循环。这里不需要计数,只要遇到NULL就行。所以用 do-while 和 while循环是比较好的。


我们把上面的一道例题和这一道立体综合起来。

/*建立一个动态链表,然后输出*/
#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct student)//
struct student
{
  long num;
  float score;
  struct student*next;
};
int n;
struct student *creat(void)//建立一个动态链表
{
  struct student*head;
  struct student*p1,*p2;
  n=0;
  p1=p2=(struct student*)malloc(LEN);//建立一个动态储存区返回一个地址,这个动态储存区的长度为LEN;
  scanf("%ld,%f",&p1->num,&p1->score);
  head=NULL;
  while(p1->num!=0)
  {
  n=n+1;
  if(n==1)head=p1;
  else p2->next=p1; 
  p2=p1;
  p1=(struct student*)malloc(LEN);
  scanf("%ld,%f",&p1->num,&p1->score);
  } 
  p2->next=NULL;
  return(head);
}
void print(struct student*head)//建立一个输出链表的函数
{
  struct student *p;
  printf("\nnow,these.%d records are :\n",n);
  p=head;
  if(head!=NULL)
  {
  do
  {
    printf("%ld %5.1f\n",p->num,p->score);
    p=p->next;
  }while(p!=NULL); 
  }
}
int main()//主函数
{
  struct student *head;
  head=creat();
  print(head);
  return 0; 
}

1670499562573.jpg

相关文章
|
Linux 数据安全/隐私保护 虚拟化
iOS 打包 IPA 教程
iOS 打包 IPA 教程
|
存储 Linux C++
网易面试:手撕定时器
网易面试:手撕定时器
227 1
|
存储 关系型数据库 MySQL
MySQL数据库锁:共享锁和独占锁
本文详细介绍了`InnoDB`存储引擎中的两种行级别锁:共享锁(S锁)与排他锁(X锁)。通过具体示例展示了这两种锁的工作机制及其在`InnoDB`与`MyISAM`引擎中的表现差异。文章还提供了锁的兼容性矩阵,帮助读者更好地理解锁之间的互斥关系。最后总结了两种锁的特点及适用场景。适合希望深入了解`MySQL`并发控制机制的读者阅读。
443 1
|
设计模式 缓存 Java
谷粒商城笔记+踩坑(14)——异步和线程池
初始化线程的4种方式、线程池详解、异步编排 CompletableFuture
谷粒商城笔记+踩坑(14)——异步和线程池
|
10月前
|
存储 消息中间件 OLAP
Hologres+Flink企业级实时数仓核心能力介绍-2024实时数仓Hologres线上公开课03
本次分享由阿里云产品经理骆撷冬(观秋)主讲,主题为“Hologres+Flink企业级实时数仓核心能力”,是2024实时数仓Hologres线上公开课的第三期。课程详细介绍了Hologres与Flink结合搭建的企业级实时数仓的核心能力,包括解决实时数仓分层问题、基于Flink Catalog的Streaming Warehouse实践,并通过典型客户案例展示了其应用效果。
263 10
Hologres+Flink企业级实时数仓核心能力介绍-2024实时数仓Hologres线上公开课03
|
调度
【浅入浅出】Qt多线程机制解析:提升程序响应性与并发处理能力
在学习QT线程的时候我们首先要知道的是QT的主线程,也叫GUI线程,意如其名,也就是我们程序的最主要的一个线程,主要负责初始化界面并监听事件循环,并根据事件处理做出界面上的反馈。但是当我们只限于在一个主线程上书写逻辑时碰到了需要一直等待的事件该怎么办?它的加载必定会带着主界面的卡顿,这时候我们就要去使用多线程。
400 6
|
XML Java 数据格式
无需手动注册:精通Spring注解扫描的高效利用
无需手动注册:精通Spring注解扫描的高效利用
127 2
|
安全 API
OpenAI邮箱API发送邮件注意事项
使用OpenAI邮箱API需注意API密钥安全,避免泄露;确保邮件内容合法合规,不发送垃圾邮件;设置正确发件人信息,防止被视为垃圾邮件;确认收件人信息准确;控制发送频率;保持内容格式规范;记录发送日志;并先进行发送测试。遵循这些提示可确保邮件发送顺利。
|
资源调度 JavaScript API
vue封装axios请求接口并添加前置拦截器和响应拦截器
vue封装axios请求接口并添加前置拦截器和响应拦截器
241 0
|
存储 编译器 BI
内存函数​(memcpy、memmove、memset、memcmp)
内存函数​(memcpy、memmove、memset、memcmp)