[英]Passing arrays to functions without using pointers
C 允许直接使用数组名称将数组传递给函数。 由于数组名称是数组的起始地址(元素 [0] 的地址) - 这是通过引用传递 - 因此即使本地数组也可以通过这种方式传递 - 并且任何一个函数的更改对另一个函数都是可见的,没有任何需要明确的回报。 到目前为止我的理解是——
函数调用只需要包含提供起始地址的数组名称 - 以及每个维度的整数大小作为单独的 int 参数 - 这样,除包含数组定义的函数之外的任何函数都具有数组位置的所有信息和大小(数组大小以及每个维度的大小)并且可以对任何元素进行操作。 再一次,由于这是通过引用传递的,因此任何此类更改将对所有此类函数都可见,包括包含数组定义的函数(它将需要将相同的信息作为参数传递给每个函数 - 起始地址的数组名称和每个维度大小),无需任何返回,这些更改对于包括调用函数在内的所有函数都是可见的。
函数原型和函数定义中的数组参数用一对方括号为同一参数中的每个维度 + 指定数组 - 最大的? 除了最重要的维度(二维数组中的行)之外的每个维度的值也被指定,例如 -
//Prototype return_type function_name ( array_name[][MAX_COLS], int rows, int cols); // Function call somewhere function_name (array_name, num_rows, num_columns); /* array name refers to starting address */
也为最大的尺寸? value 在原型和定义中指定(对于我们的示例为 MAX_COLS),调用期间的实际值,指定为单独的参数 (cols),可能并且将大部分不同。
我不知道为什么其他维度(MAX_COLS)需要大小,这将是非常好的解释。 另外,请根据需要更正、确认、改进一切。
使用一维指针数组模拟二维数组的示例代码 -
#define MAX_ROWS 20
extern void modify( int rows, int cols, int *a[]);
extern void input_2d_array(int rows, int cols, int *a[]);
extern void output_2d_array(int rows, int cols, int *a[]);
int main()
{
int rows = 4, cols = 3;
int* a[MAX_ROWS], i;
for (i=0; i<rows; i++)
a[i] = (int*) malloc ( cols*sizeof(int) );
input_2d_array(rows, cols, a);
printf("Initially in main array = \n");
output_2d_array(rows, cols, a);
modify(rows, cols, a);
printf("Finally in main array = \n");
output_2d_array(rows, cols, a);
_getch();
return 0;
}
void input_2d_array(int rows, int cols, int *a[])
{
int i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
{
printf("Please enter the elements for row %d and column %d of a \n", i + 1, j + 1);
scanf_s(" %d", (a+(i*cols)+j) );
}
return;
}
void output_2d_array(int rows, int cols, int *a[])
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
printf(" %d", *(a + (i*cols) + j) );
}
printf("\n");
}
return;
}
void modify(int rows, int cols, int *a[])
{
int i, j;
printf("Initally in modify array = \n");
output_2d_array(rows, cols, a);
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
*(a + (i*cols) + j) = (*(a + (i*cols) + j)) + 2;
// *(*(a + i) + j) += 2; // Gives exception
// *(a[i]+j) += 10; // Gives exception
}
}
printf("Finally in modify array = \n");
output_2d_array(rows, cols, a);
return;
}
有一些误解。 解释它们需要时间。
您的示例原型是(或多或少):
//Prototype ReturnType function_name(ArrayType array_name[][MAX_COLS], int rows, int cols);
在某处,您对MAX_COLS
有一个常量定义; 为了论证,它可以是:
enum { MAX_COLS = 64 };
此函数仅接受具有MAX_COLS
列的数组,尽管数组可以具有任意数量的行。 int cols
参数对该数组的定义无关紧要。 在调用代码中,必须使用MAX_COLS
(或其等效项)作为第二维来定义数组。
在函数内部,编译器会将引用array_name[i][j]
( i
和j
是整数类型)解释为:
*(array_name + (i * MAX_COLS + j))
它永远不会在该计算中使用cols
。 尝试将具有不同行宽的数组传递给函数会导致 - 我将称其为“未定义行为”,尽管对于哪种不受欢迎且未完全指定的行为适用,可能存在严格不同的解释。 结果不会是你所期望的。 如果你传递一个更窄的数组,你很可能会索引超出实际数组的范围; 如果你传递一个更宽的数组,你可能不会越界,但是(当访问越界时),你不会访问你期望访问的元素,因为计算将基于MAX_COLS
,而不是函数参数cols
。
使用可变长度数组 (VLA),您可以将函数修改为:
ReturnType function_name(int rows, int cols, ArrayType array_name[rows][cols]);
注意大小变量必须在使用前定义; 大小参数必须先于它们在数组定义中的使用。
现在,在函数内部,编译器将array_name[i][j]
(和以前一样)解释为:
*(array_name + (i * cols + j))
您可以通过阵列的任何形状,只要你得到的rows
和cols
参数是否正确。
在采用 VLA 的函数的原型中可以使用多种其他符号,包括:
ReturnType function_name(int rows, int cols, ArrayType array_name[*][*]);
ReturnType function_name(int rows, int cols, ArrayType array_name[][*]);
但是,函数定义需要在数组定义之前出现cols
:
ReturnType function_name(int rows, int cols, ArrayType array_name[rows][cols]) { … }
ReturnType function_name(int rows, int cols, ArrayType array_name[][cols]) { … }
使用 'unsized' 领先维度,你可以(如果你足够反常的话)写:
ReturnType function_name(int cols, ArrayType array_name[][*], int rows);
ReturnType function_name(int cols, ArrayType array_name[][cols], int rows) { … }
我自己的观点是,如果函数原型声明与函数定义行完全匹配,那么维护代码会更简单,所以我不会在原型中使用带有*
的符号。 我还将使用显式ArrayType array_name[rows][cols]
表示法,指示哪个参数指定每个大小。 如果您执行矩阵乘法函数,这很重要,例如:
void MatrixMultiply(int r1, int c1, int c2,
Data m1[r1][c1], Data m2[c1][c2], Data result[r1][c2]);
编译器是否会报告矩阵大小不匹配的问题是有争议的,但机会就在那里。 如果你愿意,你可以有一个冗余的r2
参数来指定m2
矩阵的行大小,但是如果r2 != c1
,你就会遇到问题,编译器更难提供帮助 - 你被简化为断言或其他错误报告机制,以表明由于矩阵大小与乘法不兼容,因此对结果没有任何用处。
我不确定我是否已经解构了你问题中的所有误解; 我怀疑不是。
来自评论:
如果我的函数签名是
void modify(int rows, int cols, int *a[])
那么我如何访问被调用函数中的a[i][j]
和&a[i][j]
?
这需要一个指向int
的指针数组,因此您必须以与之前不同的方式(在调用代码中)设置该数组。 但是,在函数内部,您只需编写:
int x = a[i][j];
int *y = &a[i][j];
int *z = a[i] + j;
y
和z
的表达式是等价的; 我可能会使用前者。
请注意,此处x
的“幕后”计算与传递数组时用于a[i][j]
的公式不同:
int x = *(*(a + i) + j);
有两个内存引用,第一个读取指针a[i]
,第二个读取a[i][j]
(有两个显式加法但没有显式乘法,尽管下标加法必须按大小缩放int *
和int
),而使用数组表示法,只有一个内存引用(有两个显式加法和一个显式乘法——只有一个按int
大小的缩放操作)。
同样的评论还说:
我尝试
a[i]+j
和(a[i]+j)
组合以及*(a+(i*cols)+j)
和(a+(i*cols)+j)
,但都没有工作。
我很有可能没有在正确的位置添加*
符号,尤其是在句子的“以及”部分。 请在新的额外(或替换)评论中提供正确的符号,我可以剖析您实际输入的内容,而不是我猜测您输入的内容。
a[i] + j
应该给你一个指向数据第i 行中第j个整数的指针。 您必须将其包装为*(a[i] + j)
才能获得int
值。 括号中的版本还为您提供了指向元素a[i][j]
的指针; 您需要在前面加一个*
才能获得该值。
像*(a+(i*cols)+j)
这样的变体是错误的,因为在这种情况下,您不需要乘以列数。 不需要a[i]
和a[i+1]
指向的连续行在内存中是连续的; 它们可以位于多个不相交的内存块中(实际上也可以有多个指向单个内存块的指针)。 当然,在一行中,元素必须是连续的。
这大致是int a[][4]
之间的区别:
+---+---+---+---+
| 2 | 3 | 5 | 7 |
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
| 7 | 5 | 3 | 2 |
+---+---+---+---+
| 9 | 8 | 7 | 6 |
+---+---+---+---+
和int *a[]
:
+------------+ +---+---+---+---+
| 0x010C0304 |------>| 2 | 3 | 5 | 7 |
+------------+ +---+---+---+---+
| 0x020D0408 |------>| 1 | 2 | 3 | 4 |
+------------+ +---+---+---+---+
| 0x030E050C |------>| 7 | 5 | 3 | 2 |
+------------+ +---+---+---+---+
| 0x040F0600 |------>| 9 | 8 | 7 | 6 |
+------------+ +---+---+---+---+
请注意,与int a[][4]
相比,保存int *a[]
数据需要更多的存储空间。 另请注意,我已将int *a[]
的 4 个数字数组int *a[]
不连续。 此外, int *a[]
示例中的行可以有不同的长度,前提是您知道如何访问长度(或者长度都至少为 4 并且您不超过第四个元素)。
正如我在上一节中所说,当您使用“指针数组”表示法时, cols
标计算中使用cols
是错误的。 这是对您的代码的改编 - 我不得不删除特定于 Windows 的功能,例如_getch()
和scanf_s()
( scanf_s()
使用 'nothing' 和scanf()
)。 我还展示了一些替代方案; 印刷品显示了一些替代方案。
#include <stdio.h>
#include <stdlib.h>
#define MAX_ROWS 20
extern void modify(int rows, int cols, int *a[]);
extern void input_2d_array(int rows, int cols, int *a[]);
extern void output_2d_array(int rows, int cols, int *a[]);
int main(void)
{
int rows = 4, cols = 3;
int *a[MAX_ROWS], i;
for (i = 0; i < rows; i++)
a[i] = (int *)malloc(cols * sizeof(int));
input_2d_array(rows, cols, a);
printf("Initially in main array =\n");
output_2d_array(rows, cols, a);
modify(rows, cols, a);
printf("Finally in main array =\n");
output_2d_array(rows, cols, a);
//_getch();
return 0;
}
void input_2d_array(int rows, int cols, int *a[])
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
printf("Please enter the elements for row %d and column %d of a\n", i + 1, j + 1);
//scanf_s(" %d", (a + (i * cols) + j));
//scanf(" %d", (a + (i * cols) + j));
scanf(" %d", &a[i][j]);
//scanf(" %d", a[i] + j);
//scanf(" %d", *(a + i) + j);
}
}
}
void output_2d_array(int rows, int cols, int *a[])
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
//printf(" %d", *(a + (i * cols) + j));
printf(" %d", a[i][j]);
printf(" (%d)", *(*(a + i) + j));
}
printf("\n");
}
}
void modify(int rows, int cols, int *a[])
{
int i, j;
printf("Initally in modify array =\n");
output_2d_array(rows, cols, a);
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
a[i][j] = a[i][j] + 2;
*(a[i] + j) = *(*(a + i) + j) + 2;
//*(a + (i * cols) + j) = (*(a + (i * cols) + j)) + 2;
// *(*(a + i) + j) += 2; // Gives exception
// *(a[i]+j) += 10; // Gives exception
}
}
printf("Finally in modify array =\n");
output_2d_array(rows, cols, a);
}
示例运行(程序mda17
):
$ mda17
Please enter the elements for row 1 and column 1 of a
23
Please enter the elements for row 1 and column 2 of a
24
Please enter the elements for row 1 and column 3 of a
25
Please enter the elements for row 2 and column 1 of a
26
Please enter the elements for row 2 and column 2 of a
27
Please enter the elements for row 2 and column 3 of a
28
Please enter the elements for row 3 and column 1 of a
99
Please enter the elements for row 3 and column 2 of a
98
Please enter the elements for row 3 and column 3 of a
97
Please enter the elements for row 4 and column 1 of a
96
Please enter the elements for row 4 and column 2 of a
95
Please enter the elements for row 4 and column 3 of a
94
Initially in main array =
23 (23) 24 (24) 25 (25)
26 (26) 27 (27) 28 (28)
99 (99) 98 (98) 97 (97)
96 (96) 95 (95) 94 (94)
Initally in modify array =
23 (23) 24 (24) 25 (25)
26 (26) 27 (27) 28 (28)
99 (99) 98 (98) 97 (97)
96 (96) 95 (95) 94 (94)
Finally in modify array =
27 (27) 28 (28) 29 (29)
30 (30) 31 (31) 32 (32)
103 (103) 102 (102) 101 (101)
100 (100) 99 (99) 98 (98)
Finally in main array =
27 (27) 28 (28) 29 (29)
30 (30) 31 (31) 32 (32)
103 (103) 102 (102) 101 (101)
100 (100) 99 (99) 98 (98)
$
检查这个并尝试查找数组是否作为数组或指向某个函数的指针传递:
char arr[]={'a','b','c'};
int sizeOfArr = sizeof(arr)/sizeof(arr[0]);
在函数中使用上述方法查找数组的大小,其中arr[]
已创建并已传递 - sizeOfArr
将破译事物。 这可能会清楚为什么要明确传递大小/尺寸 - 这就是我可以从您的整个陈述中得出的全部内容。 如果我错了,请纠正我。
不使用指针 - 将数组包装到结构中。 然后整个结构(包括数组)将被传递给函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.