目录
一、字符指针
二、指针数组
三、数组指针
四、指针笔试题
1. 指向单个字符:
int main()
{
char a = 'a';
char* pa = &a;
printf("%c", *pa);
return 0;
}
2.指向字符串:此时p指向字符数组arr首元素的地址
int main()
{
char arr[] = "abcdef";
char* p = arr;
printf("%c\n",*p);
printf("%s\n", p);
return 0;
}
注意:如果要打印p指向的字符数组的所有元素,在printf传参里传的是p而不是*p。
下面我们来观察这样一段代码:
int main()
{
char arr[] = "abcdef";
char* p = arr;
printf("p:%p\n", p);
printf("&p:%p\n", &p);
printf("arr:%p\n", arr);
printf("&arr:%p\n", &arr);
printf("&arr[0]:%p\n",&arr[0]);
return 0;
}
从结果上来看,p和&p看起来是不一样的,而p、arr、&arr[0]和&arr看起来是一样的。
针对它们的关系,画张图好理解:
①p是一级指针,本质是地址,p自己本身有一个地址(因此可以有二级指针指向它),p内部存着的arr的地址。
②从&arr[0]、&arr和arr的地址打印来看,arr和&arr都是arr[0]的地址。
问:虽然p、arr、&arr地址的值是一样的,它们之间有什么关系呢?
我们用下面这段代码解释:
int main()
{
char arr[] = "abcdef";
char* p = arr;
printf("p+1:%p\n", p + 1);
printf("arr+1:%p\n", arr + 1);
printf("&arr+1:%p\n", &arr + 1);
return 0;
}
从打印结果来看,p和arr跳过1个字节后的地址仍然相等,而&arr似乎跳过不止一个字节,将十六进制换算为十进制,B代表12,和前面的差了7个字节,正好是这个字符数组元素的个数。
究其原因:
①p和arr是一样的,都代表arr数组首元素的地址,
②&arr代表的是整个arr数组的地址,但是值与其首元素地址是一样的,
③p和arr的类型都是char*类型的指针,&arr的类型是char(*)[7]的数组指针(后面会讲)。
顾名思义,就是存放指针的数组。
那指针数组到底有什么用呢?我们来看下面这段代码:
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,7 };
int* arr[3] = { arr1,arr2,arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", *(*(arr + i) + j));
}
printf("\n");
}
return 0;
}
在字符指针的讲解中,我们知道数组名就是数组首元素的地址,我们将arr1、arr2、arr3数组的首元素地址放进arr的指针数组中,将arr模拟成了一个二维数组进行遍历打印。
其中比较难理解的是这句代码:
printf("%d ", *(*(arr + i) + j));
我们印象中的二维数组应该是这样连续存储的:
但其实在内存中arr是这样存储的:
我们先用arr+i解引用得到arr1、arr2和arr3的首元素地址,再用它加上j变量后解印用得到每个元素的值,也就是第一次解印用得到地址,第二次解印用得到值。
其实可以将*(*(arr + i) + j)直接写成arr[i][j],结果是一样的。
区别数组指针和指针数组:一个是指针,一个是数组,一个是指向数组的指针,一个是存放指针的数组。
指针数组:
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,7 };
int* arr[3] = { arr1,arr2,arr3 };
arr数组有3个元素,分别是三个数组名,数组名表示数组首元素的地址,类型是int*。
数组指针:
int arr[5]={1,2,3,4,5};
int (*parr)[5]=&arr;
parr是指向arr数组的指针,类型是int(*)[5]。
问:为什么不能写成这样呢?
int(*parr)[5]=arr;
虽然编译过得去,但是报了一个警告:
原因是arr是数组首元素的地址,类型是int *,而&arr的类型是int(*)[5]。
这就解释了上面为什么arr+1只是跳过1个数组元素,而&arr+1跳过了整个数组的元素。
数组指针的应用:二维数组传参
void print(int(*parr)[5], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("%d ", *(*(parr+i)+j));
}
printf("\n");
}
}
int main()
{
int arr[2][5] = {{ 1,2,3,4,5 }, { 2,3,4,5,6 }};
print(arr,2,5);
return 0;
}
int main()
{
char* c[] = { "ENTER","NEW","POINT","FIRST" };
char** cp[] = { c + 3,c + 2,c + 1,c };
char*** cpp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *-- * ++cpp + 3);
printf("%s\n", *cpp[-2] + 3);
printf("%s\n", cpp[-1][-1] + 1);
return 0;
}
c、cp、cpp之间的关系画图表示:
**++cpp:
*--*++cpp+3:
*cpp[-2]+3,即*(*(cpp-2))+3:
cpp[-1][-1]+1,即*(*(cpp-1)-1)+1: