如何解决取消引用运算符告诉多维数组引用期间编译器要做什么?
我一直在研究多维数组和括号符号,并尽力对解引用,指针类型和指针算术之间的相互作用有一个概念上的理解。
对于此问题,请考虑以下任意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矩阵的指针。我认为这意味着(因为一个字符需要1个字节),代码arr + 1
实际上是在说100+12
。重写方程式,然后我将得到:
*(*(*(112)+1)+1)
。我相当确定,解除引用运算符(在112
的左侧)比+
符号(在112
的右侧)具有从左到右的优先级。
根据我在一维情况下使用取消引用符号的经验,这将产生存储在112个存储单元中的值。 但这在当前情况下似乎是错误的。显然,我不了解取消引用操作在这种情况下的行为。任何帮助将不胜感激!
干杯〜
解决方法
您需要了解两件事:
-
数组不是指针,即使在大多数情况下它们会自动转换为指针。
-
与一维数组相比,多维数组得到零特殊处理。它们是一维数组,其中每个元素也是一个数组。
执行*(*(*(arr+1)+1)+1)
时,arr
将自动转换为指向其第一个元素的指针。该指针的类型为char(*)[3][4]
(指向char[3][4]
的指针)。向其添加1
会使指针的值增加1*sizeof(char[3][4]) == 12
。您似乎已经知道这一点。
取消对结果指针的引用会为您提供类型char[3][4]
的左值,继而衰减到类型为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]
的值。
否,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)
是1 -
sizeof(char[4])
是4 -
sizeof(char[3][4])
是12 -
sizeof(char[2][3][4])
是24
现在,在几乎 any 上下文中,数组类型的表达式被称为 decay 指向 first 元素的指针。这也适用于arr
。由于arr
的第一个元素是arr[0]
,并且从上面的类型或arr[0]
是arr[i]
的元素,即char[3][4]
,因此结果类型是指向char[3][4]
的指针;在C中用(char *)[3][4]
写。
如果向该指针添加整数,则结果指针将指向该类型 的第n个(从0开始)对象。
即arr + 0
将导致一个指向该数组的 first char[3][4]
子数组的指针; arr + 1
到 second ,依此类推。另外,arr + n
指向内存位置(字节),该位置距n * sizeof(char[3][4])
的开头n * sizeof(arr[0])
个字节(也是arr
)。 / p>
现在,如果我们取消引用该指针,例如*(arr + 1)
,结果表达式将是类型为char[3][4]
的 array 。但是,该值将立即衰减为指向该数组第一个元素的指针。该数组的元素类型为char [4]
,指针类型为char (*)[4]
。
如果我们如上所述添加一个整数,我们将获得一个指向该数组的第n个char[4]
子数组的指针;即使用(*(arr + 1) + 1)
。现在,如果取消引用指针*(*(arr + 1) + 1)
,我们将得到类型为char[4]
的表达式,但它会立即衰减为指向第一个元素的指针。由于char[4]
的元素是字符,因此类型将是简单明了的Garak ... err char *
。
现在,如果我们向其添加一个整数,我们将获得一个指向char[4]
数组中第n个 element 的指针(例如(*(*(arr + 1) + 1) + 1)
,如果我们取消引用({ {1}}),我们得到类型*(*(*(arr + 1) + 1) + 1)
的左值,它在数组中指定此特定字符。
那么为什么需要取消引用运算符?当然要引用子对象。
如果删除星星,char
实际上等于(((arr + 1) + 1) + 1)
,它是指向arr + 3
的指针,即 4th {{1 }} arr[3]
的子数组。由于char[3][4]
仅具有 2 个这样的子数组,因此它不仅无法执行您想要的操作,而且会导致可怕的不确定行为。
TL; DR:
鉴于上述定义arr
和arr
,在衰减之后,将导致指向相同内存位置但具有不同类型的指针。
下标数组和指针的含义相同:a[i]
告诉编译器计算数组第i
个元素的地址。如果在右值中使用a[i]
,则会读取该地址。
如果a
是指针,则此计算需要读取指针的内容,并向其添加数组元素大小的i
倍。
如果a
是一个数组,则此计算仅涉及将数组元素大小的i
乘以数组的地址。
在两种情况下,a[i]
都可以重写为*(a+i)
,因为如果a
是一个数组,它会自动衰减为指向表达式{{1}的第一个元素的指针}。从概念上讲,它等效于:
a+i
请注意, int a[10];
int i,x;
x = a[i];
int *a_ = &a[0];
x = *(a_ + i);
中的*
会导致*(a+i)
计算的地址被读取,如果该值是在右值上下文中使用的,例如a+i
或被写入如果它出现在左值上下文中:x = *(a+i);
。语句*(a+i) = x;
既不读取也不写入数组元素(除非将*(a+i);
声明为a
)。
在您的示例中,volatile
,char arr[2][3][4]
与arr[0][0][0]
,arr[0][0]
和arr[0]
具有相同的地址,只是它们的类型不同。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。