C指针问题

8

针对:

int *a;

a是一个整数可以被存储的地址。 &a是存储a的地址。 那么,&a存储在哪里? &(&a)存储在哪里? &(&(&a))存储在哪里? 这种地址的存储会一直持续下去吗?


1
<pedantic>在分配一些内存或将某个有效内存的地址分配给它之前,整数无法存储在那里。</pedantic> - Nick Meyer
如果你真的想知道指针是如何存储的,请查看这个回答:https://dev59.com/XEfSa4cB1Zd3GeqPBv7-#991152 - Robert Cartaino
在这里:https://dev59.com/X0XRa4cB1Zd3GeqPpzVd#98525 - 17 of 26
这里有一个很好的指针解释:https://dev59.com/AnVD5IYBdhLWcg3wWaVh - Bruce Alderman
14个回答

7

如果你没有明确写出 &a,它将不会被存储在任何地方。如果你写了,那么地址将被计算并存储在一个未命名的变量(临时变量)或者一个你写的命名变量中。

例如:

functionCall( &a ); // address will be in a temporary variable used for passing the parameter
int** b = &a; // address will be stored in variable b
otherFunctionCall( &&a ); // illegal, since &a is an expression operator & can't be applied to it

即使我不写&a,也一定有一个地方存储了a吧? - simplfuzz
@dta:我不确定我理解你的问题... a 存储在堆栈中,就像任何其他局部变量一样。当你写 &a 时,程序计算出存储 a 的堆栈中的地址(它已经知道了堆栈指针,因为它是一个寄存器,并且它知道堆栈帧中的偏移量,因为它被编译为常量),并对其进行操作,如 sharptooth 所解释的那样。 - rmeador
嗯,是的。你又把变量和变量的地址搞混了。它们通常不相等。变量是位于某个地址上并包含某个值的一块内存空间。 - sharptooth
这对我很有帮助。(连同Sharptooth的答案) - simplfuzz

4

a不是“存储整数的地址”。a是一个足够大的变量,可以容纳一个整数的地址。你唯一可以直接存储在a中的“整数”是整数的地址,将其视为整数本身:

int *a;
int b;

a = &b;

printf("a is now %x\n", (unsigned int) a);

确实,a本身有一个地址,即&a,但该地址在运行时没有被明确存储在某个地方。

如果勉强说的话,您可能能够存储类似于整数0的内容:

a = 0;

但这只是“空指针”的简写语法,即保证不是任何实际对象地址的指针值。


这个答案不是完美的...但我认为它绝对有最好的机会来解释/回答原问题,因此我给它一个+1。 - Beska

4

&a is a constant.

&(&a) is illegal.


1
该范围内的常量 = P - DevinB
2
"&a" 不是一个真正的常量。变量可以在堆栈上。它是在运行时计算的。 - Kirill V. Lyadvinsky
3
这是一个右值(即放在赋值运算符右边的值),因此对于程序而言是一个常量。它在不同运行中可能具有不同的值,但这并不重要。 - David Thornley
3
严格来说,&a是一个临时变量,因此是r值,你不能获取r值的地址。你可以称之为“常量”或“大象”,但无论哪种方式,你都不符合标准的语言规范。 - Steve Jessop
&a 就像函数调用的结果一样,它只是一个值。没有涉及到对象,并且不允许更改(显然,值是不能被更改的!一个对象可以被更改,这将在重新读取时产生不同的值)。 - Johannes Schaub - litb
显示剩余3条评论

3

&a是a的地址。它是运算符&应用于a的结果值,并且不被“存储”,没有地址,因此&(&a)是无效的。这就像2+3。


2
"int *a"是一个指针变量的大小,就像"int b"是一个自动整数变量一样。
如果这个声明在函数中,那么这个变量是自动的,并且在运行时存储在堆栈上(简单的堆栈减量为其分配内存)。
如果声明是全局的,则'a'只是映射到可执行文件的".DATA"区域。
附加任何更多的 & 符号都可以“创建存储”,因为您使用临时变量来保存它们 ;) :
b = &a; //the address in the executable's .DATA or on the stack (if `a` auto)
c = &b; //the address of `b` on the stack, independent of `a` or `&a`
d = &c; //the address of `c` on the stack, independent of `a` or `&a`
z = &(&a); //error: invalid lvalue in unary '&'

最后一行抱怨&要求操作数必须是左值,也就是可赋值的东西——像上面的bc。而(&a)是一个没有存储在任何地方的表达式的结果,因此不是左值

1
你可以永远继续前进:
int value = 742;
int *a = &value;
void *b = &a;
void *c = &b;
void *d = &c;

如果不将其分配给任何变量,您不会将其放在单行上-在这种情况下,它将无效。


是的,但使用void通常不被看好。最好写成int **b,int ***c,int ****d等形式。 - Dima

1
你的问题核心似乎是缺乏对内存和指针的物理本质的理解,而不是代码的工作方式。正如你知道的那样,物理内存由大量相邻的单元格组成。这些单元格的地址由计算机自身固定且硬编码,而不是由软件应用程序或使用的编程语言确定。当你引用&a时,你引用的是当前保存在计算机RAM中的值所在的物理内存块。 "a"只是你给计算机起的一个名字,以便它知道要查找你存储的值的确切内存块。我想这基本上涵盖了内存地址。
现在让我们来看看指针。指针是另一个由计算机引用的内存地址。它有你给它的任何名称。在这种情况下,它应该被称为与你给第一个值相同的东西之外的其他名称。根据你声明的方式,我们称其为“b”。b的内存位置仅能容纳一种类型的数据...另一个内存位置...因此当我说:b= &a时,我是说“b”的内存地址(仅设计用于容纳内存地址)将容纳“a”的内存地址。同时,在城镇的另一边,“a”的内存地址中存储着一个整数。
我希望这不会让你感到困惑,我尝试避免在这里使用所有的技术术语。如果你还是感到困惑,请再次发帖,下次我会用代码来解释。-UBcse

1
在 C 语言中,变量 x 可以作为值(在等号右侧,称为 rvalue),也可以作为值的容器(在等号左侧,称为 lvalue)。你可以取得 x 的地址,因为你可以取得任何 lvalue 的地址,这会给你一个指向容器的指针。但是因为指针是 rvalue 而不是容器,你永远不能取得 &(&x)。实际上,对于任何 lvalue l,&l 是合法的,但 &(&l) 永远不合法。

0

你可以有一个指向指针的指针。

例如:

void foo(int **blah)
{
 int *a = *blah;

 ...
}

指针确实占用内存。它只是一个小容器,保存着某个东西的地址。它不能不占用空间,因为计算机中的所有内容都以某种方式表示为数字。只是在C/C++中,int *a只是一个指向对象的指针,不占用任何空间。这样可以避免你管理任何类型的内存...它将机器与代码分离开来。

指针肯定会占用内存。int *a; 在堆栈上分配一个本地变量,它占用的字节数取决于您的机器上存储指针所需的空间大小。 - Dima

0

&a是一个rvalue的数字:如果您想要在已声明或分配的int*类型变量中存储它,则可以将其存储在某个地方。

也就是说:

int a = 42;
&a; /* this does not store the address of a because you've not assigned the value to a variable */
int **aptr = &a; /* aptr is on the stack */
int **aptr2 = (int*)malloc(sizeof(int*));
aptr2 = &a; /* aptr2 is in the heap */

&(&a) 不是合法的语法。 如果你想要一个指向指针的 int:

int b = 39;
int *bptr = &b;
int **ptr2bptr = &bptr;

你必须建立起间接级别。

有了上述内容,如果你想的话,可以这样做:

printf("%d\n", *aptr);
printf("%d\n", *aptr2);
printf("%d\n", *bptr);
printf("%d\n", **ptr_to_bptr);

生成输出:
42
42
39
39

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接