许多c语言的初学者常常会把指针和数组分不清楚,弄不懂指针和数组之间有什么关系。其实指针和数组并没有任何关系。如果硬要说有关系,那只能说我们可以用下标和数组两种不同的方式访问同一块内存空间,下表的方式访问类似于数组的形式,因此有些人可能会混淆,但这并不代表指针和数组是相同的。
例如:我们可以在一个函数中有如下定义:
(1) char *p ="hello";
(2) char arr[]="world";<span style="font-size:18px;">
</span><span style="font-size:18px;"></span>
在例子(1)中,p是一个指针变量,p本身只占4个字节,p里面存储的是是一块内存的首地址。
在例子(2)中,arr是一个存有7个char类型元素的数组,数组arr的空间大小为7。
但对于例(1)和例(2)我们都可以以指针形式: *(p+2), *(arr+2)或以数组的形式访问如: p[2] ,arr[2].
从上边的例子可以看出:
指针其实就是某一块内存空间的地址。并且在32位系统下,指针永远只占四个字节。
数组可以看作是一个有序数据的集合,它的大小和类型与它所存储数据的类型和个数有关,数组名的值是一个指针常量,存放的是数组中的一个元素的地址。
数组有确定数量的元素,而指针只是一个标量值。
我们可以用一个简单的例子来证明指针和数组是不同的:
例如:我们在文件1中定义
char arr[]="abcdef";
当我们文件2中声明为:
extern char *arr;
编译器自然而然地认为arr是一个指针变量,在32位系统下占4个字节,而这四个字节中存的是地址,这个地址中存的值是字符型数据。比如在这里数组arr存的是abcdef的ASCLL码0x41、0x42、0x43、0x44、0x45、0x46,但在这里会把数组中存储的值当作地址来处理,即取出地址0x41424344中的值,很显然,这不是我们所期望的值。
在文件1中定义
char *p="abcdef";
而在文件2中声明为:
extern char p[10];
在文件1中,编译器将给p分配4个字节的空间,里边存放字符串“abcdef”的首字符的地址假设为0x0018ff44,而在文件2中,编译器又把p当成一个数组,其大小为四个字节,数组内保存的是char类型的数据。所以编译器会认为数组arr中存的是0x00、0x18、0xff、0x44。很显然这也是错误的。
因此这样的定义与声明都是不正确的,因为指针和数组完完全全不是同一种东西。
说了这么多,大家应该对指针和数组有了一个正确的认识了。下面我们再看一些比较绕的东西。
一、指针数组与数组指针。
顾名思义,
指针数组:它是一个“存储指针的数组”,但它本身是一个数组,数组中所存的每一个元素都是一个指针。
我们可以定义一个指针数组为 int * p1[5]; p1是一个数组,数组中存放的是5个int* 型的指针。
数组指针:就是“指向数组的指针”,但它本是是一个指针,在32位系统下永远只占4个字节,它指向一个数组,但它所指向的数组占多少字节我们并不知道。
我们可以定义一个数组指针为 int (* p2)[5]; p2是一个指针,它存储每个元素都是一个指向存放了5个int型元素的数组的首元素地址的指针。
我们可以借助下面的图来理解指针数组和数组指针:
二、函数指针,函数指针数组和函数指针数组指针
看到这个标题估计大家都看得晕乎乎的吧!其实不用怕,我们将在下面详细的分析这三者的区别。
1、函数指针:它是一个指针,指向一个函数。
看看下面两个表达式:
int *fun1(int p1,int p2);
int (*fun2)(int q1,int q2);
两个表达式长得非常像,但是意义却相差很大,第一个表达式表示的是一个返回值为 int * ,参数分别为int, int 的函数,函数名为fun1;而第二个表达式则是一个函数指针,表示一个指针变量fun2,fun2所指向的是一个返回值为int型,有两个参数分别为int,int型的函数。
这里需要注意:函数指针它还是一个指针,所以和其他指针一样,在使用时必须把它初始化为某个函数。
下面来看一段代码让我们来熟悉一下函数指针的使用:
#include<stdio.h>
int add(int a, int b)
{
return a + b;
}
int main()
{
int tmp = 0;
int(*p)(int, int) = &add;//也可以写成int(*p)(int, int) = add;
tmp = add(2, 3);
printf("%d\n", tmp);
tmp = (*p)(2, 3);
printf("%d\n", tmp);
tmp = p(2, 3);
printf("%d\n", tmp);
return 0;
}
<img src="https://img-blog.youkuaiyun.com/20160429004825361?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
毫无疑问,这三种方式输出的结果将是相同的。也就是说我们可以直接用函数名访问函数也可以对函数指针进行间接访问(这种间接访问的操作是非必须的如上面代码的第三种访问形式)的操作来访问一个函数。
说了这么多很多人一定会有疑问,学习函数指针有什么用处呢?函数指针最常用的用途就是转换表和回调函数(把一个函数指针作为参数传递给其他函数)由于篇幅问题我们在这里不做过多解释,后面的文章会专门介绍。
2、函数指针数组:用来存放函数指针的数组(即数组中每一个元素都是指向函数的指针)
我们只需稍微的改造下函数指针的定义便可以得到函数指针数组的定义:
int (*fun[5])(int p);
这里也可以给出一个简单的例子来学习函数指针数组的使用方法:
#include<stdio.h>
void fun1(int num)
{
printf(" fun1: %d\n",num);
}
void fun2(int num)
{
printf(" fun2: %d\n",num);
}
void fun3(int num)
{
printf(" fun3: %d\n",num);
}
int main()
{
int(*pfun[3])(int);
pfun[0] = fun1;
pfun[1] = fun2;
pfun[2] = fun3;
pfun[0](6);
pfun[1](6);
pfun[2](6);
system("pause");
return 0;
}
<img src="https://img-blog.youkuaiyun.com/20160429004834283?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
3、函数指针数组指针:首先它是一个指针,指向一个数组,该数组中存放的是函数指针。
函数指针数组指针的定义如下:
int(*(*p)[5])(int);
我们还是举一个简单的例子来说明函数指针数组指针的使用方法:
#include<stdio.h>
void fun1(int num)
{
printf(" fun1: %d\n",num);
}
void fun2(int num)
{
printf(" fun2: %d\n",num);
}
void fun3(int num)
{
printf(" fun3: %d\n",num);
}
int main()
{
int(*pfun[3])(int);
int(*(*ppfun)[3])(int) = &pfun;
pfun[0] = fun1;
pfun[1] = fun2;
pfun[2] = fun3;
ppfun[0][0](6);
ppfun[0][1](6);
ppfun[0][2](6);
system("pause");
return 0;
}
有关指针和数组的内容先写这么多,如果有什么写的不对或不完整的地方还希望大家指正。