一、数组形参
数组在传入时,实际上只传入指向其首元素的指针。数组在“退化”时会丢失边界。
void average(int ary[12]); //形参ary是一个int *
//...
int anArray[] = {1,2,3};
average(anArray); //合法
void average(int ary[], int size); //传统的做法是将数组大小传入
如果数组边界的精确数组很重要,并且希望函数只接受含特定数量的元素的数组,可以考虑引用形参。
void average(int (&ary)[12]); //只接受大小为12的int数组
average(anArray); //错误!anArray是int[3]
多维数组形参,只是第一维数组退化为指针,后面的维数边界没有退化,否则无法对形参执行指针算术
void process(int ary[10][20]);
void process(int (*ary)[20]); //ary是一个指针,指向一个大小为20的int数组
void process(int ary[][20]); //建议定义为这样,更清晰
二、指针算术
指针+1不是将指针的地址增加一个字节,而是按照所指对象的大小比例进行,增加sizeOf()个字节。这也是void *不支持指针算术的原因,因为不知道指向对象的类型。
//一维数组
int ary[5] = {1,2,3,4,5};
int *ptr=(int*)(&ary+1); //&ary+1的单位是int(*)[5]
printf("%d\n%d\n",*(ary+1),*(ptr-1)); //结果 2 5
printf("sizeof(ary)=%d\n",sizeof(ary)); //结果 20
printf("sizeof(&ary)%d\n",sizeof(&ary)); //结果 4(32位系统)
/**
ary是数组首地址ary[0]的地址,&ary是对象(数组)首地址;
ary+1是数组下一个元素的地址,即ary[1];而&ary+1是下一个对象的地址,即ary[5]。
sizeof(ary) 是 数组的空间大小,即 5 * 4 = 20;
sizeof(&ary),&ary是一个指向int型数据的指针
**/
//多维数组
int a[3][4];
int (*p)[4] = a; //p指向int[4]数组,所指对象大小是sizeof(int)*4,而不是一个int。
//a+i,a[i],*(a+i),&a[i][0]是等同的,都是第i个一维数组的首地址。
//&a[i]和a[i]也是等同的,二维数组不存在元素a[i]
//*(p+i)是一维数组a[i][0]的地址; *(p+2)+3表示a[2][3]地址,*(*(p+2)+3)表示a[2][3]的值。
同类型的指针可以做减法运算,结果为两个指针之间的元素个数(而不是它们之间的字节数)
三、数组指针和指针数组
数组指针:是一个指针,指向一个数组
指针数组:是一个数组,数组元素是指针
int *p1[10]; //指针数组。[]的优先级比*高,p1[]构成一个数组,int*是数组元素。p1是数组的名字,指向数组第一个元素。
int (*p2)[10]; //数组指针。()优先级比[]高,*p2定义一个指针,int是数组元素。p2指向一个匿名的int[10]数组。
两者访问二维数组:
//数组指针
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int (*p)[4];
p = (int(*)[4])a;
for(int i=0; i<3; i++)
{
for(int j=0;j<4;j++) printf("%d",p[i][j]); //或者 *(*(p+i)+j) 或者 *(p[i]+j)
printf("\n");
}
//指针数组
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int *p[3];
for(int ix = 0; ix<3; ix++) p[ix] = a[ix];
for(int i=0; i<3; i++)
{
for(int j=0;j<4;j++) printf("%d",p[i][j]); //或者 *(*(p+i)+j) 或者 *(p[i]+j)
printf("\n");
}
四、常量指针和指针常量
常量指针:指针是常量
指针常量:指向常量的指针
T *pt = new T; //一个指向T的指针
const T *pct = pt; //一个指向const T的指针。等同于 T const* pct.
T *const cpt = pt; //一个const指针,指向T
const T *const cpct1 = pt; //一个指向常量的常量指针。等同T const *const cpct1
一个指向非常量的指针可以转换为一个指向常量的指针。但相反的转换是非法的。
const T acT;
pct = &acT;
pt = pct; //错误!
*pt = aT; //视图修改常量对象!
pt = const_cast<T *>(pct); //没有错,但这种做法不妥
*pt = aT; //视图修改常量对象!
五、函数指针
int *f1(); //一个返回值为int *的函数
void (*fp)(int); //fp是一个指向返回值为void, 参数为int的函数的指针
extern void h(int);
fp = h; //OK
fp = &h; //OK, &可有可无,通常省略
(*fp)(12); //显示解引用
fp(12); //隐式解引用,结果相同