写在前面:
本篇博客仅限于c语言的入门阶段,博主也是c语言的新人,这篇博客着重讲述数组指针与指针数组,指针函数与函数指针的简单区别,比较适合刚接触的萌新,若有不足或其他错误,可以私信博主来进行修改。
一、指针概念:
计算机中所有的数据都必须放在内存中,不同类型的数据占用的字节数不一样。为了正确地访问这些数据,必须为每个字节都编上号码。每个字节的编号是唯一的,根据编号可以准确地找到某个字节。
几种类型所占字节如下:
char:1字节
int:2字节(16位系统),4位(32、64位系统)
long:4字节(16、32位系统),8字节(64位系统)
float:4字节
double:8字节
long long:8字节
指针:3字节(16位系统),4字节(32位系统),8字节(64位系统)
而内存中字节的编号就被称为地址,也就是指针。
定义指针一般用*a来表示,定义指针后*a表示取值,&a表示取址。
示例如下:
#include
int main()
{
int n;
int *a=&n;
*a=1;
printf("%p,%d\n",&a,*a);
return 0;
}
输出结果:0x16fdff410,1
上述代码中,首先我们定义了一个变量n,n有他自己的地址,a为整形的指针变量。*本身位取值符号,但当我们使用int *a是,其只是单纯的定义指针,并无其他意义。指针只能存储地址,因此上式需要将n的地址赋给*a,在运用取值符号将数字1放在*a的地址中。
二、数组与指针
在我们学习数组时,例如:int a[3],其中a为数组名3为数组中所包含的元素的个数,而在我们在主函数中调用子函数arr(int a[],int n)时,其形式为arr(a,3)。我们不难发现数组与指针在某方面有着异曲同工之妙。而事实上他们之间也有着异常紧密的联系。
1、指针数组:
表示形式:int *a[3]。
解释:指针数组的含义便是指针类型的数组,即数组中存储的为指针。
示例如下:
#include
int main()
{
int a1[3]={1,2,3};
int a2[3]={1,2,3};
int a3[3]={1,2,3};
int *p[3]={a1,a2,a3};
printf("%p\n",*p);
printf("%d\n",**p);
}
输出结果:
0x16fdff408
1
分析:定义一个指针数组,其数组中存放的为地址,即三个数组中的数组名。*p取起地址内的第一个元素(即第一个地址)。**p为指针的指针,接下来为大家详解。
指针的指针
就上述例题而言,由于指针是从右向左编译的,因此我们可以把他看为两部步:*p去第一个元素,即a1的地址,在**p去a1的第一个元素,即1.这就是指针的指针。
计算指针数组所占的字节数
#include
int main()
{
int a1[3]={1,2,3};
int a2[3]={1,2,3};
int a3[3]={1,2,3};
int *p[3]={a1,a2,a3};
printf("%p\n",&p);
printf("%p\n",&p+1);
}
输出结果:
0x16fdff3d0
0x16fdff3e8
上述的输出结果相减即为其所占的字节,经计算为24个字节,这是为什么呢?是因为在64位系统下指针固定占8个字节,因此3*8即为24。
2、数组指针
与上面的指针数组一样,数组指针便是数组类型的指针,它的本质上还是指针。
示例:
#include
int main()
{
int a[3]={1,2,3};
int (*b)[3]=&a;
printf("%p\n",b);
printf("%p\n",b+1);
printf("%p\n",*b);
printf("%p\n",*(b+1));
printf("%d\n",**b);
printf("%d\n",**(b+1));
printf("%d\n",*((*b)+1));
return 0;
}
输出结果:
0x16fdff408
0x16fdff414
0x16fdff408
0x16fdff414
1
0
2
分析:
上述代码中(*b)便是数组a[3]数组名的首地址,而*b则是去b数组中第一个元素,*b中保存的是数组a,因此相当于取数组a的数组名,所以第一、三个printf的结果一至。而因为保存的是数组名,因此当b+1是,它便已经是在数组的整体上+1,而这个整形数组中有3个元素,一个整形为四个字节,因此第一、二,三、四个printf相差为12。
事实上是开辟一个空间b来存储数组a的首地址,在我们使用时要进行两次转化,才能得到原始数组里所包含的数字。因此,数组指针事实上我们使用的并不多,只是有时候需要这种思想。
3、指针函数
指针函数与上面的一样,可以解读为返回值为指针类型的函数。那么返回指针类型有什么好处呢?又一个很关键的一点就是我们可以借助它来返回数组,示例如下:
#include
int *paixu(int a[],int n)
{
int *b;
int i,t;
for(i=0;i<2;i++)
{
if(a[i]>a[i+1])
{
t=a[i];
a[i]=a[i+1];
a[i+1]=t;
}
}
b=a;
return b;
}
int main()
{
int a[3]={2,1,3};
int *b;
int i;
b=paixu(a,3);
for(i=0;i<3;i++)
{
printf("%d ",b[i]);
}
printf("\n");
return 0;
}
上述为一个简单的排序函数,当我们在排完序后需要返回是,便要通过指针函数来返回一个指针,在定义一个指针去接收它。当然,其实有一种更为简单的方法去解决这个问题
#include
void paixu(int a[],int n)
{
int i,t;
for(i=0;i<2;i++)
{
if(a[i]>a[i+1])
{
t=a[i];
a[i]=a[i+1];
a[i+1]=t;
}
}
}
int main()
{
int a[3]={2,1,3};
int i;
paixu(a,3);
for(i=0;i<3;i++)
{
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
将子函数定义为void型,使他在内部完成交换,都可以不需要使用指针,而且代码也会简化不少,当然有时后这种方法也是有局限的,我们还是要掌握指针函数的用法。
4、函数指针
函数指针为指向函数的指针,即子函数的参数可以为函数。示例如下:
#include
int max(int a[],int n)
{
int i,max=a[0];
for(i=0;imax)
{
max=a[i];
}
}
return max;
}
int arr(int a[],int n,int b())
{
int max;
max=b(a,n);
return max;
}
int main()
{
int a[]={1,2,3};
int b=arr(a,3,max);
printf("%d\n",b);
return 0;
}
最终的输出结果为3
上述代码定义了一个子函数arr,其中一个参数为函数指针b(),当然这个函数可以为自己定义的任何人函数,如求最小值min,求和add等等,不单单局限于一个。函数指针主要是为了避免当定义的子函数过多时,需要调用的子函数格式过于多,这时便可以采用函数指针对其进行化简。
总结:
当我们学习指针以后,我们不难发现,指针非常灵活,可以与我们所学的其他的数组、函数等进行紧密的结合,使其更加的灵活与多变,而这也正是为什么指针会被人们称为“c语言的灵魂”。