• 一级指针与二级指针在动态链表中的应用


    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;  
    }  
  • 相关阅读:
    HDU 5938 Four Operations 【贪心】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5935 Car 【模拟】 (2016年中国大学生程序设计竞赛(杭州))
    HDU 5934 Bomb 【图论缩点】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5933 ArcSoft's Office Rearrangement 【模拟】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5929 Basic Data Structure 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    【转】LaTeX 符号命令大全
    HDU 5922 Minimum’s Revenge 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    HDU 5927 Auxiliary Set 【DFS+树】(2016CCPC东北地区大学生程序设计竞赛)
    数据结构之稀疏矩阵
    C++中引用(&)的用法和应用实例
  • 原文地址:https://www.cnblogs.com/tianzeng/p/9689516.html
Copyright © 2020-2023  润新知