有关一级指针和二级指针的归纳总结

           计导课讲到指针和动态内存分配,这里真是一个难点,很多概念容易混淆,有时候一个概念要查阅好多网站,看过很多博文才能理解,不过学习编程不就是这样嘛~~一定要想办法理解才行,所以今天归纳了关于一级指针和二级指针的知识供大家学习(中间有一些摘录,不太记录来源,如果有涉及到哪位仁兄的文章,请跟我说,我会加上来源链接哒~)(http://blog.csdn.net/bearray123/article/details/7209356  这个博友写的二级指针理解很不错)

       

一级指针与二级指针的含义:

首先任何值都有地址,一级指针的值虽然是地址,但这个地址做为一个值亦需要空间来存放,是空间就具有地址 ,这就是存放地址这一值的空间所具有的地址,二级指针就是为了获取这个地址,一级指针所关联的是其值(一个地址)名下空间里的数据,这个数据可以是任意类型并做任意用途,但二级指针所关联的数据只有一个类型一个用途,就是地址,指针就是两个用途提供目标的读取或改写么二级指针就是为了提供对于内存地址的读取或改写。指针的表现形式是地址,核心是指向关系指针运算符“*”的作用是按照指向关系访问所指向的对象.如果存在A指向B的指向关系,则A是B的地址,“*A”表示通过这个指向关系间接访问B.如果B的值也是一个指针,它指向C,则B是C的地址,“*B”表示间接访问C如果C是整型、实型或者结构体等类型的变量或者是存放这些类型的数据的数组元素,则B(即C的地址)是普通的指针,称为一级指针,用于存放一级指针的变量称为一级指针变量。A(即B的地址)是指向指针的指针,称为二级指针,用于存放二级指针的变量称为二级指针变量.根据B的不同情况,二级指针又分为指向指针变量的指针和指向数组的指针.

 

 

*****总结:指针也是传值传递,当我们要在被调函数里面改变调用函数一级指针的值时,就需要以二级指针作为参数。这种情况是经常碰到的,比如在链表(无头结点)操作时是通过链表第一个元素来找到其他所有链表中的元素,如果删除操作时删除的正好是第一个 元素,那么这时就要改变链表头指针的指向了。当然还有在二叉树操作时当删除的刚好是树根结点,此时也要改变一级指针的指向。

 

为什么要用二级指针(见上传的图片)



怎么理解利用二级指针申请动态内存

以前在学习数据结构的时候一直没弄懂函数参数里面传递 ** p  以及使用 &的含义,这里摘抄了一小段文章方便理解。不懂的时候再看看这几段代码。

指针参数是如何传递内存的?

    如果函数的参数是一个指针,不要指望用该指针去申请动态内存。见如下例子:

void GetMemory(char *ip, int num)

{

 ip= (char *)malloc(sizeof(char) * num);

}

void Test(void)

{

 char*str = NULL;

 GetMemory(str,100); // str 仍然为 NULL

 strcpy(str,"hello"); // 运行错误

}

 

试图用指针参数申请动态内存

    毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数ip的副本是 _ip,编译器使 _ip= ip.如果函数体内的程序修改了_ip的内容,就导致参数ip的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_ip申请了新的内存, 只是把_ip所指的内存地址改变了,但是ip丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一 块内存,因为没有用free释放内存。

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

 void GetMemory(char **p, int num)

{

 *p = (char *)malloc(sizeof(char) * num);

}

void Test(void)

{

 char *str = NULL;

 GetMemory(&str, 100); // 注意参数是 &str,而不是str

 strcpy(str, "hello");

 std::cout<< str << std::endl;

 free(str);

}

 

这里写一段自己的理解:这个程序是想通过函数给str指针分配一点内存 --malloc,然后再给str指针赋值。。。注意这是在另一个函数内部改变str'指针的指向(str原来是指向NULL的,现在要使之指向函数 GetMemory内分配的内存区域)所以要使用二级指针来实现,其实质就是改变str指针的指向(比如从指向A 改为指向B),而不是改变str指针指向地址的内容哦。

 

 

   用指向指针的指针申请动态内存

    当然,我们也可以用函数返回值来传递动态内存。这种方法更加简单,见如下示例:

 char *GetMemory(int num)

{

 char *ip = (char *)malloc(sizeof(char) * num);

 return ip;

}

void Test(void)

{

 char *str = NULL;

 str = GetMemory(100);

 strcpy(str, "hello");

 std::cout<< str << std::endl;

 free(str);

}

 

我们在做链表的时候,我们肯定希望在用一个函数creatLink(...)函数来增加链表节点。那么我们可以有2种方法来实现
    第一种,用一级指针

[cpp] view plaincopy

  1. typedef struct node{    
  2.     ...    
  3.     ...    
  4. }list;    
  5. node *create(list *l){    
  6.    list *head;    
  7.    head = l;    
  8.    malloc...//为节点申请内存空间     
  9.    ...    
  10.    ...    
  11.    //操作     
  12.    return head;    
  13. }    
  14. int main(...){    
  15.     ...    
  16.     ...    
  17.     list *listhead    
  18.     createList(listhead);    
  19.     ....    
  20.     //以后的任何操作,我们都要考虑,我们是否拿到的是链表头指针,到底哪个是链表波的头指针,我们是否要renturn下来返回链表头指针??等。。。。     
  21. }    

 

这样做可以达到删除增加 节点的目的,但是,在任何情况下,我们的操作都得死死地抓住头指针,也即是我们增加删除节点后,任何对链表长度的修改,我们都要链表头指针返回,即 return head;所以,我们要通过这个函数最后获得头指针,抓住他,死死地抓住他,然后操作。
   
    第二种方法:用双指针,也即是二级指针。

[cpp] view plaincopy

  1. typedef struct node{    
  2.     ...    
  3.     ...    
  4. }list;    
  5. void   create(list **l){    
  6.    list *head;    
  7.    head = *l; //这里不知道写成 *l 对不对,原文是写的l,我自己觉得应该是 *l    
  8.    malloc...//为节点申请内存空间     
  9.    ...    
  10.    ...    
  11.    //操作     
  12.        
  13. }    
  14. int  main(...){    
  15.     ...    
  16.    ...    
  17.     list *st    
  18.     createList(st)    
  19.     ....    
  20.     ..    
  21. // 以后的任何操作,不管是删除还 是插入,我们不需要考虑,我们是否已经return head了,不需要,我们在任何情况下,对链表的操作都只需要使用 st来完成,因为,st就是链表的头指针,不变,因为在申明st的时候,已经为st分配 一个地址空间,它是存在的,一直存在,直到main函数结 束     
  22. }    


 

 

 

总结:  如果函数参数中传递的是指针,而且想改变传入指针的指向的话,则可以使用 二级指针来实现。(当然如果不在函数中的情况则可以直接给一级指针重新赋值使之指向另一个数据)

 

 

为什么改变主函数里指针的内容,即所指变量的地址时,要用二级指针

先来说说函数的传参机制

当数组作为参数传入时,是能够实现双向传递的

数组名作函数参数与基本数据类型作函数参数相比,具有完全不同的特点。c语言规定,数组名代表数组的首地址,所以,数组名作函数参数时,是将数组的首地址由实参传递给形参,即实参数组与形参数组会公用一个相同的数组首地址和一段相同的存储单元。所以,当形参数组元素发生变化时,实参数组元素的值也会随着发生改变。
当形参数组元素发生变化时,实参数组元素的值也会随着发生改变。
 
数组作为参数是按地址传递的
数组名就是数组的首地址。因此在数组名作函数参数时所进行的传送只是地址的传送, 也就是说把实参数组的首地址赋予形参数组名。

 

函数调用时基本的参数传递方式有传值和传地址两种,在传值方式上是将实参的值传递给形参,因此实参可以是表达式或常量,也可以是变量(数组元素),在传址方式下,要将实参的地址传递给形参,因此,实参必须是变量(数组名或数组元素,不能是表达式或常量),在这种情况下,被调函数对形参进行修改其实就是对实参进行修改,因此客观上能实现数据的双向传递。

也解释了当要改变主函数里指针的内容,即所指变量的地址时,要用二级指针。

.指针作为形参时,如果只需要改变指针指向的值,可以使用一级指针,如果需要改变指针本身的地址

,则需要使用二级指针,相当于改变的是一级指针指向的值。
2.指针作为形参时,指针指向的内容变化是可以带回的,指针地址的变化是不可带回的,即指针作为参数,其地址不可改变,否则形参就无法传回实参的值。

即指针作为形参,指针本身的地址改变,函数返回时这种变化将无效(注意!!!是当形参是指针时!!!!)





数组指针与指针数组(一些notes)

数组指针:int(*p)[4] 指向数组p的指针 a pointer to an array

指针数组:int *p[4] orint *(p[4]) 数组p中元素都为int型指针array of pointers

[ ]的优先级高于*


  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值