浅析C++中的指针

注:本文中的int型变量为32位,占四个字节。

本篇内容旨在对指针数组、数组指针、指针函数、函数指针进行总结,也希望能在总结的同时能有更深入的理解。

数组指针

数组指针顾名思义是个指针,定义形如(*arr)[n]的一个数组指针,由于()的优先级最高,所以定义的是一个指针,这个指针指向含有n个元素的数组。

可以设计一个程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>

int main(int argc,int* argv[])
{
int a[3][4] = {1,2,3,4,
5,6,7,8,
9,10,11,12
};
int (*arr)[4] = a;
printf("arr = %p\n",arr);
printf("a[0][0] = %p\n",&a[0][0]);
printf("ADDR(a[0][1]) = %p\n",&a[0][1]);
printf("arr[1] = %p\n",++arr);
printf("a[1] = %p\n",a[1]);
return 0;
}

运行程序后输出结果如下:

1
2
3
4
5
arr = 0x7ffefffb8810
a[0][0] = 0x7ffefffb8810
ADDR(a[0][1]) = 0x7ffefffb8814
arr[1] = 0x7ffefffb8820
a[1] = 0x7ffefffb8820

可以看到,初始化了arr后,其值与数组a[][]的首地址相同,而对arr的自增操作则使其值跳跃了整个数组的长度,该操作等同于二维数组a[][]的行地址加1,而进行如果下标操作a[0][1],该元素的地址相对于a[0][0]只是增加了四个字节,即一个int型变量的长度。

我们可以很清晰的从下图理解数组指针:
数组指针

指针数组

指针数组的概念就相对简单,指针数组是个数组,只不过内部存的元素都是指针类型,由于[]的优先级要高于*,所以该数组的定义形如*arr[n]

例子如下:

1
2
3
4
5
6
7
8
9
#include<stdio.h>

int main(int argc,int* argv[])
{
int a = 5,b = 10;
int *arr[] = {&a,&b};
printf("a = %d,b = %d\n",*(arr[0]),*(arr[1]));
return 0;
}

程序输出结果如下:

1
a = 5,b = 10

数组指针没有什么难度,只是用数组去存指针而已。

函数指针

函数指针其实在编程中相当有用,我们可以将函数的形参声明为函数指针,从而可以将函数的地址传入。

函数作为参数传递跟变量传递一样有两种方式:值传递,地址传递。但是值传递有一个不好之处在于太占用空间,假设在A函数中声明一个形如B函数的形参,而非形如B函数的函数指针,那么将B函数作为参数传入时,就要分配整个B函数的空间给该参数,而函数指针则只会传入一个地址。那么还有一个问题,在函数内部进行函数调用也是一样,而且不会进行值传递,但是在函数内部进行调用,不够灵活,同一类函数可以实现的功能不一样,形如B函数的函数功能和函数名可能有所不同,而每次调用不同的函数时都要去改变A函数未免太不方便,所以才要把函数地址当作参数进行传入。

设计一个程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>
int cmp_min(int x,int y)
{
return x < y?x:y;
}
int cmp_max(int x,int y)
{
return x > y?x:y;
}
int cmp(int x,int y,int (*compare)(int,int))
{
return (*compare)(x,y);
}
int main(int argc,int* argv[])
{
int a = 5,b = 10;
printf("the min of a,b is %d\n",cmp(a,b,cmp_min));
return 0;
}

输入结果如下:

1
the min of a,b is 5

值得一提的是,函数指针是一个指针,我们不能在声明函数指针的同时进行函数的定义,因为我们只是声明一个指针,我们只能声明一个函数指针,然后用一个定义过的函数去对该指针进行初始化,就像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>

int cmp_min(int x,int y)
{
return x < y?x:y;
}
int main(int argc,int* argv[])
{
int a = 5,b = 6;
int (*compare)(int,int) = cmp_min;
printf("the min of a,b is %d\n",(*compare)(a,b));
return 0;
}

输出结果跟上面一样。

其实函数指针可以和指针数组结合起来使用,我们都知道函数名单独使用其实是一个地址,所以可以将其作为指针存于指针数组中,通过数组对其进行访问。

例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<stdio.h>
int cmp_min(int x,int y)
{
return x < y?x:y;
}
int cmp_max(int x,int y)
{
return x > y?x:y;
}
int cmp(int x,int y,int (**compare)(int,int))
{
printf("the min of a,b is %d\n",(*compare[0])(x,y));
printf("the max of a,b is %d\n",(*compare[1])(x,y));
}
int main(int argc,int* argv[])
{
int a = 5,b = 10;
int (*arr[])(int,int) = {cmp_min,cmp_max};
cmp(a,b,arr);
return 0;
}

输出结果如下:

1
2
the min of a,b is 5
the max of a,b is 10

cmp(int x,int y,int (**compare)(int,int))中声明的函数形参使用了指针的指针,因为我们传入的是是数组的地址,需要先解引用获得数组内的函数地址,所以使用了指针的指针,也可以使用另一种形式:*compare[](int,int),效果等同;如果我们在传参时使用了数组下标,则可以不用如此声明,直接声明为cmp(int x,int y,int (*compare)(int,int))即可。

指针函数

指针函数是个函数,只不过返回值是个指针类型,这也是一个比较简单的概念,就像变量返回一样,指针函数的定义形如*func()