zoukankan      html  css  js  c++  java
  • 一级指针与二级指针在动态链表中的应用

    btypedef struct Node
    {
        int elem;
        struct node *next;
    }node,*LinkList;

    对于LinkList L: L是指向定义的node结构体的指针,可以用->运算符来访问结构体成员,即L->elem,而(*L)就是个Node型的结构体了,可以用点运算符访问该结构体成员,即(*L).elem;

    对于LinkList *L:L是指向定义的Node结构体指针的指针,所以(*L)是指向Node结构体的指针,可以用->运算符来访问结构体成员,即(*L)->elem,当然,(**L)就是Node型结构体了,所以可以用点运算符来访问结构体成员,即(**L).elem;

    在链表操作中,我们常常要用链表变量作物函数的参数,这时,用LinkList L还是LinkList *L就很值得考虑深究了,一个用不好,函数就会出现逻辑错误,其准则是:

    如果函数会改变指针L的值,而你希望函数结束调用后保存L的值,那你就要用LinkList *L,这样,向函数传递的就是指针的地址,结束调用后,自然就可以去改变指针的值;

    而如果函数只会修改指针所指向的内容,而不会更改指针的值,那么用LinkList L就行了;

    下面说个具体实例吧

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef int ElemType;
    typedef struct Node
    {
        ElemType elem;
        struct Node *next;
    }Node, *LinkList;
    
    //初始化链表,函数调用完毕后,L会指向一个空的链表,即会改变指针的值,所以要用*L
    void InitList(LinkList *L)
    {
        *L = (LinkList)malloc(sizeof(Node));
        (*L)->next = NULL;
    }
    
    //清空链表L,使L重新变为空链表,函数调用完后不会改变指针L的值,只会改变指针L所指向的内容(即L->next的值)
    void ClearList(LinkList L)
    {
        LinkList p;
        while(p = L->next)
            free(p);
    }
    
    //销毁链表L,释放链表L申请的内存,使L的值重新变为NULL,所以会改变L的值,得用*L
    void DestroyList(LinkList *L)
    {
        LinkList p;
        while(p = (*L)->next )
            free(p);
        free(*L);
        *L = NULL;
    }
    
    int main()
    {
        LinkList L=NULL;
        InitList(&L);
        ClearList(L);
        DestroyList(&L);
        return 0;
    }
    LinkList L  定义了一个LinkList的对象,叫L
    LinkList *L 定义了一个可以指向LinkList对象的指针,叫L
    (*L).elem   指针L指向的对象的成员变量elem,与L->next等价
    L.elem      对象L的成员变量elem
    L->next     指针L指向的对象的成员变量next
    (*L)->next       指针L指向的对象指向的对象的成员变量next

     

                      关于一级指针和二级指针作为函数的参数

    一:一级指针动态去申请内存

    void  GetMemory(char  *p,  int  num)  
    {  
               p  =  (char  *)malloc(sizeof(char)  *  num); //传过来的是P所指的地址,并不是P的地址,所以改变S不会改变P
    }  
    void  Test(void)  
    {  
               char  *str  =  NULL;  
               GetMemory(str,  100);            //  str  仍然为  NULL              
               strcpy(str,  "hello");            //  运行错误  
    }  

    毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是  _p,编译器使  _p  =  p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。

    这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把 _p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没有用free释放内存。

    myMalloc(p)的执行过程:  

      分配一个临时变量char  *s,s的值等于p,也就是NULL,但是s占用的是与p不同的内存空间。此后函数的执行与p一点关系都没有了!只是用p的值来初始化s .然后s=(char  *)  malloc(100),把s的值赋成malloc的地址,对p的值没有任何影响。p的值还是NULL。   注意指针变量只是一个特殊的变量,实际上它存的是整数值,但是它是内存中的某个地址。通过它可以访问这个地址。  

    二:二级指针动态申请内存

    如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”

    void  GetMemory2(char  **p,  int  num)  
    {  
               *p  =  (char  *)malloc(sizeof(char)  *  num);  //S指向的是P的地址,所以改变了P所指的内存单元
    void  Test2(void)  
    {  
               char  *str  =  NULL;  
               GetMemory2(&str,  100);            //  注意参数是  &str,而不是str  
               strcpy(str,  "hello");              
               cout<<  str  <<  endl;  
               free(str);              
    }  

    myMalloc(&p);

      将p的地址传入函数,假设存储p变量的地址是0x5555,则0x5555这个地址存的是指针变量p的值,也就是Ox5555指向p。   调用的时候同样分配一个临时变量char  **s,此时s  的值是&p的值也就是0x5555,但是s所占的空间是另外的空间,只不过它所指向的值是一个地址:Ox5555。   *s=(char  *)  malloc(100);这一句话的意思是将s所指向的值,也就是0x5555这个位置上的变量的值赋为(char  *)  malloc(100),而0x5555这个位置上存的是恰好是指针变量p,这样p的值就变成了(char  *)  malloc(100)的值。即p的值是新分配的这块内存的起始地址。     这个问题理解起来有点绕,关键是理解变量作函数形参调用的时候都是要分配一个副本,不管是传值还是传址。传入后就和形参没有关系了,它不会改变形参的值。myMalloc(p)不会改变p的值,p的值当然是 NULL,它只能改变p所指向的内存地址的值。但是myMalloc(&p)为什么就可以了,它不会改变(&p)的值也不可能改变,但是它可以改变(&p)所指向内存地址的值,即p的值

    由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态内存

    char  *GetMemory3(int  num)  
    {  
               char  *p  =  (char  *)malloc(sizeof(char)  *  num);  
               return  p;  
    }  
    void  Test3(void)  
    {  
               char  *str  =  NULL;  
               str  =  GetMemory3(100);              
               strcpy(str,  "hello");  
               cout<<  str  <<  endl;  
               free(str);              
    }  

    用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return语句用错了。这里强调不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡

    char  *GetString(void)  
    {  
               char  p[]  =  "hello  world";  
               return  p;            //  编译器将提出警告  return语句返回指向“栈内存”的指针 
    }  
    void  Test4(void)  
    {  
    char  *str  =  NULL;  
    str  =  GetString();            //  str  的内容是垃圾  
    cout<<  str  <<  endl;  
    }  

     return语句返回常量字符串     函数Test5运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“hello  world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。

    无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。 

    char  *GetString2(void)  
    {  
               char  *p  =  "hello  world";  
               return  p;  
    }  
    void  Test5(void)  
    {  
               char  *str  =  NULL;  
               str  =  GetString2();  
               cout<<  str  <<  endl;  
    }  
  • 相关阅读:
    PHP学习笔记一
    抓取【你懂的】应用的数据
    抓取[今日新闻]应用的数据
    开源项目Html Agility Pack实现快速解析Html
    澳大利亚玩全攻略(图文全彩版)
    畅游夏威夷,看这本就够了
    科学丨光的折射
    未解之谜.下
    科学是什么
    大脑奥秘知多少:脑科学初探
  • 原文地址:https://www.cnblogs.com/tianzeng/p/9689516.html
Copyright © 2011-2022 走看看