我正在研究一些C ++,我正在用指针进行斗争。我明白通过声明我可以有3个级别的指针:
int *(*x)[5];
以便*x
是一个指向5个元素数组的指针,这些元素是指向int
的指针。
我也知道x[0] = *(x+0);
,x[1] = *(x+1)
等等......
所以,鉴于上述声明,为什么x[0] != x[0][0] != x[0][0][0]
?
答案 0 :(得分:258)
x
是一个指向int
的5个指针数组的指针
x[0]
是指向int
的5个指针的数组
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]
是一个数组,在表达式中使用时会转换为指向其第一个元素的指针(有一些例外)。因此,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]
。
答案 1 :(得分:133)
x[0] != x[0][0] != x[0][0][0]
是根据你自己的帖子,
*(x+0) != *(*(x+0)+0) != *(*(*(x+0)+0)+0)`
简化
*x != **x != ***x
为什么要平等?
第一个是某个指针的地址
第二个是另一个指针的地址
第三个是int
值。
答案 2 :(得分:49)
以下是指针的内存布局:
+------------------+
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
运营商。但这并不是对价值的实际使用,其结果仅取决于类型。
您可以使用其地址生成x
的值,i。即具有类型int*(*)[5]
的“数组地址”。换句话说:&x[0] <=> &*(x + 0) <=> (x + 0) <=> x
在所有其他上下文中,x[0]
的值将衰减为指向数组中第一个元素的指针。也就是说,指针的值为“数组地址”和类型int**
。效果与将x
转换为int**
类型指针的效果相同。
由于情况3中的数组指针衰减,x[0]
的所有使用最终都会产生指向指针数组开头的指针;调用printf("%p", x[0])
将打印标记为“数组地址”的存储单元格的内容。
答案 3 :(得分:18)
x[0]
将最外面的指针(指针)取消引用指向int的指针大小为5的数组,并生成一个指向int
的指针大小为5的数组; x[0][0]
取消引用最外层指针和索引数组,从而生成指向int
的指针; x[0][0][0]
取消引用所有内容,从而产生具体价值。顺便说一句,如果您对这些声明的含义感到困惑,请使用cdecl。
答案 4 :(得分:11)
让我们逐步考虑表达式x[0]
,x[0][0]
和x[0][0][0]
。
由于x
的定义方式如下
int *(*x)[5];
然后表达式x[0]
是类型为int *[5]
的数组。考虑到表达式x[0]
等同于表达式*x
。这是取消引用指向数组的指针,我们得到数组本身。让我们像y那样表示我们有一个声明
int * y[5];
表达式x[0][0]
相当于y[0]
,类型为int *
。让我们像z一样表示我们有一个声明
int *z;
表达式x[0][0][0]
相当于表达式y[0][0]
,而表达式z[0]
相当于表达式int
,并且类型为x[0]
。
所以我们有
int *[5]
的类型为x[0][0]
int *
的类型为x[0][0][0]
int
的类型为std::cout << sizeof( x[0] ) << std::endl;
std::cout << sizeof( x[0][0] ) << std::endl;
std::cout << sizeof( x[0][0][0] ) << std::endl;
因此,它们是不同类型的对象,并且具有不同的大小。
运行例如
{{1}}
答案 5 :(得分:10)
首先我要说的是
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
答案 6 :(得分:7)
如果您要从现实世界的角度来看这些数组,那么它将如此显示:
x[0]
是一个装满板条箱的货运集装箱
x[0][0]
是货箱内装满鞋盒的单个箱子
x[0][0][0]
是装箱内的单个鞋盒,位于货运集装箱内。
即使它是货运集装箱中唯一的箱子里唯一的鞋盒,它仍然是鞋盒而不是货运集装箱
答案 7 :(得分:4)
C ++中有一个原则:一个变量的声明表明了使用变量的方式。请考虑您的声明:
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
所以你可以找出差异。
答案 8 :(得分:2)
成为p
指针:您正在使用p[0][0]
堆叠取消引用,这相当于*((*(p+0))+0)
。
在C引用(&amp;)和解除引用(*)表示法中:
p == &p[0] == &(&p[0])[0] == &(&(&p[0])[0])[0])
相当于:
p == &*(p+0) == &*(&*(p+0))+0 == &*(&*(&*(p+0))+0)+0
看一下,&amp; *可以重构,只需删除它:
p == p+0 == p+0+0 == p+0+0+0 == (((((p+0)+0)+0)+0)+0)
答案 9 :(得分:1)
您正在尝试按值比较不同类型
如果您使用地址,您可能会获得更多您期望的地址
请记住,您的声明会有所作为
int y [5][5][5];
会允许您想要的比较,因为y
,y[0]
,y[0][0]
,y[0][0][0]
会有不同的值和类型,但地址相同
int **x[5];
不会占据连续的空间。
x
和x [0]
具有相同的地址,但x[0][0]
和x[0][0][0]
各自位于不同的地址
答案 10 :(得分:1)
其他答案是正确的,但没有一个强调所有三个都可能包含相同值的想法,因此它们在某种程度上是不完整的。
从其他答案中无法理解的原因是,所有插图虽然有用且在大多数情况下绝对合理,但未能涵盖指针x
指向自身的情况。 / p>
这很容易构建,但显然有点难以理解。在下面的程序中,我们将看到我们如何强制所有三个值相同。
注意:此程序中的行为未定义,但我在此处发布纯粹是为了指示可以做的事情的有趣演示,但< EM>不该&#39;吨
#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个元素数组的指针,其中每个元素都是int类型的指针。此声明在运行时堆栈上分配4个字节(或更多取决于您的实现;在我的机器指针上是4个字节),因此x
指的是实际的内存位置。在C语言系列中,x
的内容只是垃圾,是之前使用该位置所遗留的内容,因此x
本身并不指向任何地方 - 当然不会分配空间。
所以,当然,我们可以将变量x
的地址放在某个地方,这样我们就可以做到。但我们会继续把它放到x本身。由于&x
的类型与x
不同,我们需要进行投射,因此我们不会收到警告。
内存模型看起来像这样:
0xbfd9198c
+------------+
| 0xbfd9198c |
+------------+
因此,地址0xbfd9198c
处的4字节内存块包含与十六进制值0xbfd9198c
对应的位模式。很简单。
接下来,我们打印出三个值。其他答案解释了每个表达式所指的内容,因此现在应该清楚这种关系。
我们可以看到值是相同的,但只是在非常低的层次意义上......它们的位模式是相同的,但与每个表达式相关联的类型数据意味着它们的解释值是不同的。
例如,如果我们使用格式字符串x[0][0][0]
打印出%d
,我们就会得到一个巨大的负数,因此&#34;值&#34;实际上是不同的,但位模式是相同的。
这实际上非常简单......在图表中,箭头只指向相同的内存地址而不是指向不同的内存地址。然而,虽然我们能够从未定义的行为强制预期结果,但它只是未定义的。这不是生产代码,只是为了完整性而进行演示。
在合理的情况下,您将使用malloc
创建5个int指针的数组,并再次创建该数组中指向的int。 malloc
总是返回一个唯一的地址(除非你内存不足,在这种情况下它返回NULL或0),所以你永远不必担心像这样的自引用指针。 / p>
希望这是您正在寻找的完整答案。您不应期望x[0]
,x[0][0]
和x[0][0][0]
相等,但如果强迫它们就可以。如果有什么事情发生了,请告诉我,我可以澄清一下!
答案 11 :(得分:0)
int *(*x)[5]
的类型为int* (*)[5]
,即指向5个指向整数的指针的数组的指针。
x
是指向int的5个指针的第一个数组的地址(类型为int* (*)[5]
的地址)x[0]
指向int的5个指针的第一个数组的地址(类型为int* [5]
的相同地址)(将地址x偏移0*sizeof(int* [5])
,即index * type-of-being -指向和取消引用)x[0][0]
是数组中一个int的第一个指针(与类型int*
相同的地址)(将地址x偏移0*sizeof(int* [5])
并先解除引用再偏移0*sizeof(int*)
和取消引用)x[0][0][0]
是指向int的指针所指向的第一个int值(将地址x偏移0*sizeof(int* [5])
并用0*sizeof(int*)
取消引用和偏移该地址,并使用$ {1}取消引用并偏移该地址。 0*sizeof(int)
并取消引用) int *(*y)[5][5][5]
的类型为int* (*)[5][5][5]
,即指向5x5x5指向整数的3d数组的指针
x
是指向类型为int*(*)[5][5][5]
的int的5x5x5指针的第一个3d数组的地址。x[0]
是指向int的5x5x5指针的第一个3d数组的地址(将地址x偏移0*sizeof(int* [5][5][5])
并取消引用)x[0][0]
是指向int的5x5指针的第一个2d数组的地址(将地址x偏移0*sizeof(int* [5][5][5])
,然后取消引用,然后将该地址偏移0*sizeof(int* [5][5])
)x[0][0][0]
是指向int的5个指针的第一个数组的地址(将地址x偏移0*sizeof(int* [5][5][5])
,然后将该地址解引用并偏移0*sizeof(int* [5][5])
,然后将该地址偏移{{1 }})0*sizeof(int* [5])
是指向数组中int的第一个指针(将地址x偏移x[0][0][0][0]
,然后将该地址解引用并偏移0*sizeof(int* [5][5][5])
,然后将该地址偏移0*sizeof(int* [5][5])
并将该地址偏移0*sizeof(int* [5])
并取消引用)0*sizeof(int*)
是指向int的指针所指向的第一个int值(将地址x偏移x[0][0][0][0][0]
,然后将该地址解引用并偏移0*sizeof(int* [5][5][5])
,然后将该地址偏移{{ 1}},然后将该地址偏移0*sizeof(int* [5][5])
,然后取消引用,然后将该地址偏移0*sizeof(int* [5])
,然后取消引用)关于数组衰减:
0*sizeof(int*)
这等效于传递0*sizeof(int)
或void function (int* x[5][5][5]){
printf("%p",&x[0][0][0][0]); //get the address of the first int pointed to by the 3d array
}
,即它们都衰减到后者。这就是为什么您不会在函数中使用int* x[][5][5]
时收到编译器警告的原因,但您会为int* (*x)[5][5]
收到编译器警告,因为该大小信息已保留
x[6][0][0]
x[0][6][0]
是指向int的5x5x5指针的第一个3d数组的地址void function (int* (*x)[5][5][5]){
printf("%p",&x[0][0][0][0][0]); //get the address of the first int pointed to by the 3d array
}
是指向int的5x5指针的第一个2d数组的地址x[0]
是5个指向整数的指针的第一个数组的地址x[0][0]
是指向数组中int的第一个指针x[0][0][0]
是指向int的指针所指向的第一个int 在最后一个示例中,使用x[0][0][0][0]
而不是x[0][0][0][0][0]
在语义上要清晰得多,这是因为这里的第一个和最后一个*(*x)[0][0][0]
是
由于类型的原因,它被解释为指针取消引用,而不是多维数组的索引。但是它们是相同的,因为x[0][0][0][0][0]
与语义无关。您还可以使用[0]
,它看起来像是对指针进行了5次解引用,但实际上它的解释完全相同:偏移量,解引用,解引用,数组中的2个偏移量和解引用,完全是因为您要将操作应用于的类型。
基本上,当您将(*x) == x[0]
或*****x
的{{1}}设为非数组类型时,由于[0]
的优先顺序,它是偏移量和取消引用。
当您将*
或*
到数组类型的*(a + 0)
时,它是一个偏移量,然后是幂等解引用(该解引用由编译器解析以产生相同的地址–这是一个幂等运算。)
当您[0]
或*
具有一维数组类型的类型时,它是一个偏移量然后是一个取消引用
如果您*
或[0]
是2d数组类型,则它仅是偏移量,即偏移量和幂等解引用。
如果您*
或[0]
是3D数组类型,则它是偏移+幂等解引用,然后是偏移+幂等解引用,然后是偏移+幂等解引用,然后是解引用。真正的取消引用仅在完全剥离数组类型时发生。
以**
为例,该类型按顺序展开。
[0][0][0]
的类型为***
int* (*x)[1][2][3]
具有类型x
(偏移量0 +幂等解引用)int* (*)[1][2][3]
具有类型*x
(偏移量0 +幂等解引用)int* [1][2][3]
具有类型**x
(偏移量0 +幂等解引用)int* [2][3]
的类型为***x
(偏移量0 +取消引用)int* [3]
的类型为****x
(偏移量0 +取消引用)