精华内容
下载资源
问答
  • 一个实例搞懂二重指针 最近在编写一个链表的时候遇到了关于指针的一些问题,在研究的过程中终于弄懂了在函数形参中使用二重指针的意义和用法。 我们先从编写一个函数说起。这个函数要求在一个链表的头部插入一个节点...

    一个实例搞懂二重指针

    最近在编写一个链表的时候遇到了关于指针的一些问题,在研究的过程中终于弄懂了在函数形参中使用二重指针的意义和用法。

    我们先从编写一个函数说起。这个函数要求在一个链表的头部插入一个节点,这个链表没有头结点,并且要求返回值是void。也就是说在函数里要完成对链表头指针的修改。

    一开始我的写法是这样的:

    typedef struct ListNode{
        int val;
        struct ListNode* next;
    }ListNode;
    
    void myLinkedListAddAtHead(ListNode* obj,int val){
        ListNode *List=obj;
        ListNode *Temp=malloc(sizeof(ListNode));
        if(temp==NULL){
            prinf("Out of space!");
        }
        else{
            Temp->val=val;
            Temp->next=List;
            obj=Temp;
        }
    }
    

    读者可以先自己想想这个函数有什么问题。我们先抛开这个例子不谈,看一下另一个简单的例子。现在要设计一个函数交换a,b的数值。

    第一种写法是直接用两个变量进行传参,交换,在父函数里打印。

    void Swap(int a,int b)
    {
    	int tmp = a;
    	a = b;
    	b = tmp;
    }
     
    int main()
    {
    	int a=1;
    	int b=2;
    	printf("a=%d,b=%d\n",a,b);
    	Swap(a,b);
        printf("a=%d,b=%d\n",a,b);
    	return 0;
    }
    

    输出结果是:

    1,2

    1,2

    结果是没有成功交换。我们来看一下其中的内存空间分配以及变量交换出现了什么问题。


    注意这张图里的黑色和红色的变量a,b虽然名字相同,却是两个不同的内存空间。因为函数没有返回值,所以我们仅仅是改变了函数内部的红色a,b的值,主函数中黑色a,b的值没有改变。所以在主函数中打印时a,b的值并没有变化。

    所以如果我们想成功输出的话,就要在函数内部进行输出:

    void Swap(int a,int b)
    {
    	int tmp = a;
    	a = b;
    	b = tmp;
    	printf("a=%d,b=%d\n",a,b);//在函数中输出
    }
     
    int main()
    {
    	int a=1;
    	int b=2;
    	printf("a=%d,b=%d\n",a,b);
    	Swap(a,b);
    	return 0;
    }
    

    输出结果是:

    1,2

    2,1

    结果是成功交换。当然黑色的a,b变量仍未交换,我们只是打印出了交换后的红色变量的值。

    那么我们就是想要交换a,b的值该怎么做呢?我们很自然的想到既然刚才黑色与红色是两块存储空间所以导致没有成功,那我们让他们变成同一块存储空间不就行了吗?所以第二种做法就是将变量的地址传入函数。

    void Swap(int *p1,int *p2)
    {
    	int *tmp = p1;
    	p1 = p2;
    	p2 = tmp;
    }
    
    int main()
    {
    	int a=1;
    	int b=2;
    	printf("a=%d,b=%d\n",a,b);
    	Swap(&a,&b);
    	printf("a=%d,b=%d\n",a,b);
    	return 0;
    }
    

    输出结果是:

    1,2

    1,2

    还是不行。这又是为什么呢?我们来分析一下内存分配的情况。

    原来我们虽然传入了地址,但是函数内部只是交换了指针指向的变量地址,a,b的值依然未被改变。所以我们要交换的不是指针,而是指针指向地址处的值(*p1和*p2)。

    void Swap(int *p1,int *p2)
    {
    	int *tmp;
    	*tmp = *p1;
    	*p1 = *p2;
    	*p2 = *tmp;
    }
    
    int main()
    {
    	int a=1;
    	int b=2;
    	printf("a=%d,b=%d\n",a,b);
    	Swap(&a,&b);
    	printf("a=%d,b=%d\n",a,b);
    	return 0;
    }
    

    运行过程中程序又崩溃了。原来tmp是个野指针,而*tmp是计算机系统中一个随机地址所存储的int数值。直接修改会造成难以预料的错误。所以我们直接用一个int型的tmp变量代替*tmp就好了,即下图。

    void Swap(int *p1,int *p2)
    {
    	int tmp;
    	tmp = *p1;
    	*p1 = *p2;
    	*p2 = tmp;
    }
    
    int main()
    {
    	int a=1;
    	int b=2;
    	printf("a=%d,b=%d\n",a,b);
    	Swap(&a,&b);
    	printf("a=%d,b=%d\n",a,b);
    	return 0;
    }
    

    兜兜转转之后,让我们回到最初的问题里,你发现问题所在了吗?没错,参数里的obj就相当于第一种写法中红色的变量a,他保存的只是指向原链表第一个节点的指针的一个副本。也就是说函数内部开辟了一块指针大小的空间,然后将链表头的地址复制到这个空间里了。对这个函数内部的空间的操作完全与原链表头无关。

    所以根据之前例子中的做法,我们把这里的ListNode*当成int来看,就会发现我们应该传入的是ListNode*的地址,即ListNode**了。这就是二重指针的由来,我们要改变指针的地址了。我们的最终写法就是:

    typedef struct ListNode{
        int val;
        struct ListNode* next;
    }ListNode;
    
    void myLinkedListAddAtHead(ListNode** obj,int val){
        ListNode *List=*obj;//obj存储的是指向链表第一个节点的指针的地址,List存储obj地址中保存的值,即链表第一个节点的地址
        ListNode *Temp=malloc(sizeof(ListNode));
        if(temp==NULL){
            prinf("Out of space!");
        }
        else{
            Temp->val=val;
            Temp->next=List;
            *obj=Temp;//将新节点的地址赋值给obj指向的地址,即赋值给指向链表第一个节点的指针
        }
    }
    

    用文字说还是很绕,上图:

    终于大功告成,至此我们终于理解了二重指针的作用。然而如果允许返回一个指针,那么其实事情本可以更简单,我们也就不用使用二重指针了。

    展开全文
  • 二重指针指向一重指针;三重指针指向二重指针; 2)指针可以通过左取* 或者右取[]来解引用; 3)数组名是指向首元素的指针; 4)一维数组是同类型变量的集合;二维数组是一维数组的集合。 1、一重指针指向普通...

    记住以下关于指针和数组的五句话,基本就能理解数组和指针的转换关系。

    1)一重指针指向普通变量;二重指针指向一重指针;三重指针指向二重指针;

    2)指针可以通过左取* 或者右取[]来解引用;

    3)数组名是指向首元素的指针;

    4)一维数组是同类型变量的集合;二维数组是一维数组的集合。

    5)指针总可以通过不断添加[]来解引用,直到去掉所有*;数组只能通过添加一个*同时去掉一个[],来变成对应的指针。

    1、一重指针指向普通变量:

    char  ch = 'a';

    char *p = &ch;                   ------->*p == p[0] ='a';

    2、数组名是指向首元素的指针:

    也就是说也仅仅是指向一个元素,故本质上也是一重指针指向普通变量;只不过,指向数组首元素的话,可以通过指针的位移,来指向数组中的其他单个元素。

    char  ch[] = {'a','\0'};-----------ch 等于 &ch[0]             ------->*ch==ch[0]=='a'

    小结:一重指针可以指向一个普通变量,也可以指向一个一维数组(本质上也是指向一个普通变量)

    举例:当数组元素为char *时,

    一维数组形如:

    char  ch1 = 'a';

    char  ch2 = 'b';

    char* ch[2] ={&ch1,&ch2};   -------->ch[0]==*ch==&ch1;    ch[0][0]==*ch[0]=='a'

    或者:

    char  ch1[] = {'a','\0'};

    char  ch2[] = {'b','\0'};

    char* ch[2] ={ch1,ch2};            ------>ch[0][1] ==*(ch[0]+1)==(*ch)[1]==*((*ch)+1)

    3、二重指针指向一重指针

    char  ch1 = 'a';

    char  *p= &ch1;

    char **ch = &p;              -------->ch[0][0]== (*ch)[0]=='a'

    或者

    char  *ch1[] = {"hello","world"};

    char **ch = ch1;            ------------->ch[1][0] == (*(ch+1))[0]=='w'

    小结:二重指针指向一重指针变量或者一重指针数组(首元素)

    二重指针本质上还是指向一个变量,只不过这个变量是个指针。

    4、三重指针指向二重指针

    char  ch1 = 'a';

    char *p = &ch1;

    char **q= &p;

    char*** ch= &q;          ------> ch[0][0][0] == *ch[0][0]=='a'

    或者 指向一个二重指针数组

    char *str1[] = {"hello","world"};

    char *str2[] = {"nihao","shijie"};

    char **str[]={str1,str2};

    char ***ch = str;                    ------->ch[1][0]=="nihao"

    注意:以下直接对数组名取地址,,获取的是一个数组指针。并不是一个二重指针

    小结:三重指针本质上还是指向一个变量,只不过这个变量是个二重指针或者是个二重指针数组(首元素)。

    5、一维数组是变量的集合

    变量可以是char ,也可以是char*

    char  ch[]={'a','\0'};

    char  ch1[] = {'a','\0'}; char  ch2[] = {'b','\0'};   char*  ch[]={ch1,ch2};

    6、二维数组是一维数组的集合    //本质也是变量的集合。

    char  ch1[] = {'a','\0'}; char  ch2[] = {'b','\0'};   char ch[2][2]={{'a','\0'},{'b','\0'}}

    由上图可以看出:二维数组的实质其实是一维数组的拼接,里面的元素都是char 类型。 正确赋值方法如下,

    这与下图中的一维数组是不同的。

    7、指针可以通过左取* 或者右取[]来解引用-----参照文中示例带箭头的加粗示例。

     

    展开全文
  • c++二重指针

    2020-12-25 21:31:44
    C++二重指针理解

    C++二重指针理解
    双重指针的用法
    c++双重指针数组
    总结一下,一个*就是类似指针,**就是指针指向的内容

    展开全文
  • 二重指针理解

    千次阅读 多人点赞 2018-12-02 15:48:58
    二重指针一般用的场合: (1)二重指针指向一重指针的地址。 #include<stdio.h> int main() { int a = 10; int *P1; int **P2; P1 = &a; P2 = &P1; return 0; }   (2)...

    二重指针一般用的场合:

    (1)二重指针指向一重指针的地址。

    #include<stdio.h>

    int main()

    {

    int a = 10;

    int *P1;

    int **P2;

    P1 = &a;

    P2 = &P1;

    return 0;

    }

     

    (2)二重指针指向指针数组的地址。

    #include<stdio.h>

    int main()

    {

    int *a[4];//指向整形变量的指针数组

    int **p;

    p = a;//注意这里为什么可以这么赋值?因为a是数组首元素的地址,并且a的首元素为int *类型,所以a便是int *类型的地址,因此可以这么赋值

    }

     

    int a=1,*p,**q;
    p=&a; p存放变量a的地址
    q=&p; q存放指针p的地址
    **q=*p=a=1;
    q是指针p的地址
    *q是指针p的内容,即a的地址
    **q是指针p指向区域的内容*p,*p=a

    普通的指针存放变量的地址
    指向指针的指针存放指针的地址

     

    例:

    int a=3, *p, **q;
    p=&a;
    q=&p;
    这样赋值后, *q就是p, **q就是a

     

    例:
    #include <stdio.h>
    int main(void)
    {
            char *q = "a";//"a"是一个字符串,将"a"赋值给字符指针是把第一个字符的地址赋值给q,这句话相当于是:char *q;q="a" 
            char **p = &q;//这句话相当于是:char **p;p=&q;//二重指针p的值是指针q的地址 
            printf("%d\n", *p);//二重指针p加*号,是取内容,意思是指针q的内容,即字符串a的地址 
            return 0;
    }
    //输出结果是:一串数字,即地址,如果改成%s输出,则输出的是字符a,如果改成%c输出,就要相应的修改参数为变量名,将*p改为:**p ,如下面的例子。
     

    例:

    #include <stdio.h>
    int main(void)
    {
            char c='a';
            char *q=&c;
            char **p = &q;//双重指针
            printf("%c\n", **p);
            return 0;
    }

     

    例:

    #include<stdio.h>
    void F1(int *p)
    {
        p++;
        *p = 9;
    }
     
    void F2(int **p)
    {
        (*p)++;
    }
    //以上函数中,接收数据的参量都是p,而不会是*p或者**p
     
    int* F3(int *p)
    {
        p++;
        return p;
    }
     
     
    int main(void)
    {
        int *p;
        int a[2] = { 4,5 };
        p = a;
        printf("1--------%d\n", *p);//开始值为4
     
        F1(p);
        printf("2--------%d\n", *p);//4
        //由于只是传值,在子函数F1的内部改变复制品的值,并不会改变原品p的值
        //可以理解为用两个变量指向同一个地址,即形参p和实参p指向同一个地址
        //其中形参p++,另外一个实参p并不会受影响
        //但是可以修改指针所指的值,如F1代码中将a[1]的值改为9;
        printf("2`-------%d\n", a[1]);//9
        
        F2(&p);
        printf("3--------%d\n", *p);//9
        //传址,p=&p(指针的地址,即p是指向指针的指针),*p=*(&p)=p;
        //如果要修改指针p,则应该传入指针p的地址(&p),此时进行*p++操作,其实是对实参指针p进行操作
     
        getchar(); getchar();
        return 0;
    }
     
    //这个例子说明:如果要在子函数中修改主函数传过来的指针的指向,那么主函数应该向子函数传入指针的地址(而非指针本身);
    //此时在子函数中进行*操作后可以获得原来指针,而不是原来指针的复制品,之后可以根据需要修改指针。
    //或者,将返回值类型改为指针类型,然后返回修改后的指针,给原来主函数的指针,如F3函数,此时在主函数中需要添加p=F3(p)代码。
     

    展开全文
  • 这两个符号通俗点儿理解其实&地址就是就是一个存放地址的变量存储空间,当p指针指向某个变量,这时这个p指针里就存放了那个变量的地址。这就是我们常说的指针指向一个地址,意思是通过它能找到以它为地址的内存...
  • 本文以计算器的设计为例介绍了通过使用C语言的二重指针实现简单工厂模式,二重指针的使用甚为精妙,值得好好体味!
  • 指针与引用

    2016-05-27 14:27:59
    深入分析指针和引用的特性
  • #includeint main(){ int **point; int a=2; *point=&a; printf("%d\n",point); printf(。指针,即指向数据的储存地址,方便计算机寻找数据,可以...看的是谭浩强的书 前面都还能理解 看到指针*地址&就...
  • 这是做了一个分别使用二重指针和指针的引用带回函数处理结果的例子,左边图使用二重指针,右边图使用指针引用。可以看出来就这个问题来说,使用指针引用简单。但是对于纯C语言的代码,没有引用的概念,就不可用了。 ...
  • 1. this指针 1.0 常识性问题 当对象访问类中的非静态成员时,编译器会自动将对象的地址隐式的作为第一参数传递给this指针。 类的静态成员属于整个类,不属于某一个对象,没有对象也就谈不上this指针。 编译器的...
  • 指针

    2020-04-15 22:35:05
    通俗来说,指针也只是种数据类型而已,但是与int,float,char等不同的是,指针是可以存储一个变量的地址的。而指针之所以叫指针是因为它可以存储地址,当我们知道了一个变量的地址后,就可以说是这个指针是指向某个...
  • c语言的汇编表示(指针数组和数组指针) c的两大支柱:数组和指针,优良的继承并发扬了它的爹的两大特性:地址加偏移。 什么是数组 那就得先说明什么是数组 当我们定义了一个数组时,虽然没有赋值,编译器已经给我们...
  • 指针学习体会

    2019-12-07 17:48:57
    #define _CRT_SECURE_NO_WARNINGS #include #include #include int main() ...1.指针的定义:指针就是一个存储变量首地址的变量,通俗的来说,如果把内存看成是...6.二重指针int **pp,表示一个存储指针变量地址的指针
  • 用指向指针方法对N个字符串进行排序并输出
  • 指针函数和函数指针

    2013-08-22 13:40:56
    在学习arm过程中发现这“指针函数”与“函数指针”容易搞错,所以今天,我自己想一次把它搞清楚,找了一些资料,首先它们之间的定义: 1、指针函数是指带指针的函数,即本质是一个函数。函数返回类型是某一类型的...
  • C语言包罗万象,异常灵活,在会用的人手里有无限可能,对于不会的人...课程目标:本课程是指针部分的高级话题,首先讲解了数组指针指针数组、函数指针等常见的复杂指针表达式的分析方法、含义,再引入typede...
  • 指针函数&函数指针

    2013-09-03 11:46:37
    在学习arm过程中发现这“指针函数”与“函数指针”容易搞错,所以今天,我自己想一次把它搞清楚,找了一些资料,首先它们之间的定义: 1、指针函数是指带指针的函数,即本质是一个函数。函数返回类型是某一类型的...
  • 在学习arm过程中发现这“指针函数”与“函数指针”容易搞错,所以今天,我自己想一次把它搞清楚,找了一些资料,首先它们之间的定义: 1、指针函数是指带指针的函数,即本质是一个函数。函数返回类型是某一类型的...
  • 运算符将作用于二重指针 fpp 上。 三、指向指针数组的指针   指针的指针另一用法旧处理指针数组。有些程序员喜欢用指针数组来代替多维数组,一个常见的用法就是处理字符串。  char *Names[]= { Bill, Sam, ...
  • 正如上一篇文章支持向量机通俗导论(理解SVM的三层境界)末尾所述:”额,blog许久未有更新了,因为最近实在忙,无暇顾及blog。“与此同时,工作之余,也一直在闲心研究数据挖掘:"神经网络将可能作
  • 一、链表的再认识(带起源结点) 1.起源结点 首结点的前驱结点即为起源结点,起源结点出现的目的是为了更方便地操作链表,其数据域可用来存放有效结点长度。 从我个人的角度出发,起源结点是链表的发源地,...2.起源指针
  • 2021-06-02

    2021-06-02 16:17:30
    学好指针 从我做起 关于指针与数组的关系,我想写点东西。 ...
  • C-简明命名规范

    2020-12-07 15:45:26
    通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net 变量的命名规范 1、变量的命名规则要求使用“匈牙利法则”。即开头字母用变量的类型,其余部分用变量的英文意思或英文...
  • 避免野指针,判断指针是否野指针时,写成if (NULL != p) 注意:输入型参数与输出型参数 用宏定义来实现DEBUG和release //学习方法:man手册、头文件、百度、博客、总结 写程序尽量避免使用全局变量,尤其是非stat..
  • 插入e *q = e; // 3.表长增1 L->length++; return OK; } /* * 删除 * * 删除顺序表第i个位置上的元素,并将被删除元素存储到e中。 * 删除成功则返回OK,否则返回ERROR。 * *【备注】 * 教材中i的含义是元素位置,从1...
  • 有时候也叫伪随机数,使用 rand() 生成随机数之前需要用随机发生器的初始化函数 srand(unsigned seed)(也位于 stdlib.h 中) 进行伪随机数序列初始化,seed 又叫随机种子,通俗讲就是,如果每次提供的 seed 是一样...
  • 通俗理解,学校可以轻松的通过学生的学号找到学生,计算机也采取了类似方式来快速寻找内存中的数据,计算机给每片内存编号(地址),地址其实就是一个long型整数,而指针变量其实就是保存了这个long型整数。...
  • 对于C语言语法以及一些难以理解的概念,均以精心绘制的示意图,清晰、通俗地进行讲解。原著在日本广受欢迎,始终位于网上书店C语言著作排行榜首位。 第1章 初识C语言 1 1-1 显示计算结果 2 计算整数的和并显示...
  • 大话数据结构 -- 图

    千次阅读 2018-09-05 19:01:16
    指向第一个邻接点的指针 ,以便于查找该顶点的边信息; 2、图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以用 单链表 存储,无向图称为顶点vi的 边表 ,有向图则称为顶点vi作为 弧尾 的 ...
  • 有向树的理解比较容易,所谓入度为 0 其实就相当于树中的根节点,其余顶点入度为 1 就是说树的非根结点的双亲只有一个。 森林:一个有向图的生成森林由若干棵有向树组成,含有图中全部顶点,但有足以构成若干棵不...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 155
精华内容 62
关键字:

**q怎么通俗理解二重指针