关于如何指向指针的指针,我正在关注这个tutorial .
让我引用相关段落:
int i = 5,j = 6,k = 7;
int * ip1 =&i,* ip2 =&j;
现在我们可以设置int ** ipp =&ip1;
和ipp指向ip1,指向i . * ipp是ip1,** ipp是i,或5.我们可以用我们熟悉的盒子和箭头表示法来说明情况,如下所示:如果那时我们说* ipp = ip2;
我们已经改变了ipp指向的指针(即ip1)来包含ip2的副本,所以它(ip1)现在指向j:
我的问题是:为什么在第二张图片中, ipp
仍然指向 ip1
而不是 ip2
?
16 回答
以后的分配没有改变
ipp
的值 . 这就是为什么它仍然指向ip1
.你用
*ipp
做什么,即ip1
,并没有改变ipp
指向ip1
的事实 .忘记关于指向类比的第二个问题 . 指针真正包含的是内存地址 .
&
是"address of"运算符 - 即它返回对象内存中的地址 .*
运算符为您提供指针引用的对象,即给定包含地址的指针,它返回该存储器地址处的对象 . 所以当你执行*ipp = ip2
时,你正在做的是*ipp
获取ipp
中保存的地址ip1
的对象,然后将ip2
中存储的值分配给ip1
,这是j
的地址 .Simply
&
- >地址*
- > Value 在因为您更改了
ipp
指向的值而不是ipp
的值 . 所以,ipp
仍指向ip1
(ipp
的值),ip1
的值现在与ip2
的值相同,因此它们都指向j
.这个:
是相同的:
希望这段代码可以提供帮助 .
它输出:
像C标签中的大多数初学者问题一样,这个问题可以通过回到第一原则来回答:
指针是一种值 .
变量包含值 .
&
运算符将变量转换为指针 .*
运算符将指针转换为变量 .(从技术上讲,我应该说“左值”而不是“变量”,但我觉得将可变存储位置描述为“变量”更为明确 . )
所以我们有变量:
变量
ip1
包含一个指针 .&
运算符将i
变为指针,该指针值分配给ip1
. 所以ip1
包含一个指向i
的指针 .变量
ip2
包含一个指针 .&
运算符将j
变为指针,该指针分配给ip2
. 所以ip2
包含一个指向j
的指针 .变量
ipp
包含一个指针 .&
运算符将变量ip1
变为指针,该指针值分配给ipp
. 所以ipp
包含一个指向ip1
的指针 .让我们总结一下这个故事:
i
包含5j
包含6ip1
包含“指向i
的指针”ip2
包含“指向j
的指针”ipp
包含“指向ip1
的指针”现在我们说
*
运算符将指针变回变量 . 我们获取ipp
的值,这是“指向ip1
的指针并将其转换为变量 . 什么变量?ip1
当然!因此,这只是另一种说法
所以我们获取
ip2
的值 . 它是什么? “指向j
的指针” . 我们将指针值分配给ip1
,所以ip1
现在是“指向j
的指针”我们只改变了一件事:
ip1
的值:i
包含5j
包含6ip1
包含“指向j
的指针”ip2
包含“指向j
的指针”ipp
包含“指向ip1
的指针”分配给变量时,变量会发生变化 . 计算作业;变量没有比赋值更多的变化!首先分配到
i
,j
,ip1
,ip2
和ipp
. 然后分配给*ipp
,正如我们所见,它意味着与“分配给ip1
”相同 . 由于你没有第二次分配到ipp
,它没有改变!如果您想要更改
ipp
,那么您必须实际分配到ipp
:例如 .
我个人的观点是,箭头指向这种方式的图片或使指针更难理解的图片 . 它确实使它们看起来像一些抽象的,神秘的实体 . 他们不是 .
像计算机中的其他所有内容一样,指针就是数字 . 名称"pointer"只是一种说法"a variable containing an address"的奇特方式 .
因此,让我通过解释计算机实际工作原理来解决问题 .
我们有一个
int
,它的名字是i
,值是5.它存储在内存中 . 就像存储在内存中的所有内容一样,它需要一个地址,否则我们将无法找到它 . 让我们说i
最终在地址0x12345678和它的伙伴j
与值6结束只是在它之后 . 假设一个32位CPU,其中int是4个字节,指针是4个字节,那么变量存储在物理内存中,如下所示:现在我们想指出这些变量 . 我们创建一个指向int,
int* ip1
和一个int* ip2
的指针 . 就像计算机中的所有内容一样,这些指针变量也会在内存中的某处分配 . 假设它们最终在j
之后立即在内存中的下一个相邻地址处结束 . 我们将指针设置为包含先前分配的变量的地址:ip1=&i;
("copy the address of i into ip1")和ip2=&j
. 这些行之间发生的事情是:所以我们得到的只是包含数字的一些4字节内存块 . 在任何地方都没有任何神秘或神奇的箭头 .
实际上,仅通过查看内存转储,我们无法判断地址0x12345680是否包含
int
或int*
. 不同之处在于我们的程序选择如何使用存储在此地址的内容 . (我们程序的任务实际上只是告诉CPU如何处理这些数字 . )然后我们用
int** ipp = &ip1;
添加另一个间接层 . 再一次,我们只获得了一大块内存:这种模式似乎很熟悉 . 另一个包含数字的4个字节的块 .
现在,如果我们有一个上述虚构的小RAM的内存转储,我们可以手动检查这些指针指向的位置 . 我们查看存储在
ipp
变量地址的内容并找到内容0x12345680 . 这当然是存储ip1
的地址 . 我们可以去那个地址,检查那里的内容,找到i
的地址,最后我们可以去那个地址找到5号 .因此,如果我们获取ipp的内容,
*ipp
,我们将得到指针变量ip1
的地址 . 通过写*ipp=ip2
我们将ip2复制到ip1,它相当于ip1=ip2
. 无论哪种情况,我们都会得到(这些示例是为大端CPU提供的)
注意分配:
结果
ipp
指向ip1
.所以
ipp
指向ip2
,我们应该以类似的方式改变,我们显然没有这样做 . 相反,我们正在更改
ipp
指向的 value at address .通过做下面的事情
我们只是替换
ip1
中存储的值 .ipp = &ip1
,表示*ipp = ip1 = &i
,现在,
*ipp = ip2 = &j
.所以,
*ipp = ip2
与ip1 = ip2
基本相同 .你放了漂亮的照片,我打算做一个漂亮的ascii艺术:
就像@ Robert-S-Barnes在他的回答中所说:忘记指针,什么指向什么,但从记忆的角度来思考 . 基本上,
int*
表示它包含变量的地址,int**
包含包含变量地址的变量的地址 . 然后你可以使用指针的代数来访问值或地址:&foo
表示address of foo
,*foo
表示value of the address contained in foo
.因此,作为关于处理内存的指针,实际制作“有形”的最佳方法是显示指针代数对内存的作用 .
所以,这是你的程序的内存(为了示例的目的而简化):
当你做初始代码时:
这是你的记忆的样子:
在那里你可以看到
ip1
和ip2
获取i
和j
的地址,而ipp
仍然不存在 . 不要忘记,地址只是以特殊类型存储的整数 .然后你声明并定义
ipp
,例如:所以这是你的记忆:
然后,您将更改
ipp
中存储的地址所指向的值,该地址存储在ip1
中:程序的记忆是
N.B . :因为
int*
是一种特殊类型,我更喜欢总是避免在同一行上声明多个指针,因为我认为int *x;
或int *x, *y;
符号可能会产生误导 . 我更喜欢写int* x; int* y;
HTH
因为当你说
你
ipp
'指向的对象指向ip2
指向的内存方向 .你不是说
ipp
指向ip2
.如果将取消引用运算符
*
添加到指针,则从指针重定向到指向对象 .例子:
因此:
如果你想
ipp
指向ip2
,你必须说ipp = &ip2;
. 但是,这将使ip1
仍然指向i
.你刚开始设定,
现在取消引用它,
考虑每个变量如下所示:
所以你的变量应该表示为这个
由于
ipp
的值是&ip1
所以结构如下:将地址
&ip1
的值更改为ip2
的值,这意味着ip1
已更改:但
ipp
仍然:所以
ipp
的值仍然是&ip1
,这意味着它仍然指向ip1
.因为您正在更改
*ipp
的指针 . 它的意思是ipp
(可变名称)----进去吧 .里面
ipp
是ip1
的地址 .现在
*ipp
所以去(里面的地址)ip1
.现在我们在
ip1
.*ipp
(即ip1
)=ip
2 .ip2
包含地址j
.soip1
内容将被包含ip2替换(即j的地址),我们不会更改ipp
内容 . 而已 .*ipp = ip2;
暗示:将
ip2
分配给ipp
指向的变量 . 所以这相当于:如果您希望将
ip2
的地址存储在ipp
中,只需执行以下操作:现在
ipp
指向ip2
.ipp
可以保存指向指针类型对象的指针(即指向) . 当你这样做那么
ipp
包含 address of the variable (pointer) ip2 ,它是指向指针的类型指针(&ip2
) . 现在第二张图片中ipp
的箭头将指向ip2
.Wiki说:
*
运算符是一个解引用运算符,对指针变量进行操作,并返回与指针地址处的值等效的l-value(变量) . 这称为解除引用指针 .将
*
运算符应用于ipp
derefrence它到指向int
类型的指针的l值 . 取消引用的l值*ipp
是指向int
的类型指针,它可以保存int
类型数据的地址 . 声明之后ipp
持有ip1
的地址,*ipp
持有(指向)i
的地址 . 你可以说*ipp
是ip1
的别名 .**ipp
和*ip1
都是i
的别名 .通过做
*ipp
和ip2
都指向同一位置,但ipp
仍然指向ip1
.实际上
*ipp = ip2;
的作用是将ip2
(j
的地址)的内容复制到ip1
(因为*ipp
是ip1
的别名),实际上使指针ip1
和ip2
指向同一个对象(j
) .所以,在第二个图中, arrow of ip1 and ip2 is pointing to j while ipp is still pointing to ip1 as no modification is done to change the value of ipp .