鱼C论坛

 找回密码
 立即注册
查看: 4713|回复: 4

关于链表功能实现的几个疑问

[复制链接]
发表于 2013-7-10 10:25:14 | 显示全部楼层 |阅读模式
10鱼币
在看 C Primer Plus(第五版)第十七章高级数据表示,采用ADT(抽象数据类型)方式定义链表,按书上例子:
数据的构造代码如下:

  1. struct film
  2. {
  3. char title[TSIZE];
  4. int rating;
  5. };
  6. typedef struct film Item;
  7. typedef struct node
  8. {
  9. Item item;
  10. struct node *next;
  11. }Node;
  12. typedef Node *List;
复制代码

对于上面的数据构造类型,设计一个功能函数如下:
  1. /*函数原型 */
  2. /*操作:初始化一个列表 */
  3. /*操作前:plist指向一个列表 */
  4. /*操作后:该列表被初始化为空列表 */
  5. void InitializeList(List *plist)
  6. {
  7. *plist=NULL;
  8. }
复制代码

我尝试梳理:首先Item是纯粹表示数据的结构类型;通过加上指针成员构成单链表的Node结构类型;并设置List为指向该Node类型的指针类型。
问题是,为何上述函数中,参数要用一个指向List的指针类型数据来表示?如果直接用List类型的数据不行么?List本身也是指针,传递的也就是地址,像如下表示有啥问题没:
  1. void InitializeList(List plist)
  2. {
  3. plist=NULL;
  4. }
复制代码

再比如同样对于前面的数据类型,另一个功能函数按书上的原型实现代码如下(注释为我的疑问):
  1. /*操作:确定列表中项目数量 */
  2. /*操作前:plist指向一个已初始化的列表 */
  3. /*操作后:返回项目数量 */
  4. unsigned int ListItemCount(const List *plist)             //为何不直接const List plist?
  5. {
  6. Node *pnew;                  //为何不直接List pnew;
  7. Node *scan=*plist;         
  8. pnew=(Node *)malloc(sizeof(Node));
  9. if (pnew==NULL)
  10.       return false;
  11. CopyToNode(item,pnew);
  12. pnew->next=NULL;
  13. if(scan==NULL)
  14.       *plist=pnew;
  15. else
  16. {
  17.       while(scan->next!=NULL)
  18.             scan=scan->next;
  19.       scan->next=pnew;
  20. }
  21. return true;
  22. }
复制代码

针对上述代码及我注释里的疑问,我的函数原型实现代码如下:
  1. /*操作:确定列表中项目数量 */
  2. /*操作前:plist指向一个已初始化的列表 */
  3. /*操作后:返回项目数量 */
  4. unsigned int ListItemCount(const List plist)
  5. {
  6. List  pnew;
  7. List scan=plist;
  8. pnew=(Node *)malloc(sizeof(Node));
  9. if (pnew==NULL)
  10.     return false;
  11. CopyToNode(item,pnew);
  12. pnew->next=NULL;
  13. if(scan==NULL)
  14.     plist=pnew;
  15. else
  16. {
  17. while(scan->next!=NULL)
  18.        scan=scan->next;
  19. scan->next=pnew;
  20. }
  21. return true;
  22. }
复制代码

请大家帮我看看,解释下我的疑问吧。





最佳答案

查看完整内容

为了说明问题,先看个简单的例子: void f( int d ); int main () { int c = 5; f(c); } 不给你看函数f 的定义,你也应该知道,调用完f(c)后,c的值没有变。这是函数值传递决定的,传进函数f,给f操作的只是c的副本,这个副本仅仅在初始的时候和c的值一样,以后和c没有任何关系。所以f不可能改变c的值。用生活中的例子说明,你把你的照片(你的副本)送给一个坏蛋,你不会担心因为他撕毁照片而对你本人照成伤害。 但是 ...
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2013-7-10 10:25:15 | 显示全部楼层
为了说明问题,先看个简单的例子:
void f( int  d );
int main () {
  int c = 5;
  f(c);
}
不给你看函数f 的定义,你也应该知道,调用完f(c)后,c的值没有变。这是函数值传递决定的,传进函数f,给f操作的只是c的副本,这个副本仅仅在初始的时候和c的值一样,以后和c没有任何关系。所以f不可能改变c的值。用生活中的例子说明,你把你的照片(你的副本)送给一个坏蛋,你不会担心因为他撕毁照片而对你本人照成伤害。
但是对于函数f( int* pd );我们就认为他可能在函数内部修改pd所指向的变量。

下面,我们把之前的函数参数int换成List来看(你现在是不是在想List是指针的指针啊?千万不要这样想,写这种简单的程序,根本不需要知道List具体是什么类型,不过你必须知道,List是一个类型,就像int是一个类型)
void InitializeList(List list);这样的函数不用看它的定义都知道调用后不可能改变变量list。只可能改变list的副本,当然函数调用结束,list副本就销毁,所以它其实什么都没做。但void InitializeList(List *plist)就有可能改变plist所指向的变量。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2013-7-10 22:03:16 | 显示全部楼层

不知道理解得对不对?
这里List是一个类型名称,如果函数操作须改变实参数据,那就应该用该类型的指针,而不必去管List本身是否为指针类型?
但这里,也因为List本身是指针类型,实际用List类型本身的值进行参数传递,也可以达到改变实参的效果?
这里更多的不是技术理解,而是良好的习惯?
可以这样理解么?
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2013-7-10 22:12:58 | 显示全部楼层
tsembrace 发表于 2013-7-10 22:03
不知道理解得对不对?
这里List是一个类型名称,如果函数操作须改变实参数据,那就应该用该类型的指针, ...

List是一个类型,所以函数如果要改变List类型的参数,必须传List*类型,不管List是什么类型,都是这样的,后面的理解是多余的,也是错误的。
不信你试试。
List lst = 0;
随便你写个函数里面爱做什么做什么,只要这样调用:
f( lst );
//这里lst一定还是0
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2013-7-10 23:02:40 | 显示全部楼层
仰望天上的光 发表于 2013-7-10 22:12
List是一个类型,所以函数如果要改变List类型的参数,必须传List*类型,不管List是什么类型,都是这样的, ...

像是顿悟一样,关键的一句话:如果只用某类型的数据作为参数传递,但对函数而言只是对其副本进行操作。而通过该类型的指针变量,可以使调用函数时候,通过指针指向实参,直接对实参进行操作。
之前基本类型做参数,理解没啥问题;现在一绕,就有难度了,看来还是不深刻。
多谢版主大大啦:lol
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-7-19 13:30

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表