指针澄清的指针

Bla*_*ake 141 c pointers

我正在按照本教程关于指针如何工作.

让我引用相关段落:


    int i = 5, j = 6, k = 7;
    int *ip1 = &i, *ip2 = &j;
Run Code Online (Sandbox Code Playgroud)

现在我们可以设置

    int **ipp = &ip1;
Run Code Online (Sandbox Code Playgroud)

ipp指向ip1哪些点i.*ippis ip1,and **ippis i,or 5.我们可以用我们熟悉的盒子和箭头符号来说明这种情况,如下所示:

在此输入图像描述

如果那时我们说

    *ipp = ip2;
Run Code Online (Sandbox Code Playgroud)

我们已经改变了指向ipp(即ip1)的指针以包含一个副本ip2,因此it(ip1)现在指向j:

在此输入图像描述


我的问题是:为什么在第二张图片中ipp仍然指向ip1但不是ip2

Rob*_*nes 142

忘记关于指向类比的第二个问题.指针真正包含的是内存地址.的&是操作者"的地址" -即它在一个对象的存储器返回地址.该*运营商给你的对象的指针指,即给予包含地址的指针,在这一点内存地址返回的对象.所以,当你做*ipp = ip2,你在做什么是*ipp获取对象举行的地址在ippip1,然后分配给ip1存储于价值ip2,这是地址j.

简单
& - >地址
*- >价值在

  • &和*从未如此简单 (14认同)
  • 我认为混淆的主要原因是*运算符的模糊性,它在变量声明期间用于指示变量实际上是指向某种数据类型的指针.但是,另一方面,它也在语句中用于访问指针所指向的变量的内容(解除引用运算符). (7认同)

Ski*_*izz 43

因为您更改了指向ipp的值而不是值ipp.因此,ipp仍指向ip1(值ipp),ip1现在的值与值相同ip2,因此它们都指向j.

这个:

*ipp = ip2;
Run Code Online (Sandbox Code Playgroud)

是相同的:

ip1 = ip2;
Run Code Online (Sandbox Code Playgroud)

  • 可能值得指出`int*ip1 =&i`和`*ipp = ip2;`之间的区别,即如果你从第一个语句中删除`int`那么赋值看起来非常相似,但是`*`正在做这两种情况有很大不同. (11认同)

mic*_*ang 21

希望这段代码可以提供帮助.

#include <iostream>
#include <stdio.h>
using namespace std;

int main()
{
    int i = 5, j = 6, k = 7;
    int *ip1 = &i, *ip2 = &j;
    int** ipp = &ip1;
    printf("address of value i: %p\n", &i);
    printf("address of value j: %p\n", &j);
    printf("value ip1: %p\n", ip1);
    printf("value ip2: %p\n", ip2);
    printf("value ipp: %p\n", ipp);
    printf("address value of ipp: %p\n", *ipp);
    printf("value of address value of ipp: %d\n", **ipp);
    *ipp = ip2;
    printf("value ipp: %p\n", ipp);
    printf("address value of ipp: %p\n", *ipp);
    printf("value of address value of ipp: %d\n", **ipp);
}
Run Code Online (Sandbox Code Playgroud)

它输出:

在此输入图像描述


Eri*_*ert 21

像C标签中的大多数初学者问题一样,这个问题可以通过回到第一原则来回答:

  • 指针是一种值.
  • 变量包含值.
  • &操作者打开一可变成指针.
  • *操作者接通一个指针到一个变量.

(从技术上讲,我应该说"左值"而不是"变量",但我觉得将可变存储位置描述为"变量"更为明确.)

所以我们有变量:

int i = 5, j = 6;
int *ip1 = &i, *ip2 = &j;
Run Code Online (Sandbox Code Playgroud)

变量ip1 包含一个指针.在&操作者接通i到一个指针和该指针值被分配给ip1.所以ip1 包含一个指针i.

变量ip2 包含一个指针.在&操作者接通j到一个指针和该指针被分配给ip2.所以ip2 包含一个指针j.

int **ipp = &ip1;
Run Code Online (Sandbox Code Playgroud)

变量ipp包含一个指针.在&操作者接通变量ip1成一个指针和该指针值被分配给ipp.所以ipp包含一个指针ip1.

让我们总结一下这个故事:

  • i 包含5
  • j 包含6
  • ip1包含"指针i"
  • ip2包含"指针j"
  • ipp包含"指针ip1"

现在我们说

*ipp = ip2;
Run Code Online (Sandbox Code Playgroud)

*操作者打开一指针返回到一个变量.我们获取值ipp,这是"指针ip1并将其转换为变量.ip1当然,什么变量?

因此,这只是另一种说法

ip1 = ip2;
Run Code Online (Sandbox Code Playgroud)

所以我们获取的值ip2.它是什么?"指向j".我们将指针值赋给ip1,所以ip1现在是"指针j"

我们只改变了一件事:价值ip1:

  • i 包含5
  • j 包含6
  • ip1包含"指针j"
  • ip2包含"指针j"
  • ipp包含"指针ip1"

为什么ipp还指向ip1而不是ip2

分配给变量时,变量会发生变化.计算作业; 变量没有比赋值更多的变化!您可以通过分配开始i,j,ip1,ip2ipp.然后你分配给*ipp,我们已经看到的意思与"分配给ip1" 相同.由于你没有ipp第二次分配,它没有改变!

如果你想改变ipp那么你必须实际分配给ipp:

ipp = &ip2;
Run Code Online (Sandbox Code Playgroud)

例如.


Lun*_*din 12

我个人的观点是,箭头指向这种方式的图片或指针更难以理解.它确实使它们看起来像一些抽象的,神秘的实体.他们不是.

与计算机中的其他所有内容一样,指针是数字.名称"指针"只是一种说"含有地址的变量"的奇特方式.

因此,让我通过解释计算机实际工作原理来解决问题.

我们有一个int,它有名称i和值5.它存储在内存中.就像存储在内存中的所有内容一样,它需要一个地址,否则我们将无法找到它.让我们说i最终在地址0x12345678,其伙伴j价值6在它之后结束.假设32位CPU,其中int是4个字节,指针是4个字节,那么变量存储在物理内存中,如下所示:

Address     Data           Meaning
0x12345678  00 00 00 05    // The variable i
0x1234567C  00 00 00 06    // The variable j
Run Code Online (Sandbox Code Playgroud)

现在我们想指出这些变量.我们创建一个指向int int* ip1,和一个的指针int* ip2.就像计算机中的所有内容一样,这些指针变量也会在内存中的某处分配.让我们假设它们最终会在内存中的下一个相邻地址结束j.我们设置指针以包含先前分配的变量ip1=&i;的地址:("将i的地址复制到ip1")和ip2=&j.这些线之间发生的事情是:

Address     Data           Meaning
0x12345680  12 34 56 78    // The variable ip1(equal to address of i)
0x12345684  12 34 56 7C    // The variable ip2(equal to address of j)
Run Code Online (Sandbox Code Playgroud)

所以我们得到的只是一些包含数字的4字节内存块.在任何地方都没有任何神秘或神奇的箭头.

实际上,仅通过查看内存转储,我们无法判断地址0x12345680是否包含intint*.不同之处在于我们的程序选择如何使用存储在此地址的内容.(我们程序的任务实际上只是告诉CPU如何处理这些数字.)

然后我们添加另一个间接级别int** ipp = &ip1;.再一次,我们只获得了一大块内存:

Address     Data           Meaning
0x12345688  12 34 56 80    // The variable ipp
Run Code Online (Sandbox Code Playgroud)

这种模式似乎很熟悉.另一个包含数字的4个字节的块.

现在,如果我们有一个上述虚构的小RAM的内存转储,我们可以手动检查这些指针指向的位置.我们查看存储在ipp变量地址的内容并找到内容0x12345680.这当然ip1是存储地址.我们可以去那个地址,检查那里的内容,找到地址i,最后我们可以到那个地址找到号码5.

因此,如果我们获取ipp的内容*ipp,我们将获得指针变量的地址ip1.通过编写*ipp=ip2我们将ip2复制到ip1,它相当于ip1=ip2.无论哪种情况,我们都会得到

Address     Data           Meaning
0x12345680  12 34 56 7C    // The variable ip1
0x12345684  12 34 56 7C    // The variable ip2
Run Code Online (Sandbox Code Playgroud)

(这些示例是为大端CPU提供的)

  • 虽然我理解你的观点,但将指针视为抽象的,神秘的实体是有价值的.指针的任何特定*实现*只是数字,但您绘制的实现策略不是实现的*要求*,它只是一种常见的策略.指针不需要与int相同,指针不必是平面虚拟内存模型中的地址,依此类推; 这些仅仅是实施细节. (5认同)

Dip*_*pto 8

注意分配:

ipp = &ip1;
Run Code Online (Sandbox Code Playgroud)

结果ipp指向ip1.

所以为了ipp指出ip2,我们应该以类似的方式改变,

ipp = &ip2;
Run Code Online (Sandbox Code Playgroud)

我们显然没有这样做.相反,我们正在改变指向的地址ipp.
通过做下面的事情

*ipp = ip2;
Run Code Online (Sandbox Code Playgroud)

我们只是替换存储的值ip1.

ipp = &ip1,意思是*ipp = ip1 = &i,
现在,*ipp = ip2 = &j.
所以,*ipp = ip2基本上是一样的ip1 = ip2.


Dan*_*nas 5

ipp = &ip1;
Run Code Online (Sandbox Code Playgroud)

以后的任务没有改变它的价值ipp.这就是为什么它仍然指向ip1.

你所做的事情*ipp,即与之相关ip1,并没有改变ipp指向的事实ip1.


小智 5

因为当你说

*ipp = ip2
Run Code Online (Sandbox Code Playgroud)

你说的是'指向的对象ipp'指向指向的内存方向ip2.

你不是说ipp要指出的ip2.


zmo*_*zmo 5

我的问题是:为什么在第二张图片中,ipp仍然指向ip1而不是ip2?

你放了漂亮的照片,我打算做一个漂亮的ascii艺术:

就像@ Robert-S-Barnes在他的回答中所说:忘记指针,什么指向什么,但从记忆的角度来思考.基本上,它int*表示它包含变量的地址,并且int**包含包含变量地址的变量的地址.然后你可以使用指针的代数来访问值或地址:&foo意思address of foo*foo手段value of the address contained in foo.

因此,作为关于处理内存的指针,实际制作"有形"的最佳方法是显示指针代数对内存的作用.

所以,这是你的程序的内存(为了示例的目的简化):

name:    i   j ip1 ip2 ipp
addr:    0   1   2   3   4
mem : [   |   |   |   |   ]
Run Code Online (Sandbox Code Playgroud)

当你做初始代码时:

int i = 5, j = 6;
int *ip1 = &i, *ip2 = &j;
Run Code Online (Sandbox Code Playgroud)

这是你的记忆的样子:

name:    i   j ip1 ip2
addr:    0   1   2   3
mem : [  5|  6|  0|  1]
Run Code Online (Sandbox Code Playgroud)

那里,你可以看到ip1ip2得到的地址ijipp仍然不存在.不要忘记,地址只是以特殊类型存储的整数.

然后你声明并定义ipp如下:

int **ipp = &ip1;
Run Code Online (Sandbox Code Playgroud)

所以这是你的记忆:

name:    i   j ip1 ip2 ipp
addr:    0   1   2   3   4
mem : [  5|  6|  0|  1|  2]
Run Code Online (Sandbox Code Playgroud)

然后,您将更改存储在ipp其中的地址所指向的值,即存储在ip1以下位置的地址:

*ipp = ip2;
Run Code Online (Sandbox Code Playgroud)

程序的记忆是

name:    i   j ip1 ip2 ipp
addr:    0   1   2   3   4
mem : [  5|  6|  1|  1|  2]
Run Code Online (Sandbox Code Playgroud)

注意:作为int*一种特殊类型,我更喜欢总是避免在同一行上声明多个指针,因为我认为int *x;或者int *x, *y;表示法可能会产生误导.我更喜欢写int* x; int* y;

HTH