问答题 函数指针与指针函数有什么区别
【正确答案】
【答案解析】指针函数是指带指针的函数,本质上是一个函数,函数返回类型是某一类型的指针。其形式一般如下:
类型标识符*函数名(参数列表)
例如,int*f(x,y),它的意思是声明一个函数f(x,y),该函数返回类型为int型指针。
而函数指针是指向函数的指针变量,即本质是一个指针变量,表示的是一个指针,它指向的是一个函数。其形式一般如下:
类型说明符(*函数名)(参数)
例如,int(*pf)(int x),它的意思就是声明一个函数指针,而pf=func则是将func函数的首地址赋值给指针。
下面为一个函数指针的实例。
#include<stdio.h>
#define NULL 0
#define ASGN 1
#define MUL 2
int asgn(int* a,int b)
{
return*a=b;
}
int mul(int* a,int b)
{
return *a*b;
}
int(*func(int op))(int*,int)
{
switch(op)
{
case ASGN;
return &asgn;
case MUL;
return &mul;
default:
return NULL;
}
return NULL;
}
int main()
{
int i=0xFEED,j=0xBEEF;
printf("%x、n",func(ASGN)(&i,j));
printf("%x/n",func(MUL)(&i,j));
printf("%x,%x/n,i,j);
return 0;
}
程序输出结果:
beef
8e67a321
beef,beef
引申:数组指针/指针数组、函数模板/模板函数、类模板,模板类、指针常量,常量指针分别有什么区别?
(1)数组指针/指针数组
数组指针就是指向数组的指针,它表示的是一个指针,它指向的是一个数组,它的重点是指针。例如,int(*pa)[8]声明了一个指针,该指针指向了一个有8个int型元素的数组。
#include<stdio.h>
int main()
{
int(*p)[4];
int a[3][4]={{1,2,3,4},{5,6,7,8),{9,10,11,12});
p=&a[0];
for(int i=0;i<12;i++)
printf("%d",(*p)[i]);
printf("/n");
return 0:
}
程序输出结果:
1 2 3 4 5 6 7 8 9 10 11 12
指针数组就是指针的数组,表示的是一个数组,它包含的元素是指针,它的重点是数组。例如,int* ap[8]声明了一个数组,该数组的每一个元素都是int型的指针。
#include<stdio.h>
int main()
{
int*p [4];
int a[4]={1,2,3,4};
p[0]=&a[0];
p[1]=&a[1];
p[2]=&a[2];
p[3]=&a[3];
for(int i=0;i<4;i++)
printf("%d",*p[i]);
printf("/n");
return 0;
}
程序输出结果:
1 2 3 4
(2)函数模板/模板函数
函数模板是对一批模样相同的函数的说明描述,它不是某一个具体的函数;而模板函数则是将函数模板内的“数据类型参数”具体化后得到的重载函数(就是由模板而来的函数)。简单地说,函数模板是抽象的,而模板函数则是具体的。
函数模板减少了程序员输入代码的工作量,是C++中功能最强的特性之一,是提高软件代码重用性的重要手段之一。函数模板的形式一般如下:
template<模板类型形参表>
<返回值类型><函数名>(模板函数形参表)
{
∥函数体
}
其中<模板函数形参表>的类型可以是任何类型,包括基本数据类型和类类型。需要注意的是,函数模板并不是一个实实在在的函数,它是一组函数的描述,并不能直接执行,需要实例化为模板函数后才能执行,而一旦数据类型形参实例化以后,就会产生一个实实在在的模板函数了。
(3)类模板/模板类
类模板与函数模板类似,将数据类型定义为参数,描述了代码类似的部分类的集合,具体化为模板类后,可以用于生成具体的对象。
template<类型参数表>
class<类名>
{
∥类说明体
};
template<类型形参表>
<返回类型><类名><类型名表>::<成员函数1>(形参表)
{
∥成员函数定义体
}
其中<类型形参表>与函数模板中的<类型形参表>意义类似,而类模板本身不是一个真实的类,只是对类的一种描述,必须用类型参数将其实例化为模板类后,才能用来生成具体的对象。简而言之,类是对象的抽象,而类模板就是类的抽象。
具体而言,C++中引入模板类主要有以下5个方面的好处:
1)可用来创建动态增长和减小的数据结构。
2)它是类型无关的,因此具有很高的可复用性。
3)它在编译时而不是运行时检查数据类型,保证了类型安全。
4)它是与平台无关的,可移植性强。
5)可用于基本数据类型。
(4)指针常量/常量指针
指针常量是指定义的指针只能在定义的时候初始化,之后不能改变其值。其格式为
[数据类型][*][const][指针常量名称]
例如:char* const p1;int* constp2;
const位于指针声明符"*"的右侧,这说明声明的对象是一个常量,而对象的数据类型是指针。所以第一句定义了一个只读的字符型指针p1;第二句定义了一个只读的整型指针p2。常指针的值不能改变,但是其指向的内容却可以改变。
#include<stdio.h>
int main()
{
char a[5]="abed";
char b[5]="efgh";
char* const p1=a;
char* const p2=b;
printf("Before Change:/n");
printf("a:%s/nb:%s/n",a,b);
*p1="1";
b[0]="2";
∥p1=p2;
printf("After Change:/n");
printf("a:%s/nb:%s/n",a,b);
return 0;
}
程序的输出结果如下:
Before Change:
a:abcd
b:efgh
After Change:
a:1bcd
b:2fgh
上例中,如果去掉注释行,执行p1=p2操作,则编译会出错:error C3892:"p1":不能给常量赋值(VS 2005)。指针所指向的内存地址不能更改,指针的值只能在定义的时候初始化,其他地方不能更改。
常量指针是指向常量的指针,因为常量指针指向的对象是常量,因此这个对象的值是不能够改变的。定义的格式如下:
[数据类型][const][*][常量指针名称];或[const][数据类型][*][常量指针名称];
例如:int const *p;const int *p;
程序示例如下:
#include<stdio.h>
int main()
{
char a[5]="abcd";
char b[5]="efgh";
const char * p1=a;
const char * p2=b;
printf("Before Change:/n");
printf("a:%s/nb:%s/np1:%s/n",a,b,p1);
a[0]="1";
p1=p2;
∥*p2="2";
printf("After Change:/n");
printf("a:%s/nb:%s/np1:%s/n",a,b,p1);
return 0;
}
程序的输出结果:
Before Change:
a:abcd
b:efgh
p1:abcd
After Changed:
a:1bcd
b:efgh
p1:efgh
上例中,如果去掉注释行,执行*p2="2"操作,则编译会出错:error C3892:"p2":不能给常量赋值。
需要注意的是,指针常量强调的是指针的不可改变性,而常量指针强调的是指针对其所指对象的不可改变性,它所指向的对象的值是不能通过常量指针来改变的。对于字符串“abc”,可以这样获取其地址:&("abc");