我一直在玩多维数组和括号表示法,尽力从概念上理解去引用、指针类型和指针算法之间的相互作用。
对于此问题,请考虑以下任意3D数组引用:
arr[i][j][k]现在,我相信以下转换是等价的(我已经运行了一些我认为证实了准确性的代码示例):
*(*(*(arr+i)+j)+k)我运行的代码如下所示:
#include <stdio.h>
int main(void) {
char arr[2][3][4] = { 'a', 'b' ,'c' ,'d' ,'e' ,'f' ,'g' ,'h' ,'i' ,'j' ,'k' ,'l' ,'m' ,'n' ,'o' ,'p' ,'q' ,'r' ,'s' ,'t' ,'u' ,'v' ,'w' ,'x' };
printf("%c \n", arr[1][1][1]);
printf("%c \n", *(*(*(arr+1)+1)+1));
return 0;
}您可以更改printf语句中的值(只要i、j和k的值在两者之间是相同的)。无论如何,他们总是在打印到命令终端的字母上达成一致。
下面的图片描述了我获得这个假设的等价答案的方式(抱歉,我使用了数学堆栈交换编写软件,因为它使一切变得更容易一些……如果指针算法的表示法有点混乱,我道歉。)

因此,我的主要困惑来自于取消引用操作符*。坦白地说,--我不明白它在告诉编译器做什么。
对于一个简单的例子,让我们假设字符数组arr从100的基址开始。
arr实际上是指向char[3][4]类型的2D矩阵的指针。我相信这意味着(因为一个字符需要一个字节),代码arr + 1实际上是100+12。如果改写我的方程式,我就会:
*(*(*(112)+1)+1)。我相当肯定的是,取消引用操作符(112的左边)比+符号(112的右边)具有从左到右的优先权。
根据我在一维情况下使用去引用符号的经验,这将产生存储在112内存单元中的值。,但在目前的情况下,,这似乎是错的。显然,我不理解取消引用操作在这个上下文中的行为。任何帮助都是非常感谢的!
干杯~
发布于 2020-08-30 21:08:17
不,C中的指针不是整数,它们有不同的类型,与数组索引运算符或指针算术一起使用的指针必须引用完全类型的对象,即使用它们的位置已知其大小。
宣言
char arr[2][3][4];可以读到arr[i][j][k]是char类型。
它还告诉我们arr本身是char[2][3][4]类型的。每个一级元素,即arr[i]类型为char[3][4],每个二级元素为char[4]类型。如果对这些类型执行sizeof,
sizeof(char) is 1sizeof(char[4]) is 4sizeof(char[3][4]) is 12sizeof(char[2][3][4]) is 24现在,在几乎任何上下文中,数组类型的表达式被称为衰减到指向第一个元素的指针。这也适用于arr。由于arr的第一个元素是arr[0],并且从上面的类型或arr[0]是arr[i] (即char[3][4] )的类型,得到的类型是指向char[3][4]的指针;在C中,char[3][4]被写成(char *)[3][4]。
如果向该指针添加整数,则结果指针将指向该类型的nth (基于0)对象。
也就是说,arr + 0将产生一个指针,指向数组的第一个char[3][4]子数组;指向第二个数组的arr + 1,等等。此外,arr + n将指向内存位置(字节),即从arr开始时的n * sizeof(char[3][4])字节(也是n * sizeof(arr[0]))。
现在,如果取消引用该指针(例如*(arr + 1) ),则得到的表达式将是一个类型为char[3][4]的数组。但该值将立即衰减到指向该数组的第一个元素的指针;该数组的元素为char [4]类型,指针类型为char (*)[4]。
如果我们添加一个整数,如上面所示,我们将得到指向该数组的第n个char[4]子数组的指针;即使用(*(arr + 1) + 1)。现在,如果取消对指针*(*(arr + 1) + 1)的引用,我们就会得到一个类型为char[4]的表达式,但它会立即衰变为指向第一个元素的指针。由于char[4]的元素是字符,所以类型将是简单的、简单的.错误char *.
现在,如果我们添加一个整数,我们得到一个指向char[4]数组中的第n个元素的指针(例如,(*(*(arr + 1) + 1) + 1),如果我们取消引用它(*(*(*(arr + 1) + 1) + 1)) ),我们得到一个char类型的lvalue,它指定数组中的这个特定字符。
那么为什么需要取消引用操作符呢?当然要重新回到子物体上。
如果移除恒星,(((arr + 1) + 1) + 1)实际上等于arr + 3,这将是指向arr[3]的指针,即arr的第4 char[3][4]子数组。因为arr只有两个这样的子数组,它不仅不能实现您想做的事情,而且会导致可怕的未定义行为。
TL;DR:
根据上述定义,arr + 1和*(arr + 1)在衰变后将导致指针指向相同的内存位置,但具有不同的类型。
发布于 2020-08-30 20:59:27
有两件事你需要理解:
与一维阵列相比,
执行*(*(*(arr+1)+1)+1)时,arr将自动转换为指向其第一个元素的指针。该指针的类型为char(*)[3][4] (指向char[3][4]的指针)。将1添加到它可以通过1*sizeof(char[3][4]) == 12增加指针的值。你好像已经知道了。
删除结果指针会给出char[3][4]类型的lvalue,,它反过来将衰减为char(*)[4]类型的指针,等于&arr[1][0]。将1添加到该指针将使其增量为1*sizeof(char[4]),之后它等于&arr[1][1]。
删除结果指针将为您提供一个char[4]类型的值。它还会衰减为char *类型等于&arr[1][1][0]的指针。在将1添加到它之后,它的值由1*sizeof(char)增加,并与&arr[1][1][1]相等。然后,最终取消引用给您arr[1][1][1]的值。
发布于 2020-08-30 21:33:43
子脚本数组和指针具有相同的含义:a[i]告诉编译器计算数组的i-th元素的地址。如果在rvalue中使用a[i],则将读取此地址。
如果a是指针,则此计算需要读取指针的内容并将i乘以数组元素的大小。
如果a是数组,则此计算只需将数组元素大小的i乘以数组地址。
在这两种情况下,a[i]都可以重写为*(a+i),因为如果a是数组,它会自动衰减为表达式a+i中第一个元素的指针。这在概念上相当于:
int a[10];
int i, x;
x = a[i];
int *a_ = &a[0];
x = *(a_ + i);请注意,如果在rvalue上下文(如* )中使用a+i计算的地址,则*(a+i)中的x = *(a+i);将导致读取该地址,或者如果该值出现在lvalue上下文中:*(a+i) = x;中,则会将其写入该地址。语句*(a+i);既不读取也不写入数组元素(除非a被声明为volatile)。
在您的示例中,char arr[2][3][4]、arr[0][0][0]具有与arr[0][0]、arr[0]和arr相同的地址,它们只是有不同的类型。
https://stackoverflow.com/questions/63661935
复制相似问题