为什么是x != x != x ?
我正在学习一点C ++,我正在用指针来对抗。 我明白,我可以通过声明3级指针:
int *(*x)[5];
所以*x
是指向int
指针的5个元素的指针。 我也知道x[0] = *(x+0);
, x[1] = *(x+1)
等等。
那么,给出上面的声明,为什么是x[0] != x[0][0] != x[0][0][0]
?
x
是指向int
的5个指针数组的指针。
x[0]
是一个 5个指向int
指针的数组 。
x[0][0]
是一个指向int
的指针。
x[0][0][0]
是一个int
。
x[0] Pointer to array +------+ x[0][0][0] x -----------------> | | Pointer to int +-------+ 0x500 | 0x100| x[0][0]----------------> 0x100 | 10 | x is a pointer to | | +-------+ an array of 5 +------+ pointers to int | | Pointer to int 0x504 | 0x222| x[0][1]----------------> 0x222 | | +------+ | | Pointer to int 0x508 | 0x001| x[0][2]----------------> 0x001 | | +------+ | | Pointer to int 0x50C | 0x123| x[0][3]----------------> 0x123 | | +------+ | | Pointer to int 0x510 | 0x000| x[0][4]----------------> 0x000 | | +------+
你可以看到
-
x[0]
是一个数组,在expression式中使用时会转换为指向其第一个元素的指针(有一些例外)。 因此,x[0]
将给出它的第一个元素x[0][0]
的地址,它是0x500
。 -
x[0][0]
包含一个int
地址,它是0x100
。 -
x[0][0][0]
包含一个int
值10
。
所以, x[0]
等于&x[0][0]
,因此, &x[0][0] != x[0][0]
。
因此, x[0] != x[0][0] != x[0][0][0]
。
x[0] != x[0][0] != x[0][0][0]
是根据你自己的post,
*(x+0) != *(*(x+0)+0) != *(*(*(x+0)+0)+0)`
这是简化的
*x != **x != ***x
为什么它应该是平等的?
第一个是一些指针的地址。
第二个是另一个指针的地址。
第三个是int
值。
这里是你的指针的内存布局:
+------------------+ x: | address of array | +------------------+ | V +-----------+-----------+-----------+-----------+-----------+ | pointer 0 | pointer 1 | pointer 2 | pointer 3 | pointer 4 | +-----------+-----------+-----------+-----------+-----------+ | V +--------------+ | some integer | +--------------+
x[0]
产生“数组的地址”,
x[0][0]
产生“指针0”,
x[0][0][0]
产生“一些整数”。
我相信,现在应该是显而易见的,为什么他们都是不同的。
上面已经足够了解基本的理解,这就是为什么我写了它的方式。 然而,正如haccks正确地指出,第一行不是100%准确。 所以这里来了所有的细节:
从C语言的定义来看, x[0]
的值是整数指针的整个数组。 然而,数组是你在C中不能做任何事情的东西。你总是操纵他们的地址或者他们的元素,而不是整个数组。
-
您可以将
x[0]
传递给sizeof
运算符。 但是,这不是一个真正的价值的使用,其结果只取决于types。 -
你可以把它的地址得到
x
的值,即“数组的地址”,types为int*(*)[5]
。 换句话说:&x[0] <=> &*(x + 0) <=> (x + 0) <=> x
-
在所有其他上下文中 ,
x[0]
的值将衰减为指向数组中第一个元素的指针。 也就是说,一个带有“数组地址”值和int**
types的指针。 这个效果和把x
赋给int**
types的指针一样。
由于情况3中的数组指针衰减, x[0]
所有用法最终都会产生指向指针数组开头的指针; 调用printf("%p", x[0])
将打印标记为“array of address”的内存单元的内容。
-
x[0]
将最外层的指针( 指向指向int的指针大小为5的数组的指针x[0]
解引用,并产生指向int
的指针大小为5的数组; -
x[0][0]
解引用最外层的指针并索引数组,导致指向int
的指针; -
x[0][0][0]
解除引用所有内容,产生具体的值。
顺便说一下,如果您对这些声明的含义感到困惑,请使用cdecl 。
考虑一步一步的expression式x[0]
, x[0][0]
和x[0][0][0]
。
由于x
的定义如下
int *(*x)[5];
那么expression式x[0]
是一个int *[5]
types的数组。 考虑到expression式x[0]
等价于expression式*x
。 这是解引用一个指向我们得到数组本身的数组的指针。 用y来表示我们有一个声明
int * y[5];
expression式x[0][0]
相当于y[0]
并且具有typesint *
。 用z来表示,就是我们有一个声明
int *z;
expression式x[0][0][0]
相当于expression式y[0][0]
,又相当于expression式z[0]
并且具有typesint
。
所以我们有
x[0]
types是int *[5]
x[0][0]
types是int *
x[0][0][0]
types是int
所以它们是不同types,不同大小的对象。
运行例如
std::cout << sizeof( x[0] ) << std::endl; std::cout << sizeof( x[0][0] ) << std::endl; std::cout << sizeof( x[0][0][0] ) << std::endl;
首先我要说的是
x [0] = *(x + 0)= * x;
x [0] [0] = *(*(x + 0)+ 0)= * * x;
x [0] [0] [0] = *(*(*(x + 0)+ 0))= * * * x;
所以* x≠* * x≠* * * x
从下面的图片中,所有事情都清楚了。
x[0][0][0]= 2000 x[0][0] = 1001 x[0] = 10
这只是一个例子,其中x [0] [0] [0] = 10的值
而x [0] [0] [0]的地址是1001
该地址存储在x [0] [0] = 1001中
而x [0] [0]的地址是2000
并且该地址存储在x [0] = 2000
所以x [0] [0] [0] ≠ x [0] [0] ≠ x [0]
。
编辑位点
计划1:
{ int ***x; x=(int***)malloc(sizeof(int***)); *x=(int**)malloc(sizeof(int**)); **x=(int*)malloc(sizeof(int*)); ***x=10; printf("%d %d %d %d\n",x,*x,**x,***x); printf("%d %d %d %d %d",x[0][0][0],x[0][0],x[0],x,&x); }
产量
142041096 142041112 142041128 10 10 142041128 142041112 142041096 -1076392836
计划2:
{ int x[1][1][1]={10}; printf("%d %d %d %d \n ",x[0][0][0],x[0][0],x[0],&x); }
产量
10 -1074058436 -1074058436 -1074058436
如果您要从真实世界的angular度来查看数组,它将如下显示:
x[0]
是一个充满箱子的货柜。
x[0][0]
是货箱内的一个装满鞋盒的单个货箱。
x[0][0][0]
是货箱内货箱内的单个鞋盒。
即使它是货运集装箱中唯一的箱子里的鞋盒,它仍然是一个鞋盒,而不是一个货运集装箱
在C ++中有一个原则,所以:variables的声明正好表示使用variables的方式。 考虑你的声明:
int *(*x)[5];
可以改写为(为了更清楚):
int *((*x)[5]);
由于这个原则,我们有:
*((*x)[i]) is treated as an int value (i = 0..4) → (*x)[i] is treated as an int* pointer (i = 0..4) → *x is treated as an int** pointer → x is treated as an int*** pointer
因此:
x[0] is an int** pointer → x[0][0] = (x[0]) [0] is an int* pointer → x[0][0][0] = (x[0][0]) [0] is an int value
所以你可以找出差异。
作为一个指针:你正在堆叠与*((*(p+0))+0)
相等的p[0][0]
。
在C引用(&)和取消引用(*)表示法中:
p == &p[0] == &(&p[0])[0] == &(&(&p[0])[0])[0])
相当于:
p == &*(p+0) == &*(&*(p+0))+0 == &*(&*(&*(p+0))+0)+0
看,&*可以重构,只是删除它:
p == p+0 == p+0+0 == p+0+0+0 == (((((p+0)+0)+0)+0)+0)
您正尝试按值比较不同的types
如果你把地址,你可能会得到更多你所期望的
请记住,您的声明有所作为
int y [5][5][5];
将会允许你想要的比较,因为y
, y[0]
, y[0][0]
, y[0][0][0]
将具有不同的值和types,但是相同的地址
int **x[5];
不占用连续的空间。
x
和x [0]
具有相同的地址,但是x[0][0]
和x[0][0][0]
分别在不同的地址
其他答案是正确的,但是没有一个强调三个人都有可能包含同样的价值 ,所以他们在某种程度上是不完整的。
从其他答案不能理解的原因是,所有插图在大多数情况下是有用的,绝对合理的,并不能涵盖指针x
指向自身的情况。
这很容易构build,但显然有点难以理解。 在下面的程序中,我们将看到如何强制所有三个值是相同的。
注意:这个程序中的行为是不确定的,但是我在这里只是作为一个指针可以做的事情的一个有趣的演示,但不应该 。
#include <stdio.h> int main () { int *(*x)[5]; x = (int *(*)[5]) &x; printf("%p\n", x[0]); printf("%p\n", x[0][0]); printf("%p\n", x[0][0][0]); }
在C89和C99中编译都没有警告,输出如下:
$ ./ptrs 0xbfd9198c 0xbfd9198c 0xbfd9198c
有趣的是,所有三个值是相同的。 但这不应该是一个惊喜! 首先,我们来分解一下这个程序。
我们将x
声明为一个指向5个元素数组的指针,其中每个元素的types指针指向int。 这个声明在运行时栈上分配4个字节(或者更多,取决于你的实现;在我的机器指针上是4个字节),所以x
指的是实际的内存位置。 在C语言家族中, x
的内容只是垃圾,是以前使用该位置时遗留的东西,所以x
本身并不指向任何地方 – 当然不是指向已分配的空间。
所以,我们自然可以把variablesx
的地址放在某个地方,这正是我们所做的。 但是我们会继续把它放到x本身。 由于&x
有一个不同于&x
的types,我们需要做一个转换,所以我们不会收到警告。
内存模型看起来像这样:
0xbfd9198c +------------+ | 0xbfd9198c | +------------+
因此地址为0xbfd9198c
的4字节内存块包含与hex值0xbfd9198c
对应的位模式。 很简单。
接下来,我们打印出三个值。 其他答案解释了每个expression所指的是什么,所以现在应该清楚这种关系。
我们可以看到,值是相同的,但只是在一个非常低的水平意义上…他们的位模式是相同的,但与每个expression式相关的types数据意味着他们的解释值是不同的。 例如,如果我们使用格式string%d
打印出x[0][0][0]
,我们会得到一个巨大的负数,所以实际上“值”是不同的,但是位模式是一样。
这实际上很简单…在图中,箭头只是指向相同的内存地址,而不是指向不同的内存地址。 但是,虽然我们能够从未定义的行为中强制获得预期的结果,但这只是未定义的。 这不是生产守则,而只是为了完整性。
在合理的情况下,您将使用malloc
创build5个int指针的数组,并再次创build指向该数组的int。 malloc
总是返回一个唯一的地址(除非你内存不足,在这种情况下它返回NULL或0),所以你永远不用担心这样的自引用指针。
希望这是你正在寻找的完整答案。 你不应该期望x[0]
, x[0][0]
和x[0][0][0]
是相等的,但是如果被强制的话也可以。 如果有什么东西在你的头上,让我知道,所以我可以澄清!