c语言结构体链表
原文链接:http://zhina123.blog.163.com/blog/static/417895782012106036289/
引用自身的结构体,一个结构体中有一个或多个成员的基类型就是本结构体类型时,说明这个结构体可以引用自己,所以称作引用自身的结构体。
例如下面的结构体:
struct link{ char ch; struct link *p} a;
p是一个可以指向struct link类型变量的指针成员,这样,a.p=&a就是合法的表达式。那么,这有什么意义呢?
这样的意义就是我们可以把分散存储的数据项,用一个结构体成员链接起来(当然这也耗费了那个存储指针的内存空间)看下面的程序
#include <stdio.h>
struct node{
int data;
struct node *next;
};
main(){
struct node a,b,c,*p;//我们假设,这样声明的结构体变量a 、b、c在内存中并不是相临的
a.data=; b.data=; c.data=; a.next=&b; b.next=&c; c.next='\0';
p=&a;
//结构体变量a地址赋给p
while(p){
printf(" %d ",p->data);
//每输出一个值后,p自动指向本项的链接项
/*这样有了一个单独保持链接的成员就把不相临的存储单元在逻辑上存储在了一起*/ p=p->next;
}
printf("\n");
}
这样的相链的数据存储形式称为链表!上面形成链表的方法是人为定义的,在程序执行过程中,不会生成新的存储单元,所以也称为“静态链表”
下面看一种更方法使用的“动态链表”
前面的日志中提到了C语言动态存储分配提供了“按需分配内存”的实现方法,有一个问题是,如果多次动态分配存储单元,各存储单元的地址不是一定是连续的,而所需要处理的批量数据往往是一个整体,各数据之间存在着顺序关系,这样,我们可以利用上面的链表把动态得到的存储单元在逻辑上(而不是在物理上)连接起来,就可以实现我们需要的顺序关系了。这时,是把链表技术与动态存储分配结合了起来,所以这里我们给了链表一个新的名词叫做“动态链表”
很自然,我们上面例子中的链表只有一个指向后面数据项的指针,如果有两个呢?一个指后,一个指前,这样就会出现“双向链表”与“单向链表”的区别
下面我们主要看单向链表
事实上,单身链表中的每个存储单元的数据(也就是结构体)的格式类型是相同的(包括数据成员和指针成员)
如下:
struct abc{int data,……struct abc *next;};
与单向链表有关的算法有以下几类:
建立链表 输出结点数据 插入结点数据 删除结点数据
下面这个程序是示例
#include <stdio.h>
#include <stdlib.h>
struct slist{ int data; struct slist *next;};
typedef struct slist SLIST; SLIST *creat_slist(){
/*该函数的作用是创建动态链表,函数的返回值是结构体指针变量,也就是新创建的动态链表的头结点,需要注意的是,这里的头结点没有数据data,只有指针next指向动态链表的第一个结点*/
int c; /*用来临时存储结构体中的data*/
SLIST *h,*s,*r; /*声明工作指针*/
h=(SLIST *)malloc(sizeof(SLIST));
/*动态获取一个结构体的存储空间*/
r=h; /*结构体指针r用来在下面的循环中使用h指针不变*/
scanf("%d",&c); /*得到一个结构体成员int data数据*/
while(c!=-){
/*如果上面得到的int data数据不为-1就进入循环 */
s=(SLIST *)malloc(sizeof(SLIST));
/*循环中用结构体指针s获取结点*/
s->data=c;
/*s成员data为c注意第一次进入循环时,这个c是在循环外部上面输入的*/
r->next=s;
/*r指针本来与h相同,r的成员next在进入循环前是没有指向的现在进入了循环得到了一个结点,就把r的next指向新的结点,这样就使h头结点指向了s*/
r=s;
/*r依次与最新的结点相同,为了依次用最新的存储空间的next指向下一个获得的结点*/
scanf("%d",&c);
}
r->next='\0';
/*退出循环后,r指向最后一个结点,而这个最后结点的成员next要指向'\0'*/
return h;
/*返回动态链表的头结点指针*/
}
void print_slist(SLIST *head){
/*该函数是依次输出动态链表的结点,参数是结构体指针变量,也就是
动态链表的头结点*/
SLIST *p;
/*声明一个工作指针,因为head不能自己往后依次移动,所以用指针
p实现*/
p=head->next;
if(p=='\0')printf("Linklist is null!\n");/*空链表*/
else{printf("head");/*非空链表*/
do{ printf("->%d",p->data);
p=p->next;}while(p!='\0');
printf("->end\n");
} } SLIST *insert_snode(SLIST *head,int x,int y){
/*该函数实现在链表值为x的结点前面插入一个结点,值为y参数有三个链表头结点,值x,y*/
SLIST *s,*p,*q;
/*定义工作指针*/
s=(SLIST *)malloc(sizeof(SLIST));
s->data=y;
/*上面这两句先获取了一个结构体动态存储空间,并给其成员data赋值为y,但此时该空间并未成为链表的一个结点*/
q=head;
p=head->next;
/*上面两句初始化工作指针,就是把工作指针q与head相同,p指向head的next*/
while((p!='\0')&&(p->data!=x)){
/*这个循环是供查找到值为x的结点所在位置的,需要注意的是这里的并的两个条件位置不能变,因为只有在p指向不为空的时候才能讨论其data成员值是否为x,否则如果p的指向是空,程序确要先判断p指向的data是不是x这样会发生地址访问错误,因为p本不指向结点,也就无从谈成员data,所以判断p指向的data是不是x是不对的*/
q=p;p=p->next;
/*满足循环条件的话,p与q依次后移直到找到值为x的结点或到了链表的尾部*/
}
s->next=p;
/*在p指向的结点前面插入,所以新的结点的next指向p*/
q->next=s;
/*q-next本指向p结点,现在令其指向s结点,实现了插入*/ return head;/*头指针并未变化,返回即可*/
} SLIST *insert_bnode(SLIST *head,int x,int y){
/*该函数实现在链表值为x的结点后面插入一个结点,值为y参数有三个链表头结点,值x,y*/
SLIST *s,*p,*q;
/*定义工作指针*/
s=(SLIST *)malloc(sizeof(SLIST));
s->data=y;
/*上面这两句先获取了一个结构体动态存储空间,并给其成员data赋值为y,但此时该空间并未成为链表的一个结点*/
q=head;
p=head->next;
/*上面两句初始化工作指针,就是把工作指针q与head相同,p指向head的next*/
while((p!='\0')&&(p->data!=x)){
/*这个循环是供查找到值为x的结点所在位置的,需要注意的是这里的并的两个条件位置不能变,因为只有在p指向不为空的时候才能讨论其data成员值是否为x,否则如果p的指向是空,程序确要先判断p指向的data是不是x这样会发生地址访问错误,因为p本不指向结点,也就无从谈成员data,所以判断p指向的data是不是x是不对的*/
q=p;p=p->next;
/*满足循环条件的话,p与q依次后移直到找到值为x的结点或到了链表的尾部*/
}
s->next=p->next;
/*在p指向的结点后面插入,所以新的结点的next指向p*/
p->next=s;
/*p-next本指向p后面的结点,现在令其指向s结点,实现了后插入*/ return head;/*头指针并未变化,返回即可*/
} SLIST *del_node(SLIST *head,int x){
/*该函数实现删除链表值为x的结点,参数有两个链表头结点,值x */
SLIST *s,*p,*q;
/*定义工作指针*/
q=head;
p=head->next;
/*上面两句初始化工作指针,就是把工作指针q与head相同, p指向head的next*/
while((p!='\0')&&(p->data!=x)){
/*这个循环是供查找到值为x的结点所在位置的,需要注意的是这里的并的两个条件位置不能变,因为只有在p指向不为空的时候才能讨论其data成员值是否为x,否则如果p的指向是空,程序确要先判断p指向的data是不是x这样会发生地址访问错误,因为p本不指向结点,也就无从谈成员data,所以判断p指向的data是不是x是不对的*/
q=p;p=p->next;
/*满足循环条件的话,p与q依次后移直到找到值为x的结点或到了链表的尾部*/
}
q->next=p->next;
/*把q->next置成p->next,*/
free(p);
/*释放p的存储空间,实现删除*/
return head; /*头指针并未变化,返回即可*/
} main(){
SLIST *head; int x,y;
head=creat_slist();//创建链表函数
print_slist(head);
printf("please input x\n"); scanf("%d",&x);
printf("please input y\n"); scanf("%d",&y);
head=insert_snode(head,x,y);//结点前插入函数
print_slist(head);
printf("please input x\n"); scanf("%d",&x);
printf("please input y\n"); scanf("%d",&y);
head=insert_bnode(head,x,y);//结点后插入函数
print_slist(head);
printf("please input x\n"); scanf("%d",&x);
head=del_node(head,x);//删除结点函数 print_slist(head);
}
c语言结构体链表的更多相关文章
- Go 语言 结构体链表
@ 目录 1. 什么是链表 2. 单项链表的基本操作 3. 使用 struct 定义单链表 4. 尾部添加节点 5. 头部插入节点 6. 指定节点后添加新节点 7. 删除节点 1. 什么是链表 链表是 ...
- Linux C语言结构体-学习笔记
Linux C语言结构体简介 前面学习了c语言的基本语法特性,本节进行更深入的学习. 预处理程序. 编译指令: 预处理, 宏定义, 建立自己的数据类型:结构体,联合体,动态数据结构 c语言表达式工具 ...
- 06. Go 语言结构体
Go语言结构体(struct) Go 语言通过用自定义的方式形成新的类型,结构体是类型中带有成员的复合类型.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性. Go 语言中的类 ...
- 漫谈C语言结构体struct、公用体union空间占用
先用代码说话: #include<stdio.h> union union_data0{ int a ;//本身占用4个字节 char b ;//本身占用1个字节 int c ; }; u ...
- 解析C语言结构体对齐(内存对齐问题)
C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的 ...
- 不可或缺 Windows Native (8) - C 语言: 结构体,共用体,枚举,类型定义符
[源码下载] 不可或缺 Windows Native (8) - C 语言: 结构体,共用体,枚举,类型定义符 作者:webabcd 介绍不可或缺 Windows Native 之 C 语言 结构体 ...
- Go语言结构体(struct)
Go 语言结构体 Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型. 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合. 结构体表示一项记录,比如保存图 ...
- C语言结构体定义的几种方法
什么是结构体? 在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类.结构体可以被声明为变量.指针或数组等,用以实现较复杂的数据 ...
- 对嵌入式开发C语言结构体的一点总结
今天冬至居然不上班,公司的良心啊!这回有心情写博客和日志了,好了,废话不多说.直接看下文: 鉴于嵌入式开发过程中,C语言结构体的使用当然是必不可少.话说,基础什么的比你会更牛逼的算法更重要,基础不牢, ...
随机推荐
- 永久激活win和office
1.关闭自己安装的防护软件 2. 关闭电脑自带的防护软件 3.运行 KMSpico
- class configured for Signature (provider: BC) cannot be found
java.security.NoSuchAlgorithmException: class configured for Signature (provider: BC) cannot be foun ...
- thinkphp 查找表并返回结果
使用 M('devices')->where($data)-> execute($resql); 返回的只是int值,表示成功与否,并没有返回search 到的 record . $res ...
- android和js互相调用
import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.o ...
- JwtBearerAppBuilderExtensions.UseJwtBearerAuthentication(IApplicationBuilder
netcore从1.1升级到2.0时,出的错,因为使用的时Jwt token参考https://github.com/aspnet/Security/issues/1310#issuecomment- ...
- js map/reduce
map 由于map()方法定义在JavaScript的Array中,我们调用Array的map()方法,传入我们自己的函数,就得到了一个新的Array作为结果: function pow(x) { r ...
- 5B - 一只小蜜蜂...
有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行.请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数. 其中,蜂房的结构如下所示. Input 输入数据的第一行是一个整数N,表示测试实例的个数,然 ...
- 右手坐标系下LookAt视图矩阵的推导
基本知识 右手坐标系 右手手掌弯曲,手指方向由正X轴指向正Y轴,如果这时Z轴正方向与大拇指方向保持一致,坐标系为右手坐标系,否则为左手坐标系. 向量叉乘的方向 向量(1,0,0)与向量(0,1,0)叉 ...
- 探索未知种族之osg类生物---呼吸分解之advance
回顾 我们用了两节的内容才堪堪讲解完ViewerBase::frame()函数中调用的realize()---Viewer:: realize()函数.我们简单的总结就是Viewer:: realiz ...
- WIN8配置IIS8.0+PHP+Mysql+Zend
第一步 开启WIN8的IIS 8.0 控制面板 → 程序与功能 → 启用或关闭WINDOWS功能 按照上面勾选 确定即可 成功安装完毕 打开 http://localhost/ 或者 http:/ ...