【问题标题】:Is there any differences between "p = &i" and "*p = i" while assigning the address int "i" to a pointer "p"将地址int“i”分配给指针“p”时,“p = &i”和“*p = i”之间有什么区别吗?
【发布时间】:2019-12-11 20:09:03
【问题描述】:

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

int i = 42;
int *p;

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

“p = &i”和“*p = i”有什么区别吗?? “p”在使用这两个表达式时会有不同的特征吗???

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

【问题讨论】:

  • 我敢打赌,书籍作者试图说 *p == i 这是一个真实的陈述。
  • 尝试在两个作业之间切换,看看会发生什么
  • 完全不同。第一个分配地址,第二个分配值。
  • 指针只是一个普通变量,它保存着其他东西的地址作为它的值。换句话说,一个指针指向内存中可以找到其他东西的地址。 p = &i; 分配内存中的地址,i 被存储为指针 p 保存的值。 (p 现在指向存储 i 的内存)要引用指针持有的地址处的值,您可以通过在指针名称前使用一元 '*' 字符取消引用指针. *p = i; 会将p 持有(指向)的内存位置的值设置为i 的值。
  • 作业的顺序是完全相关的。切换它们会导致使用未定义的指针,这会调用未定义的行为。

标签: c pointers memory-address


【解决方案1】:

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

是的,有很大的不同。只有p = &i表示“将i的地址分配给p”。

另一方面,*p = i 表示“将i 的值分配给存储在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);

【讨论】:

  • 在问题示例中,*p = i; 不执行任何操作,因为它将i 的值分配给p 指向的位置,即i
  • @WeatherVane 是的,如果你这样做 p = &i 然后之后立即 *p = i; 是没有意义的。我的回答是试图孤立地解释这两种语法。你有什么建议可以更清楚地说明这一点吗?
  • 我的评论是为了说明正在发生的事情,我投了你一票。
  • 我还会简化第二部分 - assign the value of i to the value at the address that is stored in p --> store the value of i in the memory address pointed to by p,但也许我只是在吹毛求疵 :)
  • @Billy "虽然p会有i的值,但是p会保留它得到的地址" 更准确地说,p指向的内存会有这个值的ip 本身不会改变;它只有一个地址。
【解决方案2】:

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

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 是常规的 ints,p 是指向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 开始的字节。所有三个对象当前都存储 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 的地址,而不是存储全零。请注意,表达式 *p对象 i3 具有相同的有效地址。

现在我们执行语句

*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 有效字节是寻址字节。这意味着值读取“向后” - 从左到右,从下到上。
  3. 这是一个有点戏剧性的许可 - 表达式没有这样的地址。这只是为了说明表达式*p 在大多数情况下实际上与i 相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    • 2019-11-06
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    相关资源
    最近更新 更多