呵呵,辛苦你了杰伦,看了照片我真的相信那确实是书上的代码,而它出现在一本书里我无法掩饰我的失望。
倒是让我萌生了也写本书的念头。
下面我来一一点出我的失望之处。
#include "malloc.h"
#include <stdlib.h>
#include <stdio.h>
就从这头文件说起吧,引用头文件时用尖括号包含和用双引号包含的意义你知道吧。一般来说我以及大部分人的习惯用法是标准库的头文件用尖括号包含,个人编写的头文件用双引号包含。也有全用双引号的,也挺好。
但都是标准库却有用尖括号的、有用双引号的。不是不可以,只是这种作法出现在一本专业的书籍里就显得太不专业了。由此我可以断定这代码是张莉找学生代写的,还没仔细校对,或者校对的水平太差,亦或张莉也只是为了完成任务而匆匆攒了这么一本书。
失望之余,接着往下看。
#define NULL 0
关于这句,继续失望。我想往好的方面想,也许张莉是想让读者明确地知道NULL的值是多少。但我自己都觉得这个想法牵强。
NULL的定义就在stdio.h里为什么要重新定义一次?而且这个常量在C语言里是如此重要,使用是如此频繁,怎可轻意的改变它的定义?
虽然张莉并没有改变NULL的值,但把它拿出来重新定义就是错的。初学者写成这样,我会善意地提醒他。一教学的本书里这样写,我就不能忍了。这不是误人子弟么!
程序代码:
struct student
{
long int number;
char name[20];
float score;
struct student *next;
};
struct student *creat(struct student *head);
void putout(struct student *head);
void main(void)
{
struct student *head;
head=NULL;
head=creat(head);
putout(head);
}
这段代码没什么大问题,只是前面空出的一个制表符看着不爽。原来我以为是你自己排版成这样的,结果看照片上原来书里也是这么写的。唉,各种不专业已经懒得再费口舌了,好好说说下一段吧。
程序代码:
struct student *creat(struct student *head)
{
int n=1;
struct student *p1,*p2;
p1=p2=(struct student*)malloc(sizeof(struct student));
printf("Input %dth number,name,score:\n",n++);
scanf("%d%s%f",&p1->number,&p1->name,&p1->score);
p1->next=NULL;
while(p1->number>0)
{
if(head==NULL)
head=p1;
else
p2->next=p1;
p2=p1;
p1=(struct student*)malloc(sizeof(struct student));
printf("Input %dth number,name,score:\n",n++);
scanf("%d%s%f",&p1->number,&p1->name,&p1->score);
p1->next=NULL;
}
free(p1);
return head;
}
这段代码是我最不能忍的,问题这么多都不知从何说起了。先捋一遍代码吧
n是个可有可无的东西,仅仅是一个序号(我不承认它记录的是元素个数)
先申请一个元素空间,同时让p1、p2都指向它。
然后对这个新元素进行赋值。
如果这个新元素的number字段被赋予小于等于0的值则不进入循环,释放这个元素,返回head指针。
如果新元素的number字段满足要求则进入循环。
如果head为空指针,则head指向新元素,否则将新元素追加到链表的末尾。p1 p2倒腾的就是这么一个过程。
之后申请新元素空间、赋值判断,如果往复,直到number小于零,退出,返回head指针。
你要是把书上的代码原封不动的编译执行还真能得到预期的结果,但其中存在着太多的不合理的地方。
首先,当传入的形参head为NULL时,才会执行if(head == NULL) head = p1;这一句。而这一句也只会被执行一次,因为只要执行一次后head就不为NULL了。
问题是当传入的形参head一开始就不为NULL呢?if(head == NULL)这一句永远不会被执行,只会执行else部分。结果是p1 p2倒腾出一个链表但永远不会和head链在一起。最后返回的head还是原来的head,而p1 p2倒腾出的链表也被抛弃了,还没释放内存。
也就是说这个函数只有在head为NULL时才能正确执行,一个确定的常量还用得着拿参数来传递么?
如果这段代码的本意是在head指向的链表末尾追加元素,那显然这逻辑是错误的。
再说说这元素的申请赋值过程。是先申请空间,然后赋值到这个空间,之后才判断值是否满足要求,如果不满足则释放这个元素空间。
我不知道该怎么形容这种做法。愚蠢这个词似乎有点过,也有人身攻击之嫌。
正常的做法应该是先得到输入的值,然后判断是否满足要求,满足则申请空间赋值,否则直接退出。这在逻辑上更简单,效率上也不用释放最后那个没用的元素。
即使就按他的逻辑,实现时怎么就选了个while循环?都开始讲链表了,不会没学到do while循环吧?do while循环不正是来做这种先办事再判断的合适结构么!
output函数就不讲了,不判断动态申请的空间是否成功不说了。最后再说一个让我失望之处,代码最终也没释放链表申请的空间。作为专业书籍中的例子,这实在让人忍无可忍。
由于时间关系,我就不继续失望了。有这吐槽的时间不如给你写一个更有价值的例子。
程序代码:
#include<stdio.h>
#include<malloc.h>
struct student
{
long int number;
char name[20];
float score;
struct student * next;
};
struct student * create()
{
struct student t, * tail, * p;
int i;
tail = &t;
for(i = 1;; i++)
{
printf("input %dth number,name,score:\n", i);
scanf("%d%s%f", &t.number, t.name, &t.score);
if(t.number <= 0) break;
p = (struct student *)malloc(sizeof(struct student));//这里也不作申请判断了,以简化代码逻辑
*p = t;
p->next = NULL;
tail->next = p;
tail = p;
}
return t.next;
}
void del(struct student * head)
{
struct student * p;
while(head != NULL)
{
p = head;
head = head->next;
free(p);
}
}
void output(struct student * head)
{
while(head != NULL)
{
printf("学号:%ld,姓名:%s,成绩:%5.2f\n", head->number, head->name, head->score);
head = head->next;
}
}
int main(void)
{
struct student * head;
head=create();
output(head);
del(head);
return 0;
}