指针参数
实参与形参是一种赋值关系(引用除外)。指针参数可以分为两种情况:指针做为实参赋值给形参,除特殊情况下,形参应该是与实参相同类型的指针;另外一种情况则是实参可能是数组等类型,形参则可能是一些特殊的指针类型。
一维数组情况
一维数组和指针都是一种线性关系,基本上可以通用
#include <iostream>
#include <stdio.h>
using namespace std;
void fun(int *x) //void fun(int x[10])
{
int i;
//cout<<sizeof(x)<<endl;
for(i=0;i<10;i++)
x[i]=i;
}
int main()
{
int a[10];
int i;
fun(a);
for(i=0;i<10;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
函数定义形参可以是指针,也可以是数组,其实就是指针;用sizeof(x)可以看到不管是指针还是10元素的数组,都是4个字节;结论应该形参应该是指针。
实参肯定用数组名,其他方式都不可能符合语法:a[10]肯定不行,没有这个元素(0~9)。同样也可以验证形参应该是指针,指针保存地址,数组名是首地址。
数组不一定写成 int a[10],甚至可以写成其他尺寸大小的数组:void fun(int x[5]) 语法可以通过,运行结果都一样,进一步验证了把形参统一成指针,程序员保证数组不越界访问。
二维数组、二维指针、指针数组、数组指针
二维数组具有指定的二维结构,并且按照一维的方式进行物理存储,也就是说物理上所有元素在连续的内存空间里,逻辑上有行列下标特点;
二维指针是指向指针的指针变量,它保存的是另一个指针的地址;
指针数组本质上是数组,它的每一个元素都是指针,逻辑上具有下标特点;
数组指针本质上是指针,是一种特殊的指针,它保存一个指定尺寸的数组的(首)地址。
从上面描述,可以看看相互强制转换:
1、二维数组转换成一维指针,是可取的:物理上本来就是一维的结构,但逻辑上的行需要通过一维指针的计算来实现;
#include <iostream>
#include <stdio.h>
using namespace std;
void fun(int *x)
{
int i,j;
for(i=0;i<5;i++)
for(j=0;j<4;j++)
// *(x+4*i+j) =i * j;
x[4*i+j]=i * j;
}
int main()
{
int a[5][4];
int i,j;
fun((int *)a);
for(i=0;i<5;i++)
{
for(j=0;j<4;j++)
cout<<a[i][j]<<" ";
cout<<endl;
}
return 0;
}
强制转换的依据是 物理实现的连续性: x[ i * 4 + j ] 第几行第几列要算清楚;另外下标的方式和指针取值的方式是等价的。
2、二维数组与二维指针,二维数组具有行地址的特点,数组名 a + i 是第i行的起始地址,与行的大小相关;二维指针则认为保存的是另一个指针的地址。
下标操作肯定无法满足要求:二维数组先通过行下标找行,二维指针则是加一个指针变量的大小,4个字节。
然后,取内容操作必须要把二维指针看成是一维指针,再通过上面的方法来实现。
void fun(int **x)
{
int i,j;
for(i=0;i<5;i++)
for(j=0;j<4;j++)
*((int *)x+4*i+j) =i * j;
}
函数调用时:fun((int **)a); 强转
3、二维数组与指针数组:两个都是数组,逻辑上指针数组是一维的,每个元素都是一个指针。强制转换是不成立的。要说明的它们的区别:指针数组可以通过下标找到每个元素,而每个元素都是一个指针,所以有时候可以用二维数组的行列下标来实现对元素的引用;
int main()
{
int a[2][3]={1,2,3,4,5,6};
int *x[2];
int i,j;
for(i=0;i<2;i++)
x[i]=new int[3];
for(i=0;i<2;i++)
for(j=0;j<3;j++)
x[i][j]=a[i][j];
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
cout<<x[i][j]<<" ";
cout<<endl;
}
return 0;
}
new是动态分配空间,定义数组是静态分配空间: int x[3];区别是new在堆区,程序员控制回收,局部变量数组则是在栈区。上面代码可见,指针数组确实可以具有二维的结构,每个元素都是指针,指针当然可以指向一个数组。
指针数组与二维数组的区别当然也很明显:首先,指针数组的元素是指针,是一个个变量可以赋值;而二维数组的行地址是常量;然后,指针数组的元素可以指向一个数组,也可以指向其他的普通变量,二维数组当然不行(也就是说指针数组的下标操作是有风险的);最后,即使是指针数组的每个元素都指向一个数组,每个元素指向的数组大小可以不相同的,每行存放的位置也可以不连续,也就是说每行长度不一,每行也不需要连续存储。
结论:指针数组下标引用有风险。
4、二维数组与数组指针:它们的关系是一维数组和指针的关系,也就是可以通用,不需要强制转换。数组指针是一个特殊指针,正好保留了二维数组的行列特点。数组指针指向的是一个特殊数组,加法运算应该是加特殊数组的大小,正好满足行地址的操作!
结论:二维数组和数组指针通用,最好用这种方式进行参数传递。
#include <iostream>
#include <stdio.h>
using namespace std;
void fun(int (*x)[4])
{
int i,j;
for(i=0;i<5;i++)
for(j=0;j<4;j++)
x[i][j]=i * j;
}
int main()
{
int a[5][4];
int i,j;
fun(a);
for(i=0;i<5;i++)
{
for(j=0;j<4;j++)
cout<<a[i][j]<<" ";
cout<<endl;
}
return 0;
}