最近在读《C与指针》,看到指针与数组的一节中对数组和指针区别的说明,更进一步地理解了指针和数组在函数参数中的不同。以下是我写的一个简单的例子:
#include <stdio.h>
void show_0( int a[][ 3 ] )
{
printf( "0: %d\n", **( ++a ) );
}
void show_1( int ( *a )[ 3 ] )
{
printf( "1: %d\n", **( ++a ) );
}
void show_2( int *a[ 3 ] )
{
printf( "2: %d\n", **( ++a ) );
}
void show_3( int **a )
{
printf( "3: %d\n", **( ++a ) );
}
int main()
{
int m[ 2 ][ 3 ] = {
{ 1, 2, 3 },
{ 4, 5, 6 } };
show_0( m );
show_1( m );
int a = 1;
int b = 2;
int c = 3;
int *p[ 3 ] = { &a, &b, &c };
show_2( p );
show_3( p );
return 0;
}
其中,二维数组(type) a[][]
与(type) ( *a )[]
是等价的,而二阶指针(我不清楚这个意思对应哪个专业术语,所以只好自己造了一个)(type) **a
和(type) *a[]
即(type) *( a[] )
等价。
总结一下就是,a[]
与*a
两个形式是等阶的,可以相互代替使用。因此int a[][]
可以替换成int (*a)[]
,但它不是int *( a[] )
,因此不能使用**a
来代替。不过下面的方法还是可行的:
#include <stdio.h>
typedef int * ip;
void show( ip *a )
{
printf( "%d\n", a[ 0 ] );
}
int main()
{
int a[][ 3 ] = { {1,2,3},{4,5,6} };
show( a );
return 0;
}
2015年3月9日更新
下面我们看一些更加唬人的东西。
先给结果:
void *( **trans_func( void *( *fa[] )( void * ) ) )( void * );
在讨论这货之前,需要先对C中的标识符和类型之间的关系有一个深刻的认识。在语句中,标识符处于核心地位,所有有关数据类型的内容,都是对标识符的描述。举个简单的例子:
void *f( void * );
这个声明旨在说明f
的类型属性。由于f
紧跟了一个括号,因此它是一个函数。先看函数括号内部void *
表明它的参数是一个void
指针。再看函数括号外部void *f()
,因为括号的结合性要高于取指针,故f()
的返回值是void *
,即它也返回了一个void
指针。
上边那个十分复杂的例子,也用这样的方法来分析。
先看函数括号内部
void *( *fa[] )( void * )
这说明trans_func
函数只有一个参数fa
,至于这个参数是个什么东西,还要一点一点分析。在由外向内进行剥离分析时,应当先分析结合性低的运算,再分析结合性高的运算。对于*fa[]
,显然这个东西是一个函数,它的原型是void *X( void * )
,就是上文中分析的简单例子。*fa[]
是函数,就说明fa[]
是指向函数的指针,这个指针又位于fa
这个数组之中。至此,我们知道了,fa
是一个数组,它的元素是一些指向函数的指针,它们指向的函数以一个空指针为参数,并返回一个空指针。
下面再分析trans_func
返回值部分。此时trans_func(...)
就都可以用X
来代替。即
void *( **X )( void * )
这样,**X
就又是一个上文中与f()
类型相同的函数。从形式上来说,**X
是一个指向指针的指针。一般来说,如果函数参数是指针的指针,只有两种情形,一是函数要修改指针的一些属性如修改指针地址或者为指针分配空间等,另一种可能是实参并不是指针的指针而是一个指针数组。而如果函数的返回值是二阶指针的话,通常就只需考虑指针数组这一种情况了。就此例而言,将X
看为是指向指针数组的指针更加合适。
至此,trans_func
分析完毕,该函数的参数和返回值具有相同的类型,都是指向一个数组的指针,这个数组的元素又是一些函数指针,这些函数指针所指向的函数接收一个空指针并返回一个空指针。
当然上述例子只是为了分析类型而刻意“造”出来的写法,通常考虑到代码的可读性,这个声明可以写成下边的样子:
typedef void *( *func_p )( void * );
func_p *trans_func( func_p fa[] );