1、什么是指针数组
一个数组,如果其元素都是指针类型的数据,那么这个数组就称为指针数组。指针数组的定义如下:
int *p[5];
从右往左读,先是p[5],表示是一个含5个元素的数组;然后是int *,表示这5个元素的数据类型是int *。
有几个概念我们要区分下:
(1)指针数组:本质是一个数组,该数组中的每个元素都是一个指针。
(2)数组指针:本质是一个指针,指向了一个数组。
比如,int (*p)[5],这种写法表示一个指向含5个元素的数组的指针。
从右往左读,先是(*p)[5],表示指针p指向了这个数组;int表示这个数组里的元素是int型。
(3)数组名称:数组名称表示数组元素首地址,是个指针常量。
比如,int arr[5],则arr表示数组元素首地址,arr + 1表示第2个元素的地址。
下面我们看1个指针数组的例子:
- const char* argv[] = {"a1", "b2", "c3"};
-
- for (int i = 0; i < 3; i++) {
- printf(" %s\n", argv[i]);
- }
数组argv[],其中的数据元素是指向字符串常量:a1, b2, c3的指针。这里要特别注意:char *p = "abc"; 是把字符串常量"abc"的首地址赋给了字符变量p。所以argv[]里保存的是字符串常量的指针,而不是字符串自身的值。
2、二重指针
二重指针,也就是指针的指针,实际上就是存放指针的地址。
用代码演示就是这样的:
- int a = 10;
- int *p = &a;
- int **pp = &p;
二重指针的含义:
表达式 | 相当的表达式 |
pp | &p 即指针p的地址 |
*pp | p, &a 即指针p,也就是对象a的地址 |
**pp | *p, a 即对象a |
二重指针在函数传指针参数时非常有用。因为函数的参数传的是指针的拷贝,而不是指针只身。所以当需要在
函数内部修改原指针时,就需要传二重指针。
例子:
- void swapPointer(int** p1, int** p2) {
- int* tmp;
- tmp = *p1;
- *p1 = *p2;
- *p2 = tmp;
- }
-
- int main()
- {
- int a1 = 10;
- int a2 = 15;
- int* p1 = &a1;
- int* p2 = &a2;
- printf(" p1 = %p, *p1 = %d\n", p1, *p1);
- printf(" p2 = %p, *p2 = %d\n", p2, *p2);
- swapPointer(&p1, &p2);
-
- printf(" p1 = %p, *p1 = %d\n", p1, *p1);
- printf(" p2 = %p, *p2 = %d\n", p2, *p2);
-
- return 0;
- }
其实,前面例子的:
const char* argv[] = {"a1", "b2", "c3"};
可以把argv看成是一个二重指针。这个指针数组的存储结构可以用下图来表示:
数组的首地址是argv,argv指向的值是argv[0],argv[0]的值是指向字符串常量"a1"的指针。
用代码演示如下:
- const char* argv[] = { "a1", "b2", "c3" };
- const char** pp = argv;
-
- for (int i = 0; i < 3; i++) {
- printf(" %s\n", *(pp + i));
- }