C/C++中结构体(struct)
c++ 里面struct可以new,
另外: C++中,struct关键字与Class关键字基本是一样的,但是,有两点不同
1 struct定义的数据类型里面所有成员默认级别都是共有的,而class里面所有成员默认级别都是私有的
2 在模板定义中,只能用class 或者typename 而不能用struct
- 提问者评价
-
讲的很好,很清楚,感谢高人 http://blog.sina.com.cn/s/blog_3fabd4ba0100od67.html 结构体可以看做是一种自定义的数据类型,它还有一个很重要的特性,就是结构体可以相互嵌套使用,但也是有条件的,结构体可以包含结构体指针,但绝对不能在结构体中包含结构体变量。
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指针的位置,以至于我们再也无法找会首地址的位置了。
======================
#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();
}到此关于结构体的内容已经全部讨论结束,链表的建立删除插入操作可以很好的对前面所学知识进行一个总结,它既考察了程序员对内存大理解(堆内存操作、指针操作)也考察了对结构化编程掌握的熟悉程序
C/C++中结构体(struct)的更多相关文章
- 《挑战30天C++入门极限》C/C++中结构体(struct)知识点强化
C/C++中结构体(struct)知识点强化 在上一个教程中我们已经简单的阐述了什么是结构体了,为了进一部的学习结构体这一重要的知识点,我们今天来学习一下链表结构. 结构体可以看做是一种自定义 ...
- C和C++中结构体(struct)、联合体(union)、枚举(enum)的区别
C++对C语言的结构.联合.枚举 这3种数据类型进行了扩展. 1.C++定义的结构名.联合名.枚举名 都是 类型名,可以直接用于变量的声明或定义.即在C++中定义变量时不必在结构名.联合名.枚举名 前 ...
- (转) C/C++中结构体(struct)知识点强化
本文转载于 http://pcedu.pconline.com.cn/empolder/gj/c/0503/567942_all.html#content_page_1 所有程序经过本人验证,部分程序 ...
- C/C++中结构体struct 的使用
结构(struct) 结构是由基本数据类型构成的.并用一个标识符来命名的各种变量的组合. 结构中可以使用不同的数据类型. 1. 结构说明和结构变量定义 在Turbo C ...
- c#中结构体(struct)和类(class)的区别
一.类与结构的示例比较: 结构示例: public struct Person { string Name; int height; int weight public bool overWeight ...
- C语言中结构体(struct)的几种初始化方法
转自https://www.jb51.net/article/91456.htm 本文给大家总结的struct数据有3种初始化方法 1.顺序 2.C风格的乱序 3.C++风格的乱序 下面通过示 ...
- 内核中用于数据接收的结构体struct msghdr(转)
内核中用于数据接收的结构体struct msghdr(转) 我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构.在我们的虚拟机上发送icmp回显请求包,pin ...
- Swift中元组(Tuples),结构体(Struct),枚举(Enums)之间的区别
Swift有许多种存储数据方式,你可以用枚举(enums),元组(tuples),结构体(structs),类(classes),在这篇文章中我们将比较枚举.元组.结构体之间区别,首先从最简单的开始- ...
- C#中的元组(Tuple)和结构体(struct)
在正常的函数调用中,一个函数只能返回一个类型的值,但在某些特殊情况下,我们可能需要一个方法返回多个类型的值,除了通过ref,out或者泛型集合可以实现这种需求外,今天,讲一下元组和结构体在这一方面的应 ...
随机推荐
- Eureka的自我保护模式
一 Eureka的自我保护模式 进入自我保护模式最直观的体现就是Eureka Server首页的警告,如下图: 默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,E ...
- nodejs mysql 执行多条sql语句
执行多条查询语句 为了安全起见,默认情况下是不允许执行多条查询语句的.要使用多条查询语句的功能,就需要在创建数据库连接的时候打开这一功能: var connection = mysql.createC ...
- Selenium Books
Recently, some of my projects rely heavily upon tests with selenium. Some books about selenium are c ...
- List集合去除重复对象及equals()、hashCode()方法的作用
原文:https://blog.csdn.net/freelander_j/article/details/52211010 在java中,要将一个集合中重复的对象除去,如果这个集合中的数据类型是基本 ...
- 前端Datatables自定义事件(监听Datatables插件一些常见的事件动作)
今天开发项目的时候,用Datatables插件做前端分页列表,想在列表发生翻页.排序.搜索.改变单页显示数据条数这些行为的时候做一些其他的操作,看了半天Datatables官网终于找到可以监测到这些事 ...
- Angular快速学习笔记(2) -- 架构
0. angular 与angular js angular 1.0 google改名为Angular js 新版本的,2.0以上的,继续叫angular,但是除了名字还叫angular,已经是一个全 ...
- 【LOJ】#2534. 「CQOI2018」异或序列
题解 每个数都处理成前缀和,就相当于问\([l - 1,r]\)有几个数对\(x,y\),\(sum[x] ^ sum[y] = k\) 直接莫队即可 代码 #include <bits/std ...
- P1387 最大正方形 图DP
题目描述 在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长. 输入输出格式 输入格式: 输入文件第一行为两个整数n,m(1<=n,m<=100),接下来n行,每行m ...
- thinkphp中data方法
data方法也是模型类的连贯操作方法之一,用于设置当前要操作的数据对象的值,可能大家不太习惯用这个方法,今天来讲解下如何用好data方法. 用法 写操作 通常情况下我们都是通过create方法或者赋值 ...
- 019.Zabbix的Trigger及相关函数
一 告警简介 告警指将达到某一个阀值事件的消息发送给用户,让用户在事件发生的时候即可知道监控项处于不正常状态,从而采取相应的措施.在Zabbix中,高进是由一系列的流程组成,首先是触发器达到阀值,接下 ...