"pointer points to another memory location" 是什么意思?

What does it mean by a "pointer points to another memory location"?

我知道指针变量存储的是另一个变量的内存地址。但是"Pointer points to another memory location"是什么意思?"pointer points to..."是什么意思?

从根本上说,variable 是名称与内存位置之间的关联。它可能比其他语言更复杂,并且其他信息通常与变量相关联(例如类型),但这与这里无关。

指针变量是一个变量(引用某个内存区域的名称),但是那个内存区域中有什么? pointer 本身是什么?好吧,它是一个内存地址,一种引用另一个内存位置的方式。我们不说 "the pointer has for value this other memory location",而是说指针 引用 指向 这个其他内存位置。

我们可能有另一个内存位置的名称。这将使它成为一个变量。因此,我们也说指针引用指向这个其他变量。

在下面,我们有一个指向变量i的指针p:

int i = 1000;
int *p = &i;

我们可能有这样的记忆:

p at addr 0x1122       |      ⋮       |
i at addr 0x3344       +--------------+
                       |       0x3344 | 0x1122
                       +--------------+
                       |      ⋮       |
                       +--------------+
                       |         1000 | 0x3344
                       +--------------+
                       |      ⋮       |

那么下面两种说法有什么区别呢:

int *q = p;
int j = *p;

首先,我们复制指针本身,即内存地址。

第二个,我们间接访问i。我们告诉 C 取消引用 指针,这意味着访问指针指向的对象。这是 i.

我们可能有这样的记忆:

p at addr 0x1122       |      ⋮       |
i at addr 0x3344       +--------------+
q at addr 0x5566       |       0x3344 | 0x1122
j at addr 0x7788       +--------------+
                       |      ⋮       |
                       +--------------+
                       |         1000 | 0x3344
                       +--------------+
                       |      ⋮       |
                       +--------------+
                       |       0x3344 | 0x5566
                       +--------------+
                       |      ⋮       |
                       +--------------+
                       |         1000 | 0x7788
                       +--------------+
                       |      ⋮       |

如果您更改 *p*qij 中的一个,其他人会怎样?我会让你假设和实验:)

它指向另一个存储位置,就像您最近收到的 post 卡上的 postal 地址指向您的家一样。指针包含内存位置的地址。 postal 地址包含实际位置的地址 - 可以是建筑物、公寓、post 办公室……"Points to" 因此表示 "refers to a location of"。指针和 postal 地址都是如此。

指针必须指向另一个内存位置是不正确的。确实,现实世界软件中使用的绝大多数指针确实指向其他地方。也就是说,在任何给定时间,世界上都存在 99.9...% 个指针,小数点后还有 15 个 9。至少,最起码。

但是指针当然也可以指向它自己。有时,如果您只想在某处放置一个指针,并了解 "somewhere" 在哪里,这会很方便。例如,在大多数 C 和 C++ 实现中,以下短程序将在运行时打印指针变量所在的堆栈地址(是的,实现不必将该变量存储在堆栈上,但在大多数实际情况下它确实是一个堆栈地址):

#include <stdio.h>

int main() {
  void *pointer = &pointer;
  printf("The variable \"%s\" is stored on stack at address %p\n", "pointer", &pointer);
}

例如,我得到以下输出:

The variable "pointer" is stored on stack at address 0x7fff2350e9f8

指针也可能指向没有内存位置。空指针就是一个例子:

// Conformant C and C++
int *nullPointer1 = NULL; // Idiomatic C
int *nullPointer2 = 0;
// Conformant C++
int *nullPointer3 = nullptr; // Idiomatic C++
int *nullPointer4 = {}; // Idiomatic C++
std::unique_ptr<int> nullPointer5; // This is really C++ - raw pointers should be used only when truly needed, in library code etc.

这样的指针是可以的,但它们唯一有效的用途是检查它们是否确实为空。它们不能被取消引用——我的意思是,是的,你可以取消引用它们,但这是未定义的行为,现代编译器将代码中的未定义行为视为删除此类代码的许可。也就是说,如果您解除对空指针的引用,并且编译器可以证明情况总是如此,那么执行此类解除引用的代码可能会被删除。示例(gcc 10.1 x64,-O3):

int main() {
  int *pointer = 0;
  int b = *pointer;
}

// produces same assembly output as
int main() { return 0; }

越来越好。此代码:

int main() {
   int *pointer = 0;
   *pointer = 5;
}

编译时,第二行被修改,就好像你写了 *pointer = 0 一样,赋值后紧接着 ub2 "instruction" 触发了未定义指令异常。换句话说:如果你 运行 在第 0 个内存页被映射到的进程中,它将失败,即使在地址 0 加载 5 将是一个有效操作!相反,它会在地址 0 加载 0,然后失败并出现异常。