先给出问题:
像下面这样的数组,在函数中如何传参?也就是说如何保证虚参与实参类型一致。
char str_arr[3][10] = {"yes","no","uncertain"};
char *str_array[] = {"yes","no","unsure"};
函数原型:
void func1( char (*a)[10] )
void func2( char **a )
调用:
func1( str_arr );
func2( str_array);
如果向func2()中传入str_arr会怎么样呢?编译器会警告:传递参数 1 (属于 ‘func2’)时在不兼容的指针类型间转换。即虚参与实参类型不一致。
同理,也不能向func1()中传入str_array。
我们给出完整的测试程序:
/********二维数组传参测试程序***************/
#include <stdio.h>
void func1( char (*a)[10])
{
int i;
for(i=0;i<3;i++)
printf("%s/n",a[i]);
}
void func2( char **a )
{
int i;
for(i=0;i<3;i++)
printf("%s/n",*(a+i));
}
int main()
{
char str_arr[3][10] = {"yes","no","uncertain"};
char *str_array[] = {"yes","no","unsure"};
char *str[3] = {"a","b","c"};/*这两种表达效果一样*/
func1(str_arr);
func2(str_array);
return 0;
}
/******************end*******************/
运行结果:
[root@localhost ansi_c]# gcc test.c
[root@localhost ansi_c]# ./a.out
yes
no
uncertain
yes
no
unsure
[root@localhost ansi_c]#
如果将
func1(str_arr);
func2(str_array);
改成:
func1(str_array);
func2(str_arr);
会怎么呢?
[root@localhost ansi_c]# gcc test.c
test.c: 在函数 ‘main’ 中:
test.c:22: 警告:传递参数 1 (属于 ‘func1’)时在不兼容的指针类型间转换
test.c:23: 警告:传递参数 1 (属于 ‘func2’)时在不兼容的指针类型间转换
这两种数组的正确赋值应该如下:
char str_arr[3][10] = {"yes","no","uncertain"};
char *str_array[] = {"yes","no","unsure"};
char (*pa)[10] = str_arr;
char **p = str_array;
pa和p才是和他们相一致的类型。
当然,如果不是传参的话,在main()函数中就不会发生这么多烦恼了。
/*************非传参时的情况************************/
#include <stdio.h>
int main()
{
char str_arr[3][10] = {"yes","no","uncertain"};
char *str_array[] = {"yes","no","unsure"};
char *str[3] = {"a","b","c"};
int i;
for(i=0;i<3;i++)
printf("%s/n",str_arr[i]);
for(i=0;i<3;i++)
printf("%s/n",str_array[i]);
return 0;
}
/*************************************/
运行结果:
[root@localhost ansi_c]# gcc test1.c
[root@localhost ansi_c]# ./a.out
yes
no
uncertain
yes
no
unsure
[root@localhost ansi_c]#
这说明了一点,在没传参之前,main()函数清楚它们都是二维数组。对于上面给出的两种函数原型:
函数原型:
void func1( char (*a)[10] )
void func2( char **a )
这两种传参方法有什么不同呢?这们对实参有什么要求呢?
上面只是抛出了一个问题,我在这里的主题是想搞清楚二维数组传参有什么奥秘,而非只针对这一个问题提出解决方法。
后面从基础的开始讨论。
我们先看看教材上怎么讲这一块的,
谭浩强的《C程序设计》二维数组作为参数传递,原文如下(略有改变,请原谅):
[原文开始]
可以用二维数组名作为实参或者形参,在被调用函数中对形参数组定义时可以可以指定所有维数的大小,也可以省略第一维的大小说明,如:
void Func(int array[3][10]);
void Func(int array[][10]);
二者都是合法而且等价,但是不能把第二维或者更高维的大小省略,如下面的定义是不合法的:
void Func(int array[][]);
因为从实参传递来的是数组的起始地址,在内存中按数组排列规则存放(按行存放),而并不区分行和列,如果在形参中不说明列数,则系统无法决定应为多少行多少列,不能只指定一维而不指定第二维,下面写法是错误的:
void Func(int array[3][]);
实参数组维数可以大于形参数组,例如实参数组定义为:
void Func(int array[3][10]);
而形参数组定义为:
int array[5][10];
这时形参数组只取实参数组的一部分,其余部分不起作用。
[原文结束]
也就是说多维数组传参要指定第二维或者更高维的大小,可以省略第一维的大小。
像 int array[3][4],要传参的话,函数原型可以为下面三种的任一种:
void func(int a[3][4])
void func(int a[][4])
void func(int (*a)[4])
调用时为:func(array);
同时教材里也说了,如果在型参里不说明列数,则编译器无法决定应为多少行多少列。那么能不能把
int array[3][4]的数组名 array 传给 void func(int **a)呢?
看下面:
/**********************************/
#include <stdio.h>
int main()
{
int array[3][4];
int **p = array;
}
**********************************/
root@localhost ansi_c]# gcc test2.c
test2.c: 在函数 ‘main’ 中:
test2.c:5: 警告:从不兼容的指针类型初始化
[root@localhost ansi_c]#
虽然从本质上讲int array[3][4] 的数组名相当于二级指针,但它不等同于一般的二级指针,因为它还含有数组相关的信息,所以在main函数中:
char str_arr[3][10] = {"yes","no","uncertain"};
for(i=0;i<3;i++)
printf( "%s/n",str_arr+i );
它可以通过下标,每次跳过10个字节来寻址。我们再看看编译器是怎样处理数组的:
对于数组 int p[m][n];
如果要取p[i][j]的值(i>=0 && i<m && 0<=j && j < n),
编译器是这样寻址的:
p + i*n + j;
我们再看一个例子:
/*********************二维数组传参*****************************/
#include <stdio.h>
void fun( int *a, int m, int n)
{
int i,j;
for( i=0; i<m; ++i)
{
for(j=0;j<n;++j)
{
printf("%d ", *( a+i*n+j ) );
}
putchar('/n');
}
}
void func( int *a, int m, int n)
{
int i,j;
for( i=0;i<m*n;++i)
{
printf("%d ",a[i]);
}
putchar('/n');
}
int main()
{
int a[3][3] =
{
{1, 1, 1},
{2, 2, 2},
{3, 3, 3}
};
fun( (int *)a, 3,3);
func( &a[0][0],3,3);
func( (int *)a, 3,3);
return 0;
}
********************end******************************/
[root@localhost ansi_c]# gcc test4.c
[root@localhost ansi_c]# ./a.out
1 1 1
2 2 2
3 3 3
1 1 1 2 2 2 3 3 3
1 1 1 2 2 2 3 3 3
[root@localhost ansi_c]#
我们来看其中的要点,
数组为:
int a[3][3] =
{
{1, 1, 1},
{2, 2, 2},
{3, 3, 3}
};
函数原型和调用为:
原型:
void fun( int *a, int m, int n)
{
.............
printf("%d ", *( a+i*n+j ) );
.............
}
调用:
fun( (int *)a, 3,3);
另一个函数为:
原型:
void func( int *a, int m, int n)
{
.............
printf("%d ",a[i]);
.............
}
调用:
func( &a[0][0],3,3);
func( (int *)a, 3,3);
我们发现这两种方式都能正常执行,我们把一个二级指针,强制转换成了一级指针传了进去,并在函数中模仿编译器数组的寻址方式:*( a+i*n+j )。
我们再看看二维字符数组的例子:
/*******************二维字符数组*******************************/
#include <stdio.h>
void f( char **a, int n)
{
int i;
printf("%c/n",*( (char*)a+0 ) );
printf("%c/n",((char * )a)[n] );
puts("------------OK");
for(i=0;i<3;i++)
printf("%s/n",(char*)a+i*n );
}
int main()
{
char str_arr[3][10] = {"yes","no","uncertain"};
f( (char **)str_arr, 10);
return 0;
}
/****************end*************************/
运行结果:
[root@localhost ansi_c]# ./a.out
y
n
------------OK
yes
no
uncertain
[root@localhost ansi_c]#
这里也做了强制类型转换,转换成字符指针,
printf("%s/n",(char*)a+i*n ); 每个字符串的地址就是数组中字符'y'、'n'、'u'的地址,
printf("%c/n",*( (char*)a+0 ) );字符在数组中的排列是顺序的,可以用 *( (char*)a+i )或 ((char * )a)[i] 表示。
当然这个程序也可以改成这样,完全不用二级指针:
/*****************************************************************/
#include <stdio.h>
void f( char *a, int n)
{
int i;
printf("%c/n",*( a+0 ) );
printf("%c/n",(a)[n] );
puts("------------OK");
for(i=0;i<3;i++)
printf("%s/n",a+i*n );
}
int main()
{
char str_arr[3][10] = {"yes","no","uncertain"};
f( (char *)str_arr, 10);
return 0;
}
/*****************************************************************/
归根结底,还是把它转成一级指针来用。
下面做个小结:
数组传参
数组:
int array[4][10];
函数原型:
void func1( int a[][10] );
void func2( int (*a)[10] );
void func3( int *p, int col, int row );
函数调用:
func1( array );
func2( array );
func3( (int *)array, 4, 10 );
容易出错的地方:
int arr[][10];
int **p = arr;
这种方式是错误的.
应该是
int (*p)[10] = arr;
同理,也不能将arr传给fun( int **p)
另外数组传参之后会降级为指针,如:
#include <stdio.h>
void Test(char a[][2])
{
int size = sizeof( a );//4
}
int main(void)
{
char a[3][2] = {'a','b','c','d','e','f'};
int size =sizeof( a );//6
Test( a );
}
函数原型:要求传入一个动态二维数组
void func1(int **p,int row, int column)
{
}
调用:
int main()
{
int m,n;
int **b;
cin >> m;
cin >> n;
b = new int *[m];
for(int i=0; i<m; i++)
{
b[i] = new int[n];
};
func1(b,m,n);
return 0;
}
我习惯的做法是不用指针数组,定义一个大块(这种情况主要面向每行行数相同):比如现在有一个W*H的矩阵(H个长度为W的数组),你就直接定义一个float型指针: float* pfBuffer;然后动态分配大小 pfBuffer = new float[W*H];这个buffer在用完之后要调用 delete pfBuffer;来释放.你传递这个float指针,传递行列数之后,你如果要访问y行x列的话,只要算一下它在哪儿, int addr = y*W+x;就是其"地址"了,你要访问它,直接使用pfBuffer[addr]就OK了,实际上我做图象处理的时候全部这样做,因为这样的地址访问很明了,不会给阅读带来不便,而且作为大部分的时候,我们用矩阵比较多,列数不等的情况很少。这只是个人见解。