将 2D 数组传递给函数
将 2d 数组传递给函数似乎很简单明了,我们乐意写:
#include <stdio.h>
#include <stdlib.h>
#define ROWS 3
#define COLS 2
void fun1(int **, int, int);
int main()
{
int array_2D[ROWS][COLS] = { {1, 2}, {3, 4}, {5, 6} };
int n = ROWS;
int m = COLS;
fun1(array_2D, n, m);
return EXIT_SUCCESS;
}
void fun1(int **a, int n, int m)
{
int i, j;
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
printf("array[%d][%d]=%d\n", i, j, a[i][j]);
}
}
}
但编译器,版本 4.9.4 中的 GCC,并不是很好。
$ gcc-4.9 -O3 -g3 -W -Wall -Wextra -std=c11 passarr.c -o passarr
passarr.c: In function ‘main’:
passarr.c:16:8: warning: passing argument 1 of ‘fun1’ from incompatible pointer type
fun1(array_2D, n, m);
^
passarr.c:8:6: note: expected ‘int **’ but argument is of type ‘int (*)[2]’
void fun1(int **, int, int);
造成这种情况的原因有两个:主要问题是数组不是指针,第二个不便是所谓的指针衰减。将数组传递给函数会将数组衰减为指向数组第一个元素的指针 - 在 2d 数组的情况下,它会衰减到指向第一行的指针,因为 C 数组是按行排序的。
#include <stdio.h>
#include <stdlib.h>
#define ROWS 3
#define COLS 2
void fun1(int (*)[COLS], int, int);
int main()
{
int array_2D[ROWS][COLS] = { {1, 2}, {3, 4}, {5, 6} };
int n = ROWS;
int m = COLS;
fun1(array_2D, n, m);
return EXIT_SUCCESS;
}
void fun1(int (*a)[COLS], int n, int m)
{
int i, j;
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
printf("array[%d][%d]=%d\n", i, j, a[i][j]);
}
}
}
这是必要通过的行数,它们不能被计算出来。
#include <stdio.h>
#include <stdlib.h>
#define ROWS 3
#define COLS 2
void fun1(int (*)[COLS], int);
int main()
{
int array_2D[ROWS][COLS] = { {1, 2}, {3, 4}, {5, 6} };
int rows = ROWS;
/* works here because array_2d is still in scope and still an array */
printf("MAIN: %zu\n",sizeof(array_2D)/sizeof(array_2D[0]));
fun1(array_2D, rows);
return EXIT_SUCCESS;
}
void fun1(int (*a)[COLS], int rows)
{
int i, j;
int n, m;
n = rows;
/* Works, because that information is passed (as "COLS").
It is also redundant because that value is known at compile time (in "COLS"). */
m = (int) (sizeof(a[0])/sizeof(a[0][0]));
/* Does not work here because the "decay" in "pointer decay" is meant
literally--information is lost. */
printf("FUN1: %zu\n",sizeof(a)/sizeof(a[0]));
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
printf("array[%d][%d]=%d\n", i, j, a[i][j]);
}
}
}
Version = C99
列数是预定义的,因此在编译时固定,但当前 C 标准的前身(即 ISO / IEC 9899:1999,当前是 ISO / IEC 9899:2011)实现了 VLA(TODO:链接它)和虽然目前的标准使它成为可选项,但几乎所有现代 C 编译器都支持它(TODO:检查 MS Visual Studio 现在是否支持它)。
#include <stdio.h>
#include <stdlib.h>
/* ALL CHECKS OMMITTED!*/
void fun1(int (*)[], int rows, int cols);
int main(int argc, char **argv)
{
int rows, cols, i, j;
if(argc != 3){
fprintf(stderr,"Usage: %s rows cols\n",argv[0]);
exit(EXIT_FAILURE);
}
rows = atoi(argv[1]);
cols = atoi(argv[2]);
int array_2D[rows][cols];
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
array_2D[i][j] = (i + 1) * (j + 1);
printf("array[%d][%d]=%d\n", i, j, array_2D[i][j]);
}
}
fun1(array_2D, rows, cols);
exit(EXIT_SUCCESS);
}
void fun1(int (*a)[], int rows, int cols)
{
int i, j;
int n, m;
n = rows;
/* Does not work anymore, no sizes are specified anymore
m = (int) (sizeof(a[0])/sizeof(a[0][0])); */
m = cols;
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
printf("array[%d][%d]=%d\n", i, j, a[i][j]);
}
}
}
这不起作用,编译器抱怨:
$ gcc-4.9 -O3 -g3 -W -Wall -Wextra -std=c99 passarr.c -o passarr
passarr.c: In function ‘fun1’:
passarr.c:168:7: error: invalid use of array with unspecified bounds
printf("array[%d][%d]=%d\n", i, j, a[i][j]);
如果我们通过将声明更改为 void fun1(int **a, int rows, int cols)
来故意在函数调用中出错,则会更清楚一些。这导致编译器以不同但同样模糊的方式抱怨
$ gcc-4.9 -O3 -g3 -W -Wall -Wextra -std=c99 passarr.c -o passarr
passarr.c: In function ‘main’:
passarr.c:208:8: warning: passing argument 1 of ‘fun1’ from incompatible pointer type
fun1(array_2D, rows, cols);
^
passarr.c:185:6: note: expected ‘int **’ but argument is of type ‘int (*)[(sizetype)(cols)]’
void fun1(int **, int rows, int cols);
我们可以通过多种方式做出反应,其中之一就是忽略所有这些并做一些难以辨认的指针杂耍:
#include <stdio.h>
#include <stdlib.h>
/* ALL CHECKS OMMITTED!*/
void fun1(int (*)[], int rows, int cols);
int main(int argc, char **argv)
{
int rows, cols, i, j;
if(argc != 3){
fprintf(stderr,"Usage: %s rows cols\n",argv[0]);
exit(EXIT_FAILURE);
}
rows = atoi(argv[1]);
cols = atoi(argv[2]);
int array_2D[rows][cols];
printf("Make array with %d rows and %d columns\n", rows, cols);
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
array_2D[i][j] = i * cols + j;
printf("array[%d][%d]=%d\n", i, j, array_2D[i][j]);
}
}
fun1(array_2D, rows, cols);
exit(EXIT_SUCCESS);
}
void fun1(int (*a)[], int rows, int cols)
{
int i, j;
int n, m;
n = rows;
m = cols;
printf("\nPrint array with %d rows and %d columns\n", rows, cols);
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
printf("array[%d][%d]=%d\n", i, j, *( (*a) + (i * cols + j)));
}
}
}
或者我们做得对,并将所需信息传递给 fun1
。为此,我们需要重新排列 fun1
的参数:列的大小必须在数组声明之前。为了使其更具可读性,保持行数的变量也改变了它的位置,现在是第一个。
#include <stdio.h>
#include <stdlib.h>
/* ALL CHECKS OMMITTED!*/
void fun1(int rows, int cols, int (*)[]);
int main(int argc, char **argv)
{
int rows, cols, i, j;
if(argc != 3){
fprintf(stderr,"Usage: %s rows cols\n",argv[0]);
exit(EXIT_FAILURE);
}
rows = atoi(argv[1]);
cols = atoi(argv[2]);
int array_2D[rows][cols];
printf("Make array with %d rows and %d columns\n", rows, cols);
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
array_2D[i][j] = i * cols + j;
printf("array[%d][%d]=%d\n", i, j, array_2D[i][j]);
}
}
fun1(rows, cols, array_2D);
exit(EXIT_SUCCESS);
}
void fun1(int rows, int cols, int (*a)[cols])
{
int i, j;
int n, m;
n = rows;
m = cols;
printf("\nPrint array with %d rows and %d columns\n", rows, cols);
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
printf("array[%d][%d]=%d\n", i, j, a[i][j]);
}
}
}
对于一些认为变量顺序无关紧要的人来说,这看起来很尴尬。这不是什么大问题,只需声明一个指针,让它指向数组。
#include <stdio.h>
#include <stdlib.h>
/* ALL CHECKS OMMITTED!*/
void fun1(int rows, int cols, int **);
int main(int argc, char **argv)
{
int rows, cols, i, j;
if(argc != 3){
fprintf(stderr,"Usage: %s rows cols\n",argv[0]);
exit(EXIT_FAILURE);
}
rows = atoi(argv[1]);
cols = atoi(argv[2]);
int array_2D[rows][cols];
printf("Make array with %d rows and %d columns\n", rows, cols);
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
array_2D[i][j] = i * cols + j;
printf("array[%d][%d]=%d\n", i, j, array_2D[i][j]);
}
}
// a "rows" number of pointers to "int". Again a VLA
int *a[rows];
// initialize them to point to the individual rows
for (i = 0; i < rows; i++) {
a[i] = array_2D[i];
}
fun1(rows, cols, a);
exit(EXIT_SUCCESS);
}
void fun1(int rows, int cols, int **a)
{
int i, j;
int n, m;
n = rows;
m = cols;
printf("\nPrint array with %d rows and %d columns\n", rows, cols);
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
printf("array[%d][%d]=%d\n", i, j, a[i][j]);
}
}
}