简体   繁体   English

使用指针的变量的地址

[英]Address of a variable using pointer

Consider the following code in C 请考虑C中的以下代码

int x=100;
int*addr=&x;

I know that addr will store the address of xA question that keeps popping in my mind is that the addr pointer will have its own address and that can again be accessed using ampersand operator and that too will be stored somewhere,so we have here infinite recursion on addresses so where does this end? 我知道addr会存储xA问题的地址,这个问题一直在我脑海中浮现,因为addr指针将有自己的地址,并且可以再次使用&符号运算符进行访问,并且它也会存储在某处,所以我们在这里有无限递归在地址上这样结束了吗?

The address of addr isn't "stored" anywhere explicitly, it just is. addr的地址没有明确“存储”,只是。 If you were to declare a second variable and use it to store that address, then sure it takes space: 如果您要声明第二个变量并使用它来存储该地址,那么确保它占用空间:

int **addr2 = &addr;

You can think of the memory as a series of boxes. 你可以把记忆想象成一系列的盒子。 Assuming 4-byte int and pointers, and little-endian byte order, your scenario might look like: 假设4字节的int和指针以及little-endian字节顺序,您的场景可能如下所示:

+----------+--+--+--+--+
| Address  |Data bytes |
+----------+--+--+--+--+
|0x00000000|64|00|00|00|
+----------+--+--+--+--+
|0x00000004|00|00|00|00|
+----------+--+--+--+--+

The address is shown on the left, the bytes contained at that location on the right. 地址显示在左侧,包含在右侧该位置的字节。 You can see the value 100 stored in the first four bytes (100 decimal is 0x64 in hex). 您可以看到存储在前四个字节中的值100 (十进制100是十六进制的0x64)。 The second 4-byte location holds the value 0x00000000, which is the address of x . 第二个4字节位置保存值0x00000000,这是x的地址。 The address 0x00000004 is not stored anywhere. 地址0x00000004不存储在任何地方。

Now if we add a second pointer, we'd use more memory: 现在,如果我们添加第二个指针,我们将使用更多内存:

+----------+--+--+--+--+
|0x00000008|04|00|00|00|
+----------+--+--+--+--+

That would be the memory used to represent the addr2 pointer, and you can see that it contains the address of addr , ie 0x00000004. 这将是用于表示addr2指针的内存,您可以看到它包含addr ,即0x00000004。

Dereferencing addr2 with the expression *addr2 would yield the value at address 0x00000004, ie 0x00000000 with the type int * . 使用表达式*addr2取消引用addr2将产生地址0x00000004的值,即0x00000000,类型为int * De-referencing that once more yields the int at that address, ie 0x00000064. 再次取消引用会在该地址处产生int ,即0x00000064。

Since this memory layout is chosen by the compiler, it "knows" the addresses involved, and can just substitute so that when code references addr2 , it generates instructions that manipulate address 0x00000008, and so on. 由于这个内存布局是由编译器选择的,它“知道”所涉及的地址,并且可以替代,这样当代码引用addr2 ,它会生成操作地址0x00000008的指令,依此类推。 In real code this would probably all happen on the stack, but the principle is the same. 在实际代码中,这可能都发生在堆栈上,但原理是相同的。

Final note: do heed @Phil Perry 's advice; 最后说明:请注意@Phil Perry的建议; the above simplifies and makes ocncrete a lot of things that are meant to be somewhat abstract. 上面的内容简化并使ocncrete成为许多有点抽象的东西。 This is how it really does work on many current-day architectures, but many of the things mentioned are not guaranteed by C so you cannot really depend on them to always hold true. 这就是它在许多当前架构中的实际工作方式,但C中并不能保证所提到的许多内容,所以你不能真正依赖它们来保持真实。 I meant the above as an illustration to (hopefully) make the concepts slighly less vague. 我的意思是上面的例子(希望)使概念略微模糊。

If you store an address, naturally, you need some place to store it (ie another address), so you can continue this for as long as you wish. 如果您存储地址,当然,您需要一些地方来存储它(即另一个地址),因此您可以根据需要继续这样做。 This ends precisely when you want it to end. 当你希望它结束​​时,这就结束了。

int         a = 1;
int       *pa = &a;
int     **ppa = &pa;
int   ***pppa = &ppa;
int ****ppppa = &pppa;
...

Here is a quick analogy: let's say you have a notebook with numbered pages and lines. 这是一个简单的比喻:假设你有一个带有编号页面和线条的笔记本。 Suppose you make a note on page 3, line 8, and then you want to reference that note from some other place. 假设您在第3页第8行做了注释,然后想要从其他地方引用该注释。 Perhaps you can write on page 7, line 20, a reference "see note on page 3, line 8". 也许你可以在第7页第20行写一篇参考文献“见第3页第8行注释”。 Now the reference has its own "address" - namely, page 7, line 20. You can reference it, too - on page 8, line 1, you could write "see note on page 7, line 20". 现在引用有自己的“地址” - 即第7页第20行。您也可以引用它 - 在第8页第1行,您可以写“请参阅第7页第20行注释”。 You can continue this chain of referencing for as long as you wish. 您可以根据需要继续使用此链接引用。

C implements what is called a von Neumann machine . C实现了所谓的冯·诺依曼机器 You can compile C on other kinds of computers, if you can make the computer do what a von Neumann machine would do. 您可以在其他类型的计算机上编译C,如果您可以使计算机执行冯诺依曼机器的操作。

So, what is a von Neumann machine? 那么,什么是冯诺依曼机器? With apologies to the BBC, most people assume that programs in memory have strict and distinct notions of code and data, but actually -from a unified, von Neumann perspective- a program's memory space is more like a big ball of wibbly-wobbly, numbery-wumbery... stuff . 对于BBC道歉,大多数人都认为内存中的程序具有严格而独特的代码和数据概念,但实际上 - 从统一的冯·诺伊曼的角度来看 - 程序的内存空间更像是一个摇摆不定的大球,数字- 笨蛋...... 东西 You can access and even alter it like data, or you can run it like code. 您可以像访问数据一样访问甚至更改数据,也可以像代码一样运行它。 But this latter isn't a good idea if you aren't certain that it's working code, which is one of the reasons people don't use assembly very often any more. 但如果你不确定它是否正常工作,后者不是一个好主意,这是人们不再经常使用汇编的原因之一。 Higher-level programming languages carefully organize the numbers into something that (ideally) works: they're not perfect at this, and C is, by design, less perfect at it than most. 高级编程语言将数字精心组织成(理想情况下)工作的东西:它们在这方面并不完美,而C在设计上并不像大多数那样完美。 But they usually get the job done. 但他们通常会完成工作。

Now, what does this have to do with your question? 现在,这与你的问题有什么关系? As you say, every variable has to have an address, and C has to know what these addresses are in order to use them. 正如你所说,每个变量都必须有一个地址,而C必须知道这些地址是什么才能使用它们。 The exact implementation details can vary from compiler to compiler, and sometimes on different settings within the compiler, but in some cases, the program doesn't even know the variable names anymore by the time it gets to the level of machine code. 确切的实现细节可能因编译器而异,有时在编译器中的不同设置上也有所不同,但在某些情况下,程序在达到机器代码级别时甚至不再知道变量名称。 The addresses are all that's left, and they are what actually get used. 地址都是剩下的,它们实际上是被使用的。

These addresses are decided by the compiler. 这些地址由编译器决定。 Ultimately, they become part of the code itself . 最终,它们成为代码本身的一部分 They're kept in areas that you shouldn't normally access, but can if you REALLY need to. 它们保存在您通常不应访问的区域,但如果您真的需要,则可以。 Those areas are basically where the recursion stops. 这些区域基本上是递归停止的地方。

You don't have an infinite recursion. 你没有无限递归。 Each variable has its address and that's it. 每个变量都有它的地址,就是这样。 Now if you define a variable that is equal to the address of another variable than the address variable itself will be stored somewhere and will have an address. 现在,如果你定义一个等于另一个变量的地址的变量,那么地址变量本身将存储在某个地方,并且会有一个地址。 An address itself does not have an address the variable that stores it does. 地址本身没有存储它的变量的地址。

Conceptually, storage space is only allocated for the pointer when you store the address: 从概念上讲,存储空间仅在存储地址时为指针分配:

1) In your case: int*addr=&x; 1)在你的情况下: int*addr=&x; , addr will occupy sizeof(int*) bytes of memory. addr将占用sizeof(int*)字节的内存。 (But a compiler reserves the right not to store the pointer in memory: it might keep it in a CPU register). (但编译器保留不将指针存储在内存中的权利:它可能将其保存在CPU寄存器中)。

2) &x doesn't explicitly allocate any storage. 2) &x没有明确分配任何存储。

3) &&x , &&&x etc. are not syntatically valid so you don't need to worry about potential recursion here. 3) &&x&&&x等在语法上没有效果,所以你不必担心潜在的递归。

The fact that && used to be illegal syntax allowed C++11 use adopt this for r-value references . &&曾经是非法语法的事实允许C ++ 11使用它来进行r值引用

当然地址指针将有自己的地址,但除非你专门访问它所持有的地址,否则它不会被使用,因此没有递归...

Lets look it this way. 让我们这样看。 We have limited memory. 我们的记忆力limited Out if this whole memory, limited amount of memory is allocated for compilers to STORE their variables. 如果整个内存,为编译器分配有限的内存量来STORE它们的变量。 U can declare as many variables as u wish BUT a time will come when u can't make more variables BECAUSE the allocated memory will run out space. U可以声明尽可能多的变量,但是当你无法生成更多变量时,时间将到来,因为allocated memory会耗尽空间。 So, as for your question, the answer is NO . 那么,至于你的问题,答案是NO there is no infinite recursion due to Limited Allocated Space to store variables. 由于Limited Allocated Space存储变量,因此没有无限递归。

Pointers are like a house and its address in a locality. 指针就像一所房子,它的地址在一个地方。 The house is real and its address is the way to locate the house. 房子是真实的,它的地址是找到房子的方式。 Assume there are two houses, X and ptr and I need to store the address (probably writing it in piece of paper) of one house, say X in another house say ptr. 假设有两个房子,X和ptr,我需要存放一个房子的地址(可能写在一张纸上),比如X在另一个房子里说ptr。 With this arrangement If i need to know the address of X i can look in ptr but what is there in X, i need to visit the house personally. 通过这种安排如果我需要知道X的地址我可以在ptr中查看但是X中有什么,我需要亲自访问这个房子。 But currently i am not at all intrested to know the address of ptr. 但目前我对知道ptr的地址并不感兴趣。

To get it furter 为了更进一步

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. 

It depends... 这取决于...

It depends on the compiler and level of optimization. 这取决于编译器和优化级别。 There is a chance the compiler decide not to store your pointer "addr" in the primary memory but just keep its value in a register using it to locate x in the primary memory. 编译器有可能决定不将指针“addr”存储在主存储器中,而只是将其值保存在寄存器中,使用它来定位主存储器中的x。

Lots of good answers here already but let me add one more. 这里有很多好的答案,但是我再补充一点。 I want to clarify a rather subtle point made by Bathsheba. 我想澄清一下Bathsheba提出的一个相当微妙的观点。 You said: 你说:

the addr pointer will have its own address and that can again be accessed using ampersand operator and that too will be stored somewhere addr指针将有自己的地址,并且可以再次使用&符号运算符进行访问,并且也将存储在某处

That sentence is wrong in a subtle way. 那句话是一种微妙的错误。 Here is a rephrasing that is correct: 这是一个正确的改述:

If the ampersand operator is used on the storage location that contains the pointer value then that storage location will have its own address... 如果在包含指针值的存储位置上使用&符号运算符,则该存储位置将具有自己的地址...

A pointer is a value . 指针是一个 All values are stored somewhere . 所有值都存储在某处 But not all storage locations have addresses! 但并非所有存储位置都有地址! There are storage locations that do not have addresses -- they are called registers -- and a compiler is free to use a register for any storage location provided that the address of that storage location is never taken . 存储位置没有地址 - 它们被称为寄存器 - 并且编译器可以自由地使用寄存器用于任何存储位置, 前提是永远不会占用该存储位置的地址

So when you say: 所以当你说:

int x = 100;
int*addr = &x;

Then the storage location associated with variable x must have an address because its address is taken . 然后,与变量x关联的存储位置必须具有地址,因为其地址已被采用 But there is no requirement that the storage location associated with addr have an address because its address is never taken ; 但是并不要求与addr关联的存储位置具有地址, 因为它的地址永远不会被占用 ; therefore it could be enregistered . 因此可以注册 It is not required to be enregistered, but it could be. 不需要注册,但也可以。

Make sense? 说得通?

@unwind's answer is good, but there's one enormous caveat: the C language lets you create a pointer to just about anything, and that can get you in trouble. @ unwind的答案很好,但有一个很大的警告:C语言可以让你创建一个指向任何东西的指针,这可能会让你陷入困境。 Consider this tiny C program: 考虑这个小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);
}

When run (at least in my environment), I get: 运行时(至少在我的环境中),我得到:

heapvar address = 0x22a8018, heapvar value = 20
stackvar address = 0x5d957fac, stackvar value = 0

Note that stackvar's "value" is now zero even though it had been set to 20. This is because stack_ptr points to a place on the stack that has been overwritten by some other function. 请注意,stackvar的“值”现在为零,即使它已设置为20.这是因为stack_ptr指向堆栈上已被其他某些函数覆盖的位置。

To its credit, the compiler alerted me warning: address of stack memory associated with local variable , but the moral of the story is that pointer arithmetic in C is very powerful and correspondingly dangerous! warning: address of stack memory associated with local variable赞扬的是,编译器提醒我warning: address of stack memory associated with local variable ,但故事的寓意是C中的指针算法非常强大且相应危险!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM