使用指针的变量的地址

时间:2014-02-13 10:51:57

标签: c pointers

考虑C

中的以下代码
int x=100;
int*addr=&x;

我知道addr会存储xA问题的地址,这个问题一直在我脑海中浮现,因为addr指针将有自己的地址,并且可以再次使用&符号运算符进行访问,并且也会存储在某处,所以我们有这里地址无限递归所以这结束了什么?

11 个答案:

答案 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中的指针算法非常强大且相应危险!