C语言中的二维数组和指针 - 如何访问元素?

11

我有一个涉及指向2D数组的指针的示例。 有人可以帮助我理解这个示例中正在发生的事情吗?

int main()
{

    int i = 0, j=0, sum0=0, sum1=0;
    int data[4][3] = { {23,55,50},{45,38,55},{70,43,45},{34,46,60}};
    int *Ptr;
    Ptr = *data;    //Why is the indirection operator used here? 
                    // Does Ptr = 23 by this assignment?

    for (i=0; i<4; i++) {
        sum1 = 0;
        for (j = 0; j < 3; j++) {
            sum1 += data[i][j];
        }
        if (sum1 > sum0) {
                 sum0 = sum1;
                 Ptr = *(data + i);     // Seems like this statement makes Ptr
        }                               // point one row below ... what syntax
    }                                   // can you use to access columns then?
                                       // Is it possible to use pointer arithmetic
    for (i=0; i<3; i++)                 // to access elements of data[i][j] that
        printf("%d\n", Ptr[i]);          // are not at j = 0?

  return 0;
}

是的!我正在学习一门计算机科学入门课程,这只是我的讲座笔记中提供的一个例子。 - shafools
5个回答

21

data 是一个二维数组,有4行,每行有3个元素(即4 X 3)。

现在,Ptr = *data; 意味着你将第一行的起始地址存储到指针变量 Ptr 中。这条语句等同于 Ptr = *(data + 0)Ptr = *(data + 1) - 这意味着我们正在分配第二行的起始地址。

然后,*Ptr*(Ptr + 0) 将给出指向的行的第一个元素的值。类似地,*(Ptr + 1) 将给出指向的行的第二个元素的值。

你的程序中的 for 循环用于识别哪行具有其元素总和(3个元素)的最大值。一旦控制流程出了那个 for 循环,Ptr 将指向具有其元素总和最大值的行,而 sum0 的值将为该总和。

考虑一个数组 int a[5];,希望你知道 a[0]0[a] 是相同的。这是因为 a[0] 表示 *(a+0),而 0[a] 表示 *(0 + a)。这个逻辑也可以用于二维数组。

data[i][j] 相当于 *(*(data + i) + j)。我们也可以将其写为 i[data][j]

更多详情请参阅 Yashavant Kanetkar 的书《C指针理解》。


7
Ptr = *data;*(data+0)+0的简写,它是指向第一行第一列元素的指针。添加到data的第一个0是行号,是间接的,并将我们带到第一行。* (data+0)仍然是一个地址而不是它指向的值(对于2D数组)。因此,Ptr现在指向第一行中第一列的地址。第二个零是列号。所以选择了第一行和第一列的内存地址。再次使用间接(*)只会给出地址所包含的值。例如:* (*(data+0)+0)**data
通常,如果p是指针名称,i是行号,j是列号,
  1. (*(p+i)+j)将给出2D数组中元素的内存地址。i是行号,j是列号,
  2. *(*(p+i)+j)将给出该元素的值。
  3. *(p+i)将访问第i行
  4. 要访问列,请将列号添加到*(p+i)。您可能需要将指针声明为(*p)[columns]而不仅仅是*p。这样做,您声明了指向2D数组的指针。
使用指针算术将2D数组视为1D数组。将指针*Ptr初始化为第一个元素(int *Ptr = *data),然后添加数字(Ptr + n)以访问列。添加比列号更高的数字将简单地继续计算下一行的第一列中的元素,如果存在的话。

1

data 是由整数的 3 元素数组组成的数组。在期望“指向 foo 的指针”的上下文中,您可以使用“foo 的数组”,它将像指向其第一个元素的指针一样运行,因此 *data 是指向 data 的第一个元素的指针,也就是说 {23,55,50}

所以,在评论中的第一个问题的答案是:不,Ptr = 23 不是真的。(它不可能是;Ptr 是一个 int *,而 23 是一个 int。)

您正确地指出了 Ptr = *(data+i) 使 Ptr 指向 data 的第 i 行。更准确地说,data 是由整数的 3 元素数组组成的数组,它的行为类似于指向整数的 3 元素数组的指针;将 i 添加到它上面会移动过去这样的数组。

访问数组的其他列的常规方法是使用普通的数组索引。如果您引用 data[i][j],则获取行 i 的列 j。如果您想使用显式指针算术来完成它,则请注意,例如代码中的 Ptr 是类型为“整数指针”,因此 Ptr+1(例如)是指向 Ptr 指向的任何行的元素 1。(但是,出于风格的考虑,当您实际上不需要时,通常不应执行显式指针算术。)

0
在你的例子中,循环遍历矩阵的所有行,以找到所有元素总和最大的那一行。
在开始时,指针被分配给第一行。
Ptr = *data;

这意味着以下内容是正确的:
(Ptr[0] == 23 && Ptr[1] == 55 && Ptr[2] == 50)

请注意,Ptr是指针,因此它保存了一个内存地址,因此Ptr23不同(除非内存地址碰巧是23,这种情况不太可能发生)。

0

C语言支持多维数组,将它们在内存中布局为连续的位置,并进行更多的地址算术运算。 考虑一个二维数组。

int arr[ 3 ][ 3 ] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

编译器将二维数组处理为数组的数组,其中数组名称是指向数组中第一个元素的指针。因此,arr 指向第一个 3 元素数组,实际上是二维数组的第一行(即第 0 行)。同样,(arr + 1) 指向第二个 3 元素数组(即第 1 行),以此类推。这个指针的值 *(arr+1) 引用了整个行。由于第 1 行是一维数组,所以 (arr+1) 实际上是指向第一行中第一个元素的指针。现在将该指针加 2。因此,(*(arr + 1) + 2) 是指向第 1 行第 2 个元素(即第三个元素)的指针。此表达式的值 *(*(arr+1)+2) 是第一行第二列元素的值。

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