花了一个小时,用C实现的简单链表操作,望大牛指教

周凯_csdn 2011-08-12 12:28:17

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<stdbool.h> //在C99中使用bool需要引入stdbool.h这个头文件

/*************************************************************
* C语言实现的单向链表(创建,遍历,计算链表长度)
* desc:需要更加良好的封装
* time:2011-08-12 0:23
**************************************************************
*/

/**
* 定义节点类型的数据类型,
* data表示节点的数据域,pNode保存的是下一个节点的地址
*/
struct Node {
int data;
struct Node * pNode;
};

/**
* 声明函数用于构建链表,函数返回值是链表头结点的地址
*/
struct Node * buildLinkedList(int length);

/**
* 声明函数,根据指向链表的头指针,对链表进行遍历
*/
int traverse(struct Node * pHead);

/**
* 声明判断链表是否为空的函数,为空返回true,否则返回false
*/
bool isEmpty(struct Node * pHead);

/**
* 声明计算链表长度函数,为空则返回-1
*/
int length(struct Node * pHead);


int main(void) {
int len = 0; //定义变量用于保存要创建的链表的长度
printf("请输入链表长度:");
scanf("%d", &len); //请求要创建链表的长度
struct Node * pHead = NULL; //定义一个节点类型的指针变量保存链表头指针
pHead = buildLinkedList(len); //动态构建链表
traverse(pHead); //对链表进行遍历
bool empty = isEmpty(pHead); //验证链表是否为空
printf("%s\n",empty==false?"false":"true");
len = length(pHead);
printf("链表长度:%d", len);
free(pHead); //释放为链表分配的内存空间
pHead = NULL; //防止pHead指向垃圾地址,将其指向置为NULL
return 0;
}

/**
* 定义用于动态构建链表的函数
*/
struct Node * buildLinkedList(int length) {
if(length < 1) {
printf("链表的长度值非法,长度值必须是正整数,程序已经退出!");
exit(-1); //此库函数需要引入stdlib.h头文件
}
struct Node * headNode = (struct Node *)malloc(sizeof(struct Node)); //创建链表头结点
if(NULL == headNode) {
printf("链表头结点内存非配失败,链表创建失败,程序已经退出!");
exit(-1);
}
struct Node * currNode = headNode; //当前节点和头结点指向同一片内存空间,currNode节点指向的空间就是新创建的节点空间
currNode->pNode = NULL; //此时头节点的下一个结点是空的,此时链表为空
int val = 0; //定义保存链表数据域的变量
int i = 0; //声明创建链表的索引变量并进行初始化
for(i=0; i<length; i++) {
printf("请输入第%d个结点内容:", i+1);
scanf("%d", &val);
struct Node * newNode = (struct Node *)malloc(sizeof(struct Node)); //动态创建每一个结点
if(NULL == newNode) {
printf("为第%d个节点分配内存失败,此节点可能会丢失!", i+1);
continue;
}
newNode->data = val; //为新结点的数据域赋值
newNode->pNode = NULL; //为新节点的指针域赋值,新节点的下一个结点为NULL
currNode->pNode = newNode; //上一个节点的指针域地址就是当前创建节点地址
currNode = newNode; //当前节点指向新创建节点的内存空间
}
return headNode; //返回链表的头指针,头指针指向的空间也就是链表的头结点
}

/**
* 定义函数用于对指定给出头指针的链表进行遍历
*/
int traverse(struct Node * pHead) {
struct Node * firstNode = NULL;
if(NULL != pHead) {
firstNode = pHead->pNode; //得到该链表的首节点
}
int index = 0;
printf("\n");
while(NULL != firstNode) {
index++;
int data = firstNode->data;
printf("Node%d->data = %d\n", index, data);
firstNode = firstNode->pNode;
}
return index;
}

/**
* 定义函数根据链表的头指针判断链表是否为空
*/
bool isEmpty(struct Node * pHead){
//如果链表的头节点为空,此链表必为空
if(NULL == pHead) {
return true;
}
//如果链表的首节点为空,此链表必为空
if(NULL == pHead->pNode) {
return true;
}
//否则链表不为空
return false;
}

/**
* 定义函数计算链表长度
*/
int length(struct Node * pHead) {
int len = 0;
if(isEmpty(pHead)) {
len = -1;
}
len = traverse(pHead);
return len;
}




问题:
1)感觉封装的太死,太僵硬化,又不知道该如何优化
2)下一步计划看C Primer Plus,有人看过没
3)介绍基本C语言的经典资料(注意不要信口开河,需要时你看过的,不需要你的google,百度-“地球人都知道的事”)




困得不行,睁不开眼了快,睡觉了,身体是革命的本钱哪!!!这永远是真理,希望大家有好的指教,建议,继续分享。。。
...全文
378 15 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
B09030435xzl 2011-08-22
  • 打赏
  • 举报
回复
打酱油的飘过
honbaa 2011-08-22
  • 打赏
  • 举报
回复
可以用
typedef struct node{
int data;
struct LNode *next;
}LNode,*LinkList;

这样用LNode去代替struct node定义节点。 用LinkList代替struct node*定义节点指针变量。优化代码。
小牛毛 2011-08-21
  • 打赏
  • 举报
回复
lz好有耐心啊,注释的好明白,学习哈。。。
jackfans 2011-08-21
  • 打赏
  • 举报
回复
搞懂链表的前提是必须会指针。可惜我不懂!!!放弃
Tauren_Chieftan 2011-08-21
  • 打赏
  • 举报
回复
结构体定义的不够抽象,适用性不强
struct Node{
struct Node *next;
void *data;
}
玩笑 2011-08-21
  • 打赏
  • 举报
回复
要是我,我会用递归,,而且我会定义个头结点,头结点用来记录链表的属性(按需要定义结构,比如包括长度啊,要是像上面这种链表,还可以在头节点中刻录最大值,最小值之类的信息,反正看需要定义就行了),,这样在操作时会方便很多,至少你不需要判断传入的结点是否是链表头了...
至于C Primer Plus,呵呵,个人觉得,C入门非常牛B的书,,..没什么可说的,,好东西不需要解释
寻找自我 2011-08-20
  • 打赏
  • 举报
回复
C和指针 入门之后看,

c专家编程 总结着看

c缺陷和陷阱 想想着看
周凯_csdn 2011-08-20
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 xin_wu_hen 的回复:]

(1)长度可以视作链表的一个属性,所以,如果不考虑存储空间性能的损失,你可以在Node中添加一个长度属性,这样在lsempty时 ,就不用遍历链表了。同时,你也可以添加一个current指针属性,指向前一个访问的地址,当查找时 ,我们可以先判断要找到的节点指针比该指针大还是小,从而是从头开始查起,还是从current位置开始查起。你只有简单的遍历,这道无所谓了,不需添加current指针属性了。……
[/Quote]


#include <stdio.h>
#include <malloc.h>

int main(void) {
double * p = (double *)malloc(sizeof(int));
double q = 100;
*p = q;
printf("*p = %lf, q = %lf\n", *p, q);

free(p);
return 0;
}
上面的malloc到底分配了几个字节,*p和q的值是一样的,说明进行强制转换后p指向的是8个字节而非四个字节,那malloc的意义又何在呢?
如何理解呢?
赵4老师 2011-08-12
  • 打赏
  • 举报
回复
内存地址→指针→链表
VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。
从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单!
指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。”

提醒:
“学习用汇编语言写程序”

“VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习C和汇编的对应关系。”
不是一回事!

//将c:\\tmp文件夹下的所有文件的内容全部放到用malloc分配的内存中
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
struct FB {
char fn[256];
size_t fl;
char *b;
struct FB *next;
struct FB *prev;
} *fh,*fb,*ft;
char ln[256];
char fpn[256];
FILE *af;
FILE *f;
int L,n;
int main() {
system("dir /b /a-d c:\\tmp\\*.* >c:\\allfn.txt");
af=fopen("c:\\allfn.txt","r");
if (NULL==af) {
printf("Can not open file c:\\allfn.txt!\n");
return 1;
}
fh=NULL;
fb=NULL;
n=0;
while (1) {
if (NULL==fgets(ln,256,af)) break;
L=strlen(ln);
if ('\n'==ln[L-1]) ln[L-1]=0;
printf("read %s\n",ln);
strcpy(fpn,"c:\\tmp\\");
strcat(fpn,ln);
ft=(struct FB *)malloc(sizeof(struct FB));
if (NULL==ft) {
printf("Can not malloc ft!\n");
fclose(af);
return 2;//之前的malloc在main退出后由操作系统自动free
}
printf("ft[%d]==%p\n",n,ft);
strcpy(ft->fn,fpn);
f=fopen(fpn,"rb");
if (NULL==f) {
printf("Can not open file %s!\n",fpn);
fclose(af);
return 3;//之前的malloc在main退出后由操作系统自动free
}
ft->fl=_filelength(fileno(f));
ft->b=malloc(ft->fl);
if (NULL==ft->b) {
printf("Can not malloc ft->b!\n");
fclose(f);
fclose(af);
return 4;//之前的malloc在main退出后由操作系统自动free
}
printf("ft[%d]->b==%p\n",n,ft->b);
if (ft->fl!=fread(ft->b,1,ft->fl,f)) {
printf("fread error!\n");
fclose(f);
fclose(af);
return 5;//之前的malloc在main退出后由操作系统自动free
}
fclose(f);
ft->next=NULL;

if (NULL==fh) {
ft->prev=NULL;
fh=ft;
} else {
fb->next=ft;
ft->prev=fb;
}
fb=ft;
n++;
}
fclose(af);
printf("-----list-----\n");
for (ft=fh;NULL!=ft;ft=ft->next) {
printf("%8d %s\n",ft->fl,ft->fn);
if (NULL!=ft) fb=ft;
}
printf("-----free-----\n");
n--;
if (NULL!=fh) {
for (ft=fb->prev;NULL!=ft;ft=ft->prev) {
if (NULL!=ft->next->b) {
printf("ft[%d]->b==%p\n",n,ft->next->b);
free(ft->next->b);
}
if (NULL!=ft->next) {
printf("ft[%d]==%p\n",n,ft->next);
free(ft->next);
}
n--;
}
if (NULL!=fh->b) {
printf("ft[0]->b==%p\n",fh->b);
free(fh->b);
}
printf("ft[0]==%p\n",fh);
free(fh);
}
return 0;
}
//C:\tmp\tmp\Debug>dir /a-d c:\tmp
// 驱动器 C 中的卷是 C_HD5_1
// 卷的序列号是 1817-D526
//
// c:\tmp 的目录
//
//找不到文件
//
//C:\tmp\tmp\Debug>tmp
//找不到文件
//-----list-----
//-----free-----
//
//C:\tmp\tmp\Debug>dir /a-d c:\tmp
// 驱动器 C 中的卷是 C_HD5_1
// 卷的序列号是 1817-D526
//
// c:\tmp 的目录
//
//2011-06-30 18:04 44,840 my_c.rar
//2011-06-30 17:18 1,036 err.frm
//2011-06-30 14:32 14,243 出租.txt
//2011-06-28 12:08 23,681 MSDN98书签.txt
// 4 个文件 83,800 字节
// 0 个目录 17,041,870,848 可用字节
//
//C:\tmp\tmp\Debug>tmp
//read my_c.rar
//ft[0]==00421800
//ft[0]->b==00520068
//read err.frm
//ft[1]==00421670
//ft[1]->b==0052AFC0
//read 出租.txt
//ft[2]==00421530
//ft[2]->b==00378F28
//read MSDN98书签.txt
//ft[3]==004213F0
//ft[3]->b==0052B3F8
//-----list-----
// 44840 c:\tmp\my_c.rar
// 1036 c:\tmp\err.frm
// 14243 c:\tmp\出租.txt
// 23681 c:\tmp\MSDN98书签.txt
//-----free-----
//ft[3]->b==0052B3F8
//ft[3]==004213F0
//ft[2]->b==00378F28
//ft[2]==00421530
//ft[1]->b==0052AFC0
//ft[1]==00421670
//ft[0]->b==00520068
//ft[0]==00421800
//
//C:\tmp\tmp\Debug>
赵4老师 2011-08-12
  • 打赏
  • 举报
回复
记不得哪位C++大牛在哪本学习C++的书的前言里面说过
“用C语言1000行源码能完成的工作千万不要用C++重写!”
AnYidan 2011-08-12
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 babilife 的回复:]
引用 3 楼 hacqing 的回复:
我是来看看的...


++1
[/Quote]

++
至善者善之敌 2011-08-12
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 hacqing 的回复:]
我是来看看的...
[/Quote]

++1
hacqing 2011-08-12
  • 打赏
  • 举报
回复

我是来看看的...
無_1024 2011-08-12
  • 打赏
  • 举报
回复
长度作为一个属性 尽量少用返回链表头结点 多用传参数的方式
Cpremier Plus没看过 直接就看了C++ premier
大IP 2011-08-12
  • 打赏
  • 举报
回复
(1)长度可以视作链表的一个属性,所以,如果不考虑存储空间性能的损失,你可以在Node中添加一个长度属性,这样在lsempty时 ,就不用遍历链表了。同时,你也可以添加一个current指针属性,指向前一个访问的地址,当查找时 ,我们可以先判断要找到的节点指针比该指针大还是小,从而是从头开始查起,还是从current位置开始查起。你只有简单的遍历,这道无所谓了,不需添加current指针属性了。
(2)这个没看过。
(3)我看过C traps and tricks,林锐的高级c/c++编程,还有c语言深度剖析(这个人的名字好像有一个含字的,忘了姓了,不过书的名字好像也不是记得太清楚了,反正是从汇编的角度来分析c程序的,很不错啊)

70,026

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧