巧用二级指针


title: 巧用二级指针

date: 2024-09-14 20:52:50

description: two level pointer

tags:

  • c language

skaiuijing

我们在学习指针时,经常有人会告诉我们指针是能直接修改内存的地址变量。

但是,真正理解指针作为变量的本质,才能真正理解指针。

int a,代表在内存中开辟一片空间,空间存放a的值。int *a,代表在内存中开辟一片空间,空间存放a的值,只不过此时a的值是一个地址,可以通过这个值找到另一块内存空间。

当我们进行 int *a = malloc(sizeof(xxx))的操作时,实际上就是改变这片空间存储的值。此时a就是一个有意义的值,它代表一个存在且可以被存放的内存空间的地址。

而二级指针,代表指向指针的指针。

以下是示例:(开发环境的编译器为32位,所以指针为4个字节)

复制代码
#include<stdio.h>
#include<malloc.h>


int main()
{
    int a =1;
    int *pa = &a;
    int **ppa = &(pa);
    int ***pppa = &(ppa);

    //printf("pppa-value:%d",(int)(&(&(&a)))); //请思考这样做是否可行?

    printf("pa-value:%d\n",(int)&a);
    printf("ppa-value:%d\n",(int)(&(pa)));
    printf("pppa-value:%d\n",(int)(&(ppa)));



}

为了直观理解,多级指针的内存布局如下(模型更接近汇编):

变量 存储的值 地址
int ***pppa 6422016 ***
int **ppa 6422024 6422016
int *pa 6422036 6422024
int a 1 6422036

其实int a和int*a没有本质区别,它们只不过都是变量而已

这么一看,(int)(&(&(&a)))这个操作肯定是不行的,认为这样是可行的朋友,一定是把多级指针当成了这样:
地址 地址 地址 a的值

参考上文的模型,这是不合理的。

真实的情况是这样的:
存放 存放 存放 存放 a的地址 a的值 pa的地址 a的地址 ppa的地址 pa的地址 pppa的地址 ppa的地址

所以说,(int)(&(&(&a)))中,只有&a是成立的,&(&a)完全是错误且荒谬的。

现在我们该面对多级指针了。

经常有人看见多级指针就害怕,其实多级指针并不神奇。

如果我们寻求ai或者搜索引擎,渴望得到多级指针的精髓,以二级指针为例,我们得到的答案往往是这样的:

  1. 动态内存分配 :二级指针可以用于动态分配二维数组或更高维数组的内存。例如,分配一个 m x n 的二维数组,可以使用二级指针来管理内存。
  2. 传递指针并修改其值:通过使用二级指针,可以在函数中修改指针的值。例如,函数可以分配内存并将其地址返回给调用者。
  3. 实现复杂数据结构:二级指针在实现链表、树和图等复杂数据结构时非常有用。例如,链表中的每个节点可以包含一个指向下一个节点的指针,而这些指针本身可以通过二级指针进行操作。
  4. 多级间接引用:在某些情况下,程序需要多级间接引用数据。二级指针允许通过多级指针访问和操作数据。
  5. 节省内存:虽然二级指针本身并不会直接节省内存,但它们可以帮助更有效地管理和分配内存。例如,通过动态分配内存,可以避免分配不必要的内存块,从而提高内存使用效率。

二级指针最常见的用法就是修改一级指针。

示例:

复制代码
#include<stdio.h>


void sparrowfly()
{
    printf("sparrowfly!\n");
}

void eaglefly()
{
    printf("eaglefly!\n");
}



typedef void (*impletation)();

struct bird *table[] = {
        eaglefly,
        sparrowfly
};

void change(struct function **x,int id)
{
    *x = table[id];
}


int main()
{
    impletation impll;
    struct function **twopinter = &impll;

    change(twopinter, 1);

    impll();


    return 0;
}

使用二级指针,这样就是传址调用,而不是传值调用了。

笔者也可以自己实验一下:

复制代码
void change(struct function *x,int id)
{
    x = table[id];
}


int main()
{
    impletation impll;
    struct function **twopinter = &impll;

    impll = eaglefly;
    change(impll, 1);

    impll();

    return 0;
}

传址调用对于多级指针也是成立的,毕竟笔者前面已经说过,指针的本质是变量。

多级指针还有一个好处:增加程序可扩展性和解耦合。

复制代码
#include<stdio.h>
#include<malloc.h>


typedef void (*fly)(struct bird **self);


struct bird{
    fly common;
};

struct sparrow{
    struct bird *interface;
    char name;
};

struct eagle{
    struct bird *interface;
    char name;
};



void sparrowfly()
{
    printf("sparrowfly!\n");
}

void eaglefly()
{
    printf("eaglefly!\n");
}


struct bird **fly_table[] = {
        (struct bird **)&(struct sparrow) {
                .interface = &(struct bird) {.common = sparrowfly},
                        .name = 's'
        },
        (struct bird **)&(struct eagle){
            .interface = &(struct bird){.common = eaglefly},
                    .name = 'e'
        }
};

void polymorphism(int opt)
{
    struct bird **p = fly_table[opt];
    (*p)->common(p);
}



int main()
{

    polymorphism(0);

    return 0;
}

这一段程序虽然self指针也可以被一级指针代替,但是当我们在main函数中修改一些局部指针变量时,二级指针的扩展性就体现出来了:

复制代码
#include<stdio.h>
#include<malloc.h>


typedef void (*fly)(struct bird **self);


struct bird{
    fly common;
};

struct sparrow{
    struct bird *interface;
    char name;
};

struct eagle{
    struct bird *interface;
    char name;
};



void sparrowfly()
{
    printf("sparrowfly!\n");
}

void eaglefly()
{
    printf("eaglefly!\n");
}


struct bird **fly_table[] = {
        (struct bird **)&(struct sparrow) {
                .interface = &(struct bird) {.common = sparrowfly},
                        .name = 's'
        },
        (struct bird **)&(struct eagle){
            .interface = &(struct bird){.common = eaglefly},
                    .name = 'e'
        }
};


void implement(struct bird **p)
{
    (*p)->common(p);
}



void polymorphism(int opt)
{
    struct bird **p = fly_table[opt];
    (*p)->common(p);
}


struct bird *table[] = {
        eaglefly,
        sparrowfly
};



void change(struct bird **self,int id)
{
    (*self)->common = table[id] ;
}



int main()
{
    struct sparrow sparrow1;
    struct bird bird1;
    sparrow1.interface = &bird1;

    struct bird **Ainterface ;
    Ainterface = malloc(sizeof(int));
    *Ainterface = sparrow1.interface;

    change(Ainterface,0);
    implement(Ainterface);

    polymorphism(0);

    return 0;
}

当然,指针也并不是越多级就越好,我们也看到了,指针的本质仍然是变量,只是对存储的值有一定要求而已。过多级数的指针并不会带来更好的程序优化,相反,可能使程序性能、可读性、可维护性变差。

相关推荐
·云扬·9 分钟前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
liulilittle40 分钟前
C++ i386/AMD64平台汇编指令对齐长度获取实现
c语言·开发语言·汇编·c++
Wilber的技术分享1 小时前
【机器学习实战笔记 14】集成学习:XGBoost算法(一) 原理简介与快速应用
人工智能·笔记·算法·随机森林·机器学习·集成学习·xgboost
Tanecious.1 小时前
LeetCode 876. 链表的中间结点
算法·leetcode·链表
Thomas_YXQ1 小时前
Unity URP法线贴图实现教程
开发语言·unity·性能优化·游戏引擎·unity3d·贴图·单一职责原则
打好高远球1 小时前
如何用AI破解相亲信息不对称
架构
Wo3Shi4七1 小时前
哈希冲突
数据结构·算法·go
Zz_waiting.1 小时前
Javaweb - 10.4 ServletConfig 和 ServletContext
java·开发语言·前端·servlet·servletconfig·servletcontext·域对象
呆呆的小鳄鱼2 小时前
cin,cin.get()等异同点[面试题系列]
java·算法·面试
Touper.2 小时前
JavaSE -- 泛型详细介绍
java·开发语言·算法