为什么是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]包含一个int10

所以, 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中不能做任何事情的东西。你总是操纵他们的地址或者他们的元素,而不是整个数组。

  1. 您可以将x[0]传递给sizeof运算符。 但是,这不是一个真正的价值的使用,其结果只取决于types。

  2. 你可以把它的地址得到x的值,即“数组的地址”,types为int*(*)[5] 。 换句话说: &x[0] <=> &*(x + 0) <=> (x + 0) <=> x

  3. 所有其他上下文中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]; 

将会允许你想要的比较,因为yy[0]y[0][0]y[0][0][0]将具有不同的值和types,但是相同的地址

 int **x[5]; 

不占用连续的空间。

xx [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]是相等的,但是如果被强制的话也可以。 如果有什么东西在你的头上,让我知道,所以我可以澄清!