数据结构总结之 指针篇

举报
看,未来 发表于 2021/01/03 20:36:06 2021/01/03
【摘要】 指针

指针和动态内存分配


指针是C语言的基本概念,C语言中指针无处不在。实际上,每种数据类型,都有相应的指向T的指针类型。
指针类型变量存放的值,实际上就是内存地址。指针类型有两个最基本的操作:


&:取地址操作 

*:去引用 (间接引用)操作


引用&


首先,&不是地址运算符,而是类型标识符的一种,就像*也不是指针运算符一样。

就像char* 意为指向char的指针一样,int& 意为指向int 的引用。

栗子来一颗:


int a;

int &at = a;

//上述声明允许将at和a互换,它们指向相同的值和内存单元,就像连体婴一样。


  

上面这个栗子其实很有内涵在里面
我为什么不写成下面这个形式呢?


int a;

int &at;

at = a;


在指针中是可以的,但是&不允许,&必须在声明时将其初始化。

引用经常被用作函数参数,使得函数中的变量名成为调用程序中变量的别名。这种调用方法我一直搞得晕晕的,正好这次一次性根除。这种传递参数的方法称为按引用传递。按引用传递允许被调用函数能够访问调用函数中的变量。这是C++相比C的一个超越。
来个经典的栗子:


void swap_a(int &a,int &b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}

//顺便来个指针的

void swap_b(int *a,int *b)
{
    int temp;
    temp = *a;    //a,b是指针,*a,*b才是int
    *a = *b;
    *b = temp;
}

int main()
{
    int a = 1;
    int b = 2;
    int c = 3;
    int d = 4;

    swap_a(a,b);    //看仔细咯,这个是引用调用
    swap_b(&a,&b);    //看仔细咯,这个是指针调用

    //如果理解不了,这样理解:参数中的*和&只是走个过场,告诉人家那个参数是什么类型的
    //调用函数时的参数是a,不是*a,也不是&a
    //所以&a传的这个a是一个int类型,而*a的这个a就是指针,地址,所以要取地址传给它

    return 0;

}

如果你的意图是让函数使用传给它的信息,又不想把这些信息进行改动,那么应该使用const。
将引用参数声明为const数据的好处有这些:

防止无意中被修改。
使用const参数可以兼容非const传参。


将引用用于结构

C++引入引用主要就是为了和结构和类。
它还通过让函数返回指向结构的引用而增添了一个有趣的特点,这与返回结构有所不同。


//代码太长,放段伪代码吧

struct Str
{
};

Str& test(Str &a,const Str &b)    
{
    //从b中取值,对a进行填充
    return a;//其实可以做void类型,没必要多此一举
}

int main()
{
    Str a,b,c;
    //b是有初值的,这是伪代码
    c = test(a,b);
    return 0;
}

如果test函数返回一个结构,而不是指向结构的引用,相当于把整个结构体复制到一个临时位置,再将这个拷贝复制给c,但是现在返回值为引用,将直接将a复制到c,效率更高

返回引用时最重要的一点是:应避免返回函数终止时将不再存在的内存单元的引用
下面是一个反面教材:

Str& test(const Str &d)
{
    Str &e;
    ···
    return e;
}



何时使用引用参数?



程序员能够修改调用函数中的数据对象。
通过传递引用而不是整个数据对象,可以提高程序的运行速度。

指针

指针和const

将const用于指针有一些很微妙的地方。
可以用两种不同的方式将const关键字用于指针。

int age = 20; const int * pt = &age;

//该声明指出,pt指向一个const int,因此不能使用pt来修改这个值。
//现在来看一个很微妙的问题:其实age并不是一个常量,只是对于pt来说,它是一个常量。
//就是说age可以改,只不过不能用pt来改而已。

注意点:不允许将常量数据赋值给非常量指针,个中理由就不用多解释了吧。


const int age = 20; int * pt = &age;
int sloth = 80; int * const finger = &sloth;
#这种声明格式使得这个指针只能指向sloth,不过可以通过这个指针修改sloth的值。


通过指针返回字符串的函数

现在,假设需要一个返回字符串的函数,是的,函数无法返回一个字符串,但是可以返回字符串的地址,这样效率更高。

void test(char *rc)
{
    ···
    memset(rc,字符串);
    ···
}

相当于是使用回调函数,我个人比较喜欢这一套模式。

通过指针返回结构

具体操作参考第二点。
当然,这里还有另外的应用场景:

void test2(const JieGouTi1 *a,JieGouTi2 *b)
{
    //将a中的某些值赋值给b
}
//这里有一个注意点,传进去赋值的结构体指针最好用const.



函数指针

函数指针完成任务的流程是这样的:

> 获取函数的地址 
> 声明一个函数指针 
> 使用函数指针来调用函数 
> 获取函数地址

获取函数地址那是比较简单的事,如果说 void Hanshu();这是一个函数,那么它的地址就是 Hanshu。
如果函数Hanshubaba();要调用这个函数,是这样的:`Hanshubaba(Hanshu);`
切记不能写成:`Hanshubaba(Hanshu());`

声明函数指针


假设现在有这么一个函数:`int test3(void *arg);    //这个arg参数,回调函数里面用,要解释有点长。`
现在要将之改成函数指针形式:`int (*test3)(void *arg);`

首先,将test3更换成(*test3),因此,(*test3)也是函数,那么test3就是函数指针。
为声明优先级,需要将 *test3 括号起来。


函数指针用武之地

如果你非要我说函数指针存在的意义,那我也真不好给你扯个所以然出来,那我就,举几个用得到的地方吧:

自定义排序/搜索

不同的模式(如策略,观察者)

回调


关于指针的一些思考

前面说到,将指针作为参数传入,在函数内部对指针进行修改,函数结束后指针的修改将被保留。
因为指针传参代表着地址传参。

解惑:如何让对指针参数的修改不被保存。

看个栗子:


class B {
    char* b;

public:
    B() {
        b = new char[5];

        strcpy(b,"aaaa");
    }
    char* get_b() { return b; }
};

class A {
private:
    char* a;

public:
    A(B* temp) { a = temp->get_b(); };

    void set_A() { 
        strcpy(a, "kkkk");    //顶替掉了
    }
};

int main() {
    B* b = new B();
    A* a = new A(b);
    a->set_A();
    cout << b->get_b() << endl;
    return 0;
}


结局打印出来的 b,就是“kkkk”。
那为什么会这样?前面解释过了,a、b都是对内存地址的映射,对a进行修改,就是对地址上的数据进行修改,而b只不过是地址的一个映射而已,读取b,就是读取地址上的东西,那本质已经被改了,读出来的东西自然不一样。

再看个例子:

void Del (POINT_T * the_head, int index)
{
    POINT_T *pFree=NULL;
    POINT_T *pNode=the_head;
    int flag=0;

    while (pNode->next!=NULL)
    {
        if(flag==index-1)
        {
            pFree=pNode->next;                //再指向数据域就爆了
            pNode->next=pNode->next->next;
            free(pFree->pData);
            free(pFree);
            break;
        }
        pNode=pNode->next;
        flag++;    
    }    
}

这是链表的一个例子,那可能会纳闷儿,为什么对 pNode执行了 `pNode=pNode->next;`操作,而`the_head`却没有跟着变呢?
原因很简单,pNode->next也是一个映射地址,这句话的意思就是用一个新的地址映射,顶替掉那个旧的,使得指针pNode指向一块新的地址,和the_head失去联系。





【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。