我知道一个指针变量存储了另一个变量的内存地址。 “指针指向另一个内存位置”是什么意思??
答案 0 :(得分:0)
从根本上讲,variable是名称与内存中位置之间的关联。它可能比其他语言更复杂,并且其他信息通常与变量(例如类型)相关联,但这与此处无关。
指针变量是一个变量(一个引用内存某些区域的名称),但是该内存区域中有什么? pointer本身是什么?好吧,这是一个内存地址,一种引用另一个内存位置的方式。我们不是说“指针具有价值这个另一个存储位置”,而是说指针引用或指向另一个存储位置。< / p>
我们可能有其他存储位置的名称。那将使其成为变量。因此,我们也说指针引用或指向该另一个变量。
下面,我们有一个指向变量p
的指针i
:
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指针 dereference ,这意味着访问指针所指向的指针。这是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
,*q
,i
和j
中的一个会怎样?我让你进行假设和实验:)
答案 1 :(得分:0)
它指向另一个存储位置,就像您最近收到的明信片上的邮寄地址指向您的家一样。指针包含一个存储单元的地址。邮政地址包含实际位置的地址-无论是建筑物,公寓还是邮政信箱……“指向”因此表示“是指……的位置”。指针和邮政地址都是如此。
并非必须将指针指向另一个存储位置。的确,现实世界中使用的绝大多数指针确实指向其他地方。也就是说,99.9...%
指针在世界上在任何给定时间都存在,小数点后还有15个9。至少,非常。
但是指针当然也可以指向自身。有时,如果您只想将指针放在某个地方并了解该“某处”在哪里,就会很方便。例如,在大多数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
“指令”,该指令触发了未定义的指令异常。换句话说:如果在映射第0个内存页的进程中运行了此操作,则即使在地址5
加载0
也是有效的操作,它也会失败!相反,它将在地址0
上加载0
,然后由于异常而失败。