一:数组指针和指针数组的理解:
1.首先我们从一个例子开始讲起
include<stdio.h>
int main()
{
int a[5][5]={0};//创建一个二维整型数组,并初始每个元素化为0;
int(*p)[4]=NULL;//定义一个数组指针,这个指针指向大小为四,并且每个元素为int型的指针,将此指针初始化为空;
p=(int(*)[4])a;//二维数组的数组名a,代表一个大小为5,每个指针指向5个元素的指针数组;而此时的p为一个可以指向
//四个元素的指针,所以此时要给a强制类型转换为指向四个int类型元素的数组指针,再付给p;
printf("%p,%d",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);
return 0;
}
结果为
详细分析:
对于最后一行的分析:
printf("%p,%d",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);
(1)指针减指针输出的是元素个数,又因为p[4][2]的地址小于a[4][2]的地址;
所以如果以%d的格式输出来直接就是-4;
(2)printf函数族中对于%p一般以十六进制整数方式输出指针的值,附加前缀0x。
所以把-4用十六进制输出,数字在内存中以补码的方式存在;
-4的32位二进制
原码为:10000000 00000000 00000000 00000100
反码(符号位不变,其余安位取反):11111111 11111111 11111111 11111011
补码(反码加1):11111111 11111111 11111111 11111100
将此补码转换成十六进制为:fffffffc
所以最后输出的是fffffffc
(3)总结此题:本题考察的是指针的部署, int(*p)[4]=NULL,像这样的指针加1,则跳过了一个大小为四,每个元素为整型的数组;
2.再看一个例子:
#include<stdio.h>
int main()
{
int aa[2][5]={1,2,3,4,5,6,7,8,9,10};
int *prt1=(int *)(&aa+1);
int *prt2=(int *)(*(aa+1));
printf("%d,%d",*(prt1-1),*(prt2-1));
return 0;
}
(1)结果为:
(2)分析:
3.其实在指针这块还是很有趣的,
例如给出这样一个例子
#include<stdio.h>
int main()
{
int a[]={1,2,3,4,5,6};
printf("%d",a[2]);
printf("%d",2[a]);
}
(1)程序输出的结果为:
(2)分析:
因为a[2]相当于*(a+2)
然而2[a]的意义是*(2+a)
因为a代表首元素的地址,为整型,2也为整型,所以a和2顺序可以互换,不影响结果;
4.总结:
(1)数组指针:强调的是指针,它是一个指向数组的指针;int (*arr)[3];
(2)指针数组:强调的是数组,数组里面保存的是指针,数组的每个元素都是指针类型;int* arr[3];
二:二级指针
1.概念:二级指针用来存放一级指针的地址。
2.格式:char**p
3.二级指针的内存分布:
三:数组参数和指针参数:
1.一维数组参数:
#include<stdio.h>
void fun(char arr[])//此时,可以不写形参数组的大小,因为此时只是定义了一个数组名字,并没有给这个数组开辟内存空间。
//因为,实参传过去的并不是整个数组,而是数组首元素的地址,所以不能用sizeof在函数内部求取
//整个数组的大小;求取的是指针的大小,为四个字节。所以,形参可以写成一个指针 void fun(char* arr),
{
char c=arr[4];
}
int main()
{
char arr[]="abcde";
fun(arr);//函数调用传递整个数组时,实参只需要写数组名即可,不可给数组名之后加下标;加了下标表示数组的某
//个元素;此时其实并没有传递整个数组,因为数组名代表数组首元素的地址。
return 0;
}
2.一级指针参数:
(1)简单举例:
#include<stdio.h>
void fun (char*p)
{
char c=p[3];//等价于char c=*(p+3)
}
int main()
{
char* p ="abcdef" //此时p的空间中保存地是字符a的地址;
fun(p) ; //传递a的地址;
return 0;
}。
3.二维数组参数和二级指针参数:
(1)首先从一例子开始分析:
include<stdio.h>
//viod fun(int **arr)。错误:传参时。不能用二级指针接受二维数组。
//viod fun(int arr[][])。错误:如果去掉后一个方括号中的数字3,则不能确定传进来的数组指针指向的数组的大小。
//viod fun(int arr[][3]) 正确
//viod fun(int (* arr)[3]) 正确
viod fun(int arr[2][3])
{
;
}
int main()
{
int arr[2][3];
fun(arr); //数组名在传参的时候发生降级,表示数组首元素的地址;
// 此时,这个二维数组的名表示一个数组指针,指向一个大小为三,每个元素类型为int的指针类型;
//也就是这样int (*)[3];
return 0;
}
(2)深入理解二级指针做形参的情况:
int fun(char** p)
//int fun(char* arr[])
{
;
}
int main()
{
char*pc;//一级指针
char **ppc;//二级指针
char *arr[3];/指针数组;
fun(&pc);//二级指针就是用来存放以及指针的地址的。
fun(ppc)//二级指针传给二级指针,正确。
fun(arr);//因为此时arr为指针数组,数组的每个元素都是指针类型;而我们一再强调,
//数组名代表数组首元素的地址;从而arr带表它的数组首元素指针的地址;
//接收指针的地址当然要用二级指针去接收。
return 0;
}
(3)总结:
上面讨论的那么多,我们把二维数组和二维指针参数的等效关系总结一下;
a.当数组的参数为数组的数组,即二维数组时,如char arr[2][2];
所对应的指针参数为 数组指针,char(*)[2]
b.当数组的参数为指针数组,例如:char * arr[5];
<span style="font-size: 18px