C/C++中访问多维静态数组的指针

3

我知道,静态数组在内存中是连续排列的。例如,int T[10][10]基本上与int T[100]存储方式相同。我可以通过多种方式访问索引为i,j的元素,例如:

int T[10][10];
/*filling array*/
int i=3, j=7;
int x = T[i][j];
//EDIT - old and wrong: int * ptr = T;
int * ptr = &T[0][0];
int y = *(ptr + 10* i + j);

另一方面,当我自己创建动态分配的二维数组时:

int ** T;
T = malloc(10 * sizeof(int *));
for(i = 0; i < N; i++) 
    T[i] = malloc(10 * sizeof(int));

我的数组包含指针

显然,我可以通过以下方式访问这个数组的元素:

int i=3, j=7;
int x = *(*(T+i)+j);

现在我的问题是:为什么静态数组可以工作,它是如何工作的? 为什么?
int T[10][10];
/*filling array*/
int i=3, j=7;
int x = *(*(T+i)+j);

当这个表不包含指向数组的指针时,如何将好的值返回给x?在我看来,*(*(T+i)) 在那里没有意义,即使有,它也应该返回T[0][i],因为T指向数组的第一个元素。编译器如何解释这个问题,* 在这里是否是其他的东西而不是解引用?请给我解释一下。


4
  1. C不等于C++,这段程序不能在C++编译通过。
  2. 你的先决条件有误:int **不是一个二维数组,不能指向一个二维数组,也不能代表一个二维数组。
  3. 分配一个二维数组。你认为静态数组的假设为什么不适用于动态分配的二维数组?
- too honest for this site
尝试通过指针访问2D数组是一个不好的想法。2D数组在不同的机器上存储方式不同。 - user6754053
1
@MarkYisri:错误!写T[i][j]*(*(T+i)+j)是完全相同的(根据[]运算符的定义)。 - Martin Bonner supports Monica
@MartinBonner: 对于2D数组和指向指针(也称为不规则数组)它们具有非常不同的语义!相同的语法并不意味着相同的语义! - too honest for this site
@TheVee:你看过[]运算符的定义吗?抽象机器将涉及两个解引用。编译器将优化为您显示的表达式。(尽管请注意,实际上它将是*((int*)T + i*s + j) - 强制转换是必需的,因为T会衰减为“指向包含10个整数的数组的指针” - 并且偏移量将全错)。 - Martin Bonner supports Monica
2个回答

4

首先:

int * ptr = T;

这样做实际上是行不通的,至少你的编译器会大声抱怨。正确的做法是:

int * ptr = &t[0][0];

这个问题与你的问题实际上非常相关。

众所周知,在表达式中使用数组会被退化为指针。例如:

char foo[10];

bar(foo);

当数组被用于表达式中,比如作为函数的参数时,数组会退化(decay)为指向底层类型的指针。这里使用foo会得到一个char *类型的指针。

但是需要注意的是:数组只会退化一级。如果该数组是一个二维数组,在这种情况下,该数组不会退化为底层值(在本例中为int)。而是会退化为指向一维数组的指针。

int T[10][10];
/*filling array*/
int i=3, j=7;
int x = *(*(T+i)+j);

以下是发生的步骤:

  1. T 衰变成一个指向包含 10 个整数的数组的指针,或者说是 int (*)[10]

  2. 加上 i 的值会将指针往前移动。指针向前移动的距离取决于所指向元素的大小。由于指针指向的是 包含 10 个整数的数组,因此指针会相应地向前移动。如果 i 是2,则粗略地说,指针会向前移动两个 包含10个整数的数组 的距离。

  3. * 运算符将 "指向包含 10 个整数的数组" 转换为 "一个包含 10 个整数的数组"。换句话说:从 int (*)[10]int [10]

  4. 由于结果用在表达式中,即 + j 的左操作数中,而左操作数是数组类型,因此数组类型会衰减为 "指向 int 的指针"。

  5. j 添加到结果中,并进行解引用操作。


int *ptr = T; should be equal to int *ptr = &T[0][0]; - user6754053
@MarkYisri - 你错了。自己试试看: t.C:4:14: 错误:无法将 'int (*)[10]' 转换为 'int*' 进行初始化 int * ptr = T; - Sam Varshavchik
我有点错过了答案。OP似乎在问为什么相同的语法对于二维数组和int **都有效。这似乎是初学者(以及一些不太初级的人)认为后者也是二维数组的主要原因(他们是错误的)。 - too honest for this site
C语言只需要一个显式转换,这里是完全可以的:int *ptr = (int *) T;,因为多维数组的第一行始于数组的开头,所以总是会得到预期的结果! - Serge Ballesta

1

Why does

int T[10][10];
/*filling array*/
int i=3, j=7;
int x = *(*(T+i)+j);

return good value to x

"魔法都在 *(*(T+3)+7) 中(我已将其转换为文字值)。
T 是一个大小为 10 的数组,其中包含大小为 10 的 int 数组。
当 T 在表达式中使用时,它会退化为指向其第一个元素的指针,因此它会退化为“指向大小为 10 的 int 数组”的指针”。
将整数添加到该指针将推进到数组的第四个元素。
因此,T+3 是指向 10 个 int 的数组的指针,特别是 T 中的第四个这样的数组。
*(T+3) 通过该指针间接引用,以给出类型为“10 个 int 的数组”的 l 值。
啊哈!这是另一个在表达式中使用的数组 - 因此它会衰减为指向其第一个元素的指针! (在 sizeof 中不会衰减,因此 sizeof(*(T+3)) 通常为 40。)
(*(T+3) + 7) 只是指向数组中的第八个元素,然后..."
"

*(*(T+3) + 7)是int类型的l-value!

"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接