im 编写一个程序,我想在其中使用这段代码在函数中传输 2D 数组,但我并不完全了解它是如何工作的。有人可以解释一下,特别是第 7 行吗?
#include <stdio.h>
void print(int *arr, int m, int n)
{
int i, j;
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
printf("%d ", *((arr+i*n) + j));
}
int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int m = 3, n = 3;
// We can also use "print(&arr[0][0], m, n);"
print((int *)arr, m, n);
return 0;
}
我也尝试使用
*( *(p + i) + j)
相反,但它并没有真正起作用,我不知道为什么,所以如果有人能解释为什么这不起作用,我将不胜感激。
在现代 C 中,您应该使用 C99 中引入的可变长度数组类型。
void print(int m, int n, int arr[m][n])
{
int i, j;
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
printf("%d ", arr[i][j]);
}
该函数应使用简单的调用:
int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int m = 3, n = 3;
print(m, n, arr);
return 0;
}
VLA 类型在 C11 中是可选功能,但在 C23 中它们将是必需的。
您将一个2 元数组传递给您的打印函数,其中包含单个数组中的项目数量和 2D 数组中的数组数量。
现在让我们进入循环:
首先,如果 i 和 j 都是零,你会得到第一个数组的第一个项目。在内循环的下一次迭代中,j 是 1,因此(arr+i*n) + 1
指向第一个数组的 2 元素,因为 i 仍然为零,j 将为 1 ((arr + 0 * 3) + 1
)。在下一次迭代中,它是相同的,但 i 是 2,因此指向第二个元素。
当内部循环完成时,i 增加到 1,表达式现在(arr + 1 * 3) + 0
。所以现在i * 3
将指向第二个数组的第一个元素。
在外循环的第三次迭代中,i
将指向第三个数组的第一个元素。因此,在此 2D 数组中,i * 3
始终是指向数组第一个元素的指针,并且+ j
始终指向数组中的单个元素。通过组合它,可以打印2D阵列。
*( *(p + i) + j)
不起作用,因为假设 p 是指向数组 arr 的指针,因为您要取消引用它,因此在第一次迭代中它将计算为*(1 + 0)
这会导致分段错误,因为您不被允许读取此内存地址。这是因为通过取消引用它,您*(p + 0)
引用第一个数组的第一个元素,即 1。
在int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
中,1、2 和 3 初始化一个 3int
的数组。数组是一组连续分配的对象,因此 1、2 和 3 在内存中是连续的。4、5 和 6 初始化另一个 3int
数组,7、8 和 9 也是如此。这三个 3 个int
数组本身就是另一个数组,一个由 3 个数组组成的数组,每个 3 个int
数组。由于数组是一组连续分配的对象,因此 3 个数组在内存中是连续的。因此,4、5 和 6 遵循 1、2 和 3,7、8 和 9 遵循 4、5 和 6。
因此,总体效果是 1、2、3、4、5、6、7、8 和 9 在内存中是连续且连续的。
*((arr+i*n) + j)
使用此事实来计算元素在第i
行和列j
中的位置。数组的起始地址为arr
。i*n
是从开始到i
行的元素数。也就是说,每行都有n
元素,因此i
行都有i*n
元素。然后j
是从行的开头到该行第j
列中的元素的元素数。因此,arr + i*n + j
是第i
行、列j
中的元素所在位置,*(arr + i*n + j)
是该元素。*((arr+i*n) + j)
中多余的括号是不必要的。
此代码滥用 C 类型模型。在main
中,arr
是一个由3个数组组成的数组,每个数组的数组为3int
。当main
调用print
时,它会传递(int *)arr
。这会传递指向int
的指针,而不是指向 3int
数组的指针,然后print
绕过数组类型的正常应用程序来访问内存。从技术上讲,此代码的行为不是由 C 标准定义的,但它适用于许多 C 实现。
C 是一种非常简单的语言,它之所以流行,主要是因为简单部分被设计为以取代以前语言的复杂部分的方式组合(参见for
作为示例)。一个副作用是它省略了您在其他语言中期望的部分。
特别是对于数组,数组没有关于其大小或格式的信息,假设程序员会跟踪它,或者每个维度的大小,但第一个维度是恒定的(通常第一个也是)。因此,无论数组声明为多少维,数组都只是一个内存块,足以容纳所有元素,并且位置是使用[]
运算符在内部计算的。
有趣的是,C 允许您将a[1]
指定为1[a]
,因为它都转换为加法和乘法。但不要那样做。
如果你有一个数组,它的大小是多个维度的可变大小,C 不支持这一点,所以你必须自己做数学运算,这就是print()
函数正在做的事情,其中m
和n
是维度的大小。第一行从arr
(或arr + 0
)开始,转到arr + (n - 1)
(0 到 n-1 是 n 个元素),在支持它的语言中看起来像arr[0][0]
arr[0][n-1]
。下一行从arr + n
开始(将arr[1][0]
)到arr + (2 * n) - 1
,依此类推(直到arr[m-1][n-1]
)。
在此处的函数中,i
和j
分别从 0 变为 m-1 和 n-1,因此您在代码中看不到- 1
。
还有一件事,C 至少足够有用,知道当你在指针上使用+
时,你的意思是按你指向的东西的大小递增,所以你不必弄清楚一个int
中有多少字节。