type
status
date
slug
summary
tags
category
icon
password

Cpp中的指针

令人头大的指针 😥

作为C系列的精髓之一,指针在C/Cpp中使用在经常不过了,但是这个东西有一点点抽象啊。我总是要翻来覆去的看定义,因此今天做一下小小的总结。
 

变量名就是地址!!!

这点就是理解整个地址的一个关键,好像每一个对于指针的讲解中,都会首先说这个道理,我这里也难落俗套,把这个概念在强调一遍。
变量名其实就是地址,其实只是在编写代码的过程中为了方便自己记忆和在编译过程中编译器进行编译而对地址进行一个“起外号”的工具。
首先我们还是拿出一段代码来看看:
运行结果
首先我们初始化一块内存区域,这块内存区域在计算机中叫做0x7ffe7b54849c,但是我们将这块内存叫做value_a,在这里value_a和0x7ffe7b54849c就是等价的,变量名就是地址!你去找value_a中存储数据的内容和去找0x7ffe7b54849c中存储数据的内容,在内存中找的是一块地方,对于计算机而言都是0x7ffe7b54849c。
 

*与&

在明确地址和变量名的关系之后,可以进一步的研究一些其他的细节了。首先我们需要认识这样一对运算符
*和&

& 地址运算符(引用运算符)

首先说说&
&是地址运算符,通过&,我们可以获得变量名对应的地址。例如将上面的程序稍加修改,得到下面的程序
我们就能得到下面的输出
也就是value_a对应的地址
 

*间接运算符(取消引用运算符)

在代码段1中,我们有这么一句注释掉的程序
这里实际上是提出了一个问题,就是既然我的引用运算符&能够直接获得这个变量的地址,那么我可不可以直接将这个地址作为一个新的变量来存到另一个内存区域中呢?这个问题我们后面在聊聊,这里先强制的规定一下,引用运算符出来的地址我们也只能放到一个地址类的变量中,那么这个地址类的变量的定义就是通过 int*这个操作实现的,也就是定义一个int型的内存空间,这个内存空间的名字叫做pointer_a, 而定义完成之后的变量名pointer_a存储的数据就是value_a的地址0x7ffe7b54849c。那我们通过取消引用*pointer_a,也就是获取地址中的变量就能获得0x7ffe7b54849c也就是value_a的值。
所以实际上*pointer_a=value_a=*0x7ffe7b54849c
而pointer_a也是一个变量名,也有他自己的地址,我们可以在程序段1中的输出看到这个地址是0x7ffe7b5484a0,而且我们直接输出pointer_a他的值也确实是0x7ffe7b54849c。
 
所以我们得到了一个结论,就是变量名就是地址,我们想要获得一个变量名的地址就去引用他(&)。我们想要获得一个地址中的值就去反引用他(*)。并且我们每一次获得一个新的地址的值的时候,就要初始化一个能够储存指针类型的内存来存储指针,这个就是指针的初始化。
 

指针的最后一点点小问题 🧐

刚才我一直将&成为引用,将*称为反引用。这里的称呼其实是有一点点鸡生蛋生鸡的困扰的。也就是我们注释掉的那行代码,为什么我不能直接将一个指针赋值给一个变量呢?为什么一定要初始化一个之类类型的变量呢?
也许就是非常直接的类型要求 😜
这样子思考也许就非常简单了,比如我们既然有1,那为什么还要初始化一个数字类型int呢?为什么还要有double,float这些类型呢?
因为这些类型在内存中的表示不同,同样都表示1,但是int,double和float是不同的储存方式。所以对于地址变量,虽然他在值上是可以表示为整数的。但是指针变量还是有很多自己独特的运算形式的,尽管这种运算形式例如加减等似乎和整数是重合的,但是大部分还是不一样的。所以我们需要一个专门的数据类型来表示指针。
所以我喜欢将*称为反引用 🤔
尽管在逻辑上来说*和&好像是一个鸡生蛋蛋生鸡问题,究竟是为了先访问地址中的变量呢还是为了获得存储变量的地址呢?这里我抛开这个头大的问题,来个简单粗暴的。
我们既然想要更加灵活的操作内存,比如定义一个数组,但是我不确定这个数组的长度,我就可以只通过数组头的地址来访问整个数组。
那么首先我们就是需要获得一个地址,那这个地址就需要通过引用&获得,而为了存储这个地址,我们需要定义一个能够存储地址的变量int* a。而这里的*是为了&服务的。所以反引用对于一个程序而言更加的顺畅。
 
ROS1与ROS2之间的rosbga转换小湖一游