考虑C
中的以下代码int x=100;
int*addr=&x;
我知道addr会存储xA问题的地址,这个问题一直在我脑海中浮现,因为addr指针将有自己的地址,并且可以再次使用&符号运算符进行访问,并且也会存储在某处,所以我们有这里地址无限递归所以这结束了什么?
答案 0 :(得分:26)
addr
的地址没有明确地存储在任何地方,只是。如果您要声明第二个变量并使用它来存储该地址,那么确定需要空间:
int **addr2 = &addr;
您可以将内存视为一系列框。假设4字节int
和指针以及little-endian字节顺序,您的场景可能如下所示:
+----------+--+--+--+--+
| Address |Data bytes |
+----------+--+--+--+--+
|0x00000000|64|00|00|00|
+----------+--+--+--+--+
|0x00000004|00|00|00|00|
+----------+--+--+--+--+
地址显示在左侧,包含在右侧该位置的字节。您可以看到存储在前四个字节中的值100
(十进制100是十六进制的0x64)。第二个4字节位置保存值0x00000000,即x
的地址。地址0x00000004不存储在任何地方。
现在,如果我们添加第二个指针,我们将使用更多内存:
+----------+--+--+--+--+
|0x00000008|04|00|00|00|
+----------+--+--+--+--+
这将是用于表示addr2
指针的内存,您可以看到它包含addr
的地址,即0x00000004。
使用表达式addr2
取消引用*addr2
将产生地址0x00000004处的值,即具有类型int *
的0x00000000。再次取消引用会在该地址处产生int
,即0x00000064。
由于编译器选择了这种内存布局,它“知道”所涉及的地址,并且可以替换,以便在代码引用addr2
时,它生成操作地址0x00000008的指令,依此类推。在实际代码中,这可能都发生在堆栈上,但原理是相同的。
最后的注意事项:注意@Phil Perry的建议;上面的内容简化并使ocncrete成为许多有点抽象的东西。这就是它在许多当前架构中的实际工作方式,但C中并不能保证所提到的许多内容,所以你不能真正依赖它们来保持真实。我的意思是上面的例子(希望)使概念略显模糊。
答案 1 :(得分:14)
如果您存储地址,当然,您需要一些地方来存储它(即另一个地址),因此您可以根据需要继续这样做。当你希望它结束时,这就完全结束了。
int a = 1;
int *pa = &a;
int **ppa = &pa;
int ***pppa = &ppa;
int ****ppppa = &pppa;
...
这是一个简单的类比:假设你有一个带有编号页面和线条的笔记本。假设您在第3页第8行做了注释,然后想要从其他地方引用该注释。也许你可以在第7页第20行写一篇参考文献“见第3页第8行注释”。现在引用有自己的“地址” - 即第7页第20行。您也可以引用它 - 在第8页第1行,您可以写“请参阅第7页第20行注释”。您可以根据需要继续这一系列的参考。
答案 2 :(得分:8)
C实现了所谓的 von Neumann机器。您可以在其他类型的计算机上编译C,如果您可以让计算机执行冯诺依曼机器的操作。
那么,什么是冯诺依曼机器? 对于BBC道歉,大多数人都认为内存中的程序有严格而独特的代码和数据概念,但实际上 - 从统一的冯诺依曼的角度来看 - 程序的内存空间更像是一个大的虚拟球 - wobbly,numbery-wumbery ...... stuff 。你可以像数据一样访问甚至改变它,或者你可以像代码一样运行它。但如果你不确定它是否正常工作,后者不是一个好主意,这是人们不再经常使用汇编的原因之一。高级编程语言将数字精心组织成(理想情况下)工作的东西:它们在这方面并不完美,而C在设计上并不像大多数那样完美。但他们通常会完成工作。
现在,这与你的问题有什么关系? 正如你所说,每个变量都必须有一个地址,C必须知道这些地址是什么才能使用它们。确切的实现细节因编译器而异,有时在不同的设置上在编译器中,但在某些情况下,程序在到达机器代码级别时甚至不再知道变量名称。地址都是剩下的,它们实际上是被使用的。
这些地址由编译器决定。最终,它们成为代码本身的一部分。它们保存在您通常不应访问的区域,但如果您真的需要,则可以。这些区域基本上是递归停止的地方。
答案 3 :(得分:5)
您没有无限递归。每个变量都有它的地址,就是这样。现在,如果你定义一个等于另一个变量的地址的变量,那么地址变量本身将存储在某个地方,并且会有一个地址。地址本身没有存储它的地址变量。
答案 4 :(得分:4)
从概念上讲,存储空间仅在存储地址时为指针分配:
1)在您的情况下:int*addr=&x;
,addr
将占用sizeof(int*)
个字节的内存。 (但编译器保留不将指针存储在内存中的权利:它可能将其保存在CPU寄存器中。)
2)&x
没有明确分配任何存储空间。
3)&&x
,&&&x
等在语法上无效,所以你不必担心潜在的递归。
&&
曾经是非法语法的事实允许C ++ 11使用这个用于 r值引用。
答案 5 :(得分:3)
当然地址指针会有自己的地址,但除非你专门访问它所拥有的地址,否则不会使用它,因此没有递归...
答案 6 :(得分:2)
让我们这样看。我们有limited
个记忆。如果这整个内存,为编译器分配有限的内存量为STORE
他们的变量。 U可以声明尽可能多的变量但是当你不能创造更多的变量时,时间会到来,因为allocated memory
会耗尽空间。那么,至于你的问题,答案是NO
。由于有限的分配空间来存储变量,因此没有无限递归。
答案 7 :(得分:2)
指针就像一个房子,它的地址在一个地方。房子是真实的,它的地址是找到房子的方式。假设有两个房子,X和ptr,我需要存放一个房子的地址(可能写在一张纸上),比如X在另一个房子里说ptr。 通过这种安排如果我需要知道X的地址我可以在ptr中查看但是X中有什么,我需要亲自访问这个房子。但目前我对知道ptr的地址并不感兴趣。
更进一步
ptr and X are two houses.
//lets say &X is a sheet of paper containing the address of house X. I am going to keep this address in the house ptr. So the address of X is stored in ptr.
ptr = &X;
//Now what about ptr. Only if I need to know the address, I need to write the address in some paper and store it again in some other house. Else I am happy to know the house exits.
答案 8 :(得分:2)
这取决于......
这取决于编译器和优化级别。编译器有可能决定不将指针“addr”存储在主存储器中,而只是将其值保存在寄存器中,使用它来定位主存储器中的x。
答案 9 :(得分:2)
这里有很多好的答案但是我再添加一个。我想澄清一下Bathsheba提出的一个相当微妙的观点。你说:
addr指针将有自己的地址,并且可以再次使用&符号运算符进行访问,并且也将存储在某处
这句话是错误的。这是一个正确的改写:
如果在包含指针值的存储位置上使用&符运算符,则该存储位置将具有自己的地址...
指针是值。所有值都存储在某处。 但并非所有存储位置都有地址!存在没有地址的存储位置 - 它们被称为寄存器 - 并且编译器可以免费使用寄存器存储位置,前提是该存储位置的地址永远不会被采用。
所以当你说:
int x = 100;
int*addr = &x;
然后,与变量x
关联的存储位置必须具有地址,因为其地址为 。但是,并不要求与addr
关联的存储位置具有地址,因为其地址永远不会被采用;因此可以注册。注册不是 required ,但可能是。
有意义吗?
答案 10 :(得分:2)
@ unwind的答案很好,但有一个很大的警告:C语言可以让你创建一个指向任何东西的指针,这可能会让你陷入困境。考虑一下这个小小的C程序:
/*
* show that a variable allocated on the heap sticks around, while a
* variable allocated on the stack vanishes after its enclosing
* function returns. Holding a pointer to the latter can lead to
* unexpected results.
*/
#include <stdio.h>
int heapvar = 20;
int *get_heapvar_ptr() {
return &heapvar;
}
int *get_stackvar_ptr() {
int stackvar = 30;
return &stackvar;
}
int main() {
int *heap_ptr = get_heapvar_ptr();
int *stack_ptr = get_stackvar_ptr();
/* should print value = 20 */
printf("heapvar address = %#x, heapvar value = %d\n", heap_ptr, *heap_ptr);
/* you might expect this to print value = 30, but it (probably) doesn't */
printf("stackvar address = %#x, stackvar value = %d\n", stack_ptr, *stack_ptr);
}
运行时(至少在我的环境中),我得到:
heapvar address = 0x22a8018, heapvar value = 20
stackvar address = 0x5d957fac, stackvar value = 0
请注意,stackvar的“value”现在为零,即使它已设置为20.这是因为stack_ptr指向堆栈上已被其他函数覆盖的位置。
值得赞扬的是,编译器提醒我warning: address of stack memory associated with local variable
,但故事的寓意是C中的指针算法非常强大且相应危险!