在将地址 int "i" 分配给指针 "p" 时,"p = &i" 和“*p = i”之间有什么区别吗

Is there any differences between "p = &i" and "*p = i" while assigning the address int "i" to a pointer "p"

我在我的C编程书上找到了这段代码:

int i = 42;
int *p;

p = &i;    // & is address of sign
*p = i;    // * is dereference sign

"p = &i"和"*p = i"有区别吗?? "p"使用这两个表达式会不会有不同的特点???

编辑:因为这段代码只是试图解释指针的概念,所以它不可运行...所以这两个赋值的顺序在这种情况下不相关...抱歉让事情变得模糊。 ..

Is there any differences between “p = &i” and “*p = i” while assigning the address int “i” to a pointer “p”

是的,有很大的不同。只有 p = &i 表示 "assign the address of i to p".

另一方面 *p = i 表示 "assign the value of i to the value at the address that is stored in p"。你可以通过一个简单的例子看到结果:

int i = 42;
int* p;

p = &i;
printf("%d", i);
*p = 5;
printf("%d", i);

int j = 3;
printf("%d %d", i, j);
p = &j;
*p = i;
printf("%d %d", i, j);

首先,快速总结 - 给定声明

int i, *p;

和声明

p = &i;

则下列说法成立:

 p == &i // int * == int *
*p ==  i // int   == int

您已将 i 地址 分配给 p。因此,表达式 *pi 的计算结果相同。给*p赋值和给i赋值是一样的,从*p读一个值和从i读一个值是一样的。如果你后来做了类似

的事情
*p = 10;

那么这就和写一样了

i = 10;

IOW,您正在为 p 指向 的事物分配一个新值。

如果我们引入多级间接寻址,例如:

int i;
int *p = &i;
int **q = &p;

则下列皆为真:

  q == &p         // int ** == int **
 *q ==  p == &i   // int *  == int *  == int *
**q == *p ==  i   // int    == int    == int

因此,写入**q与写入*p等同于写入i。写入 *q 与写入 p 相同。


以下内容可能有帮助,也可能没有帮助。

我写了一个小实用程序来显示内存中各种项目的内容,我将用它来说明 p = &i*p = i 之间的区别。

首先,这是测试程序:

#include <stdio.h>
#include "dumper.h"

int main( void )
{
  int i = 0, j = 0, *p = NULL;
  char *names[] = { "i", "j", "p", "*p"};
  void *addrs[] = { &i, &j, &p, NULL };
  size_t sizes[] = { sizeof i, sizeof j, sizeof p, sizeof *p };

  puts( "Before any assignments: ");
  dumper( names, addrs, sizes, 3, stdout );

  p = &i;
  addrs[3] = p;

  puts( "After p = &i: " );
  dumper( names, addrs, sizes, 4, stdout );

  *p = 42;
  puts( "After *p = 42: " );
  dumper( names, addrs, sizes, 4, stdout );

  p = &j;
  addrs[3] = p;

  puts( "After p = &j: " );
  dumper( names, addrs, sizes, 4, stdout );

  *p = 10;
  puts( "After *p = 10: " );
  dumper( names, addrs, sizes, 4, stdout );

  return 0;
}

dumper函数显示当时内存中各种对象的状态。

所以,我们从声明开始:

int i = 0, j = 0, *p = 0;

ij 是常规的 intp 是指向 int 指针 。这意味着 p 中存储的值是其他 int 对象的地址。这是此时内存中的内容:

       Item         Address   00   01   02   03
       ----         -------   --   --   --   --
          i  0x7ffee3d07a28   00   00   00   00    ....

          j  0x7ffee3d07a24   00   00   00   00    ....

          p  0x7ffee3d07a18   00   00   00   00    ....
             0x7ffee3d07a1c   00   00   00   00    ....

i占用地址0x7ffee3d07a28开始的4个字节1j占用地址0x7ffee3d07a24开始的四个字节, p 从地址 0x7ffee3d07a18 开始占用 8 个字节。所有三个对象当前都存储 0 个值。

接下来,我们执行语句

p = &i;

这会将 i 地址 存储到 p。以下是事后的情况:

       Item         Address   00   01   02   03
       ----         -------   --   --   --   --
          i  0x7ffee3d07a28   00   00   00   00    ....

          j  0x7ffee3d07a24   00   00   00   00    ....

          p  0x7ffee3d07a18   28   7a   d0   e3    (z..
             0x7ffee3d07a1c   fe   7f   00   00    ....

         *p  0x7ffee3d07a28   00   00   00   00    ....

p 现在存储 i2 的地址,而不是存储全零。请注意 expression *pobject i3[= 具有相同的有效地址168=]。

现在我们执行语句

*p = 42;

我们的记忆现在是这样的:

       Item         Address   00   01   02   03
       ----         -------   --   --   --   --
          i  0x7ffee3d07a28   2a   00   00   00    *...

          j  0x7ffee3d07a24   00   00   00   00    ....

          p  0x7ffee3d07a18   28   7a   d0   e3    (z..
             0x7ffee3d07a1c   fe   7f   00   00    ....

         *p  0x7ffee3d07a28   2a   00   00   00    *...

i 的最低有效字节现在存储值 0x2a,即十六进制的 42。请注意 *p 显示相同的内容。同样,在大多数情况下,*p 等同于 i

现在,我们把j的地址赋值给p:

p = &j;

现在世界的状态是这样的:

       Item         Address   00   01   02   03
       ----         -------   --   --   --   --
          i  0x7ffee3d07a28   2a   00   00   00    *...

          j  0x7ffee3d07a24   00   00   00   00    ....

          p  0x7ffee3d07a18   24   7a   d0   e3    $z..
             0x7ffee3d07a1c   fe   7f   00   00    ....

         *p  0x7ffee3d07a24   00   00   00   00    ....

p 现在存储 j 的地址,*p 现在等同于 j。我们通过将 10 分配给 *p:

来完成
*p = 10;

这给我们留下了

       Item         Address   00   01   02   03
       ----         -------   --   --   --   --
          i  0x7ffee3d07a28   2a   00   00   00    *...

          j  0x7ffee3d07a24   0a   00   00   00    ....

          p  0x7ffee3d07a18   24   7a   d0   e3    $z..
             0x7ffee3d07a1c   fe   7f   00   00    ....

         *p  0x7ffee3d07a24   0a   00   00   00    ....

j 现在存储值 0x0a,它是 10 的十六进制值。同样,表达式 *p 等同于 j


  1. 在大多数系统上,地址会从 运行 运行 变化,所以不要太在意确切的地址值。
  2. x86 是 little-endian,所以 least 有效字节是寻址字节。这意味着值读取 "backwards" - 从左到右,从下到上。
  3. 这是一个有点戏剧性的许可 - 表达式 没有这样的地址。这只是为了说明表达式 *p 在大多数情况下实际上与 i 相同。