首页 文章

指针澄清的指针

提问于
浏览
138

关于如何指向指针的指针,我正在关注这个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 回答

  • 5
    ipp = &ip1;
    

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

    你用 *ipp 做什么,即 ip1 ,并没有改变 ipp 指向 ip1 的事实 .

  • 1

    忘记关于指向类比的第二个问题 . 指针真正包含的是内存地址 . & 是"address of"运算符 - 即它返回对象内存中的地址 . * 运算符为您提供指针引用的对象,即给定包含地址的指针,它返回该存储器地址处的对象 . 所以当你执行 *ipp = ip2 时,你正在做的是 *ipp 获取 ipp 中保存的地址 ip1 的对象,然后将 ip2 中存储的值分配给 ip1 ,这是 j 的地址 .

    Simply
    & - >地址
    * - > Value 在

  • 1

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

    这个:

    *ipp = ip2;
    

    是相同的:

    ip1 = ip2;
    
  • 4

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

    #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);
    }
    

    它输出:

    enter image description here

  • 3

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

    • 指针是一种值 .

    • 变量包含值 .

    • & 运算符将变量转换为指针 .

    • * 运算符将指针转换为变量 .

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

    所以我们有变量:

    int i = 5, j = 6;
    int *ip1 = &i, *ip2 = &j;
    

    变量 ip1 包含一个指针 . & 运算符将 i 变为指针,该指针值分配给 ip1 . 所以 ip1 包含一个指向 i 的指针 .

    变量 ip2 包含一个指针 . & 运算符将 j 变为指针,该指针分配给 ip2 . 所以 ip2 包含一个指向 j 的指针 .

    int **ipp = &ip1;
    

    变量 ipp 包含一个指针 . & 运算符将变量 ip1 变为指针,该指针值分配给 ipp . 所以 ipp 包含一个指向 ip1 的指针 .

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

    • i 包含5

    • j 包含6

    • ip1 包含“指向 i 的指针”

    • ip2 包含“指向 j 的指针”

    • ipp 包含“指向 ip1 的指针”

    现在我们说

    *ipp = ip2;
    

    * 运算符将指针变回变量 . 我们获取 ipp 的值,这是“指向 ip1 的指针并将其转换为变量 . 什么变量? ip1 当然!

    因此,这只是另一种说法

    ip1 = ip2;
    

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

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

    • i 包含5

    • j 包含6

    • ip1 包含“指向 j 的指针”

    • ip2 包含“指向 j 的指针”

    • ipp 包含“指向 ip1 的指针”

    为什么ipp仍然指向ip1而不是ip2?

    分配给变量时,变量会发生变化 . 计算作业;变量没有比赋值更多的变化!首先分配到 ijip1ip2ipp . 然后分配给 *ipp ,正如我们所见,它意味着与“分配给 ip1 ”相同 . 由于你没有第二次分配到 ipp ,它没有改变!

    如果您想要更改 ipp ,那么您必须实际分配到 ipp

    ipp = &ip2;
    

    例如 .

  • 5

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

    像计算机中的其他所有内容一样,指针就是数字 . 名称"pointer"只是一种说法"a variable containing an address"的奇特方式 .

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

    我们有一个 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
    

    现在我们想指出这些变量 . 我们创建一个指向int, int* ip1 和一个 int* ip2 的指针 . 就像计算机中的所有内容一样,这些指针变量也会在内存中的某处分配 . 假设它们最终在 j 之后立即在内存中的下一个相邻地址处结束 . 我们将指针设置为包含先前分配的变量的地址: ip1=&i; ("copy the address of i into 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)
    

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

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

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

    Address     Data           Meaning
    0x12345688  12 34 56 80    // The variable ipp
    

    这种模式似乎很熟悉 . 另一个包含数字的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
    

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

  • 43

    注意分配:

    ipp = &ip1;
    

    结果 ipp 指向 ip1 .

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

    ipp = &ip2;
    

    我们显然没有这样做 . 相反,我们正在更改 ipp 指向的 value at address .
    通过做下面的事情

    *ipp = ip2;
    

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

    ipp = &ip1 ,表示 *ipp = ip1 = &i
    现在, *ipp = ip2 = &j .
    所以, *ipp = ip2ip1 = ip2 基本相同 .

  • 21

    我的问题是:为什么在第二张图片中,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 : [   |   |   |   |   ]
    

    当你做初始代码时:

    int i = 5, j = 6;
    int *ip1 = &i, *ip2 = &j;
    

    这是你的记忆的样子:

    name:    i   j ip1 ip2
    addr:    0   1   2   3
    mem : [  5|  6|  0|  1]
    

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

    然后你声明并定义 ipp ,例如:

    int **ipp = &ip1;
    

    所以这是你的记忆:

    name:    i   j ip1 ip2 ipp
    addr:    0   1   2   3   4
    mem : [  5|  6|  0|  1|  2]
    

    然后,您将更改 ipp 中存储的地址所指向的值,该地址存储在 ip1 中:

    *ipp = ip2;
    

    程序的记忆是

    name:    i   j ip1 ip2 ipp
    addr:    0   1   2   3   4
    mem : [  5|  6|  1|  1|  2]
    

    N.B . :因为 int* 是一种特殊类型,我更喜欢总是避免在同一行上声明多个指针,因为我认为 int *x;int *x, *y; 符号可能会产生误导 . 我更喜欢写 int* x; int* y;

    HTH

  • 8

    因为当你说

    *ipp = ip2
    

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

    你不是说 ipp 指向 ip2 .

  • 0

    如果将取消引用运算符 * 添加到指针,则从指针重定向到指向对象 .

    例子:

    int i = 0;
    int *p = &i; // <-- N.B. the pointer declaration also uses the `*`
                 //     it's not the dereference operator in this context
    *p;          // <-- this expression uses the pointed-to object, that is `i`
    p;           // <-- this expression uses the pointer object itself, that is `p`
    

    因此:

    *ipp = ip2; // <-- you change the pointer `ipp` points to, not `ipp` itself
                //     therefore, `ipp` still points to `ip1` afterwards.
    
  • 3

    如果你想 ipp 指向 ip2 ,你必须说 ipp = &ip2; . 但是,这将使 ip1 仍然指向 i .

  • 140

    你刚开始设定,

    ipp = &ip1;
    

    现在取消引用它,

    *ipp = *&ip1 // Here *& becomes 1  
    *ipp = ip1   // Hence proved
    
  • 3

    考虑每个变量如下所示:

    type  : (name, adress, value)
    

    所以你的变量应该表示为这个

    int   : ( i ,  &i , 5 ); ( j ,  &j ,  6); ( k ,  &k , 5 )
    
    int*  : (ip1, &ip1, &i); (ip1, &ip1, &j)
    
    int** : (ipp, &ipp, &ip1)
    

    由于 ipp 的值是 &ip1 所以结构如下:

    *ipp = ip2;
    

    将地址 &ip1 的值更改为 ip2 的值,这意味着 ip1 已更改:

    (ip1, &ip1, &i) -> (ip1, &ip1, &j)
    

    ipp 仍然:

    (ipp, &ipp, &ip1)
    

    所以 ipp 的值仍然是 &ip1 ,这意味着它仍然指向 ip1 .

  • 12

    因为您正在更改 *ipp 的指针 . 它的意思是

    • ipp (可变名称)----进去吧 .

    • 里面 ippip1 的地址 .

    • 现在 *ipp 所以去(里面的地址) ip1 .

    现在我们在 ip1 . *ipp (即 ip1 )= ip 2 .
    ip2 包含地址 j .so ip1 内容将被包含ip2替换(即j的地址),我们不会更改 ipp 内容 . 而已 .

  • 20

    *ipp = ip2; 暗示:

    ip2 分配给 ipp 指向的变量 . 所以这相当于:

    ip1 = ip2;
    

    如果您希望将 ip2 的地址存储在 ipp 中,只需执行以下操作:

    ipp = &ip2;
    

    现在 ipp 指向 ip2 .

  • 5

    ipp 可以保存指向指针类型对象的指针(即指向) . 当你这样做

    ipp = &ip2;
    

    那么 ipp 包含 address of the variable (pointer) ip2 ,它是指向指针的类型指针( &ip2 ) . 现在第二张图片中 ipp 的箭头将指向 ip2 .

    Wiki说:
    * 运算符是一个解引用运算符,对指针变量进行操作,并返回与指针地址处的值等效的l-value(变量) . 这称为解除引用指针 .

    * 运算符应用于 ipp derefrence它到指向 int 类型的指针的l值 . 取消引用的l值 *ipp 是指向 int 的类型指针,它可以保存 int 类型数据的地址 . 声明之后

    ipp = &ip1;
    

    ipp 持有 ip1 的地址, *ipp 持有(指向) i 的地址 . 你可以说 *ippip1 的别名 . **ipp*ip1 都是 i 的别名 .
    通过做

    *ipp = ip2;
    

    *ippip2 都指向同一位置,但 ipp 仍然指向 ip1 .

    实际上 *ipp = ip2; 的作用是将 ip2j 的地址)的内容复制到 ip1 (因为 *ippip1 的别名),实际上使指针 ip1ip2 指向同一个对象( 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 .

相关问题