一、指针函数
1、指针函数:首先它实质是一个函数;只是函数的返回值是一个地址量。
2、通常一个函数都有返回值,如果一个函数没有返回值,则该函数是一个无值函数。
3、指针函数一般形式:
<数据类型> *<函数名称>(<形参说明>)
{
语句序列;
return [(表达式)];
}
其中:
<数据类型> :例如 int(整型),char(字符型) ,float(浮点型),double(双精度)等等;
与返回值的数据类型保持一致。
<函数名称>: 符合标识符命名规则。(由字符,下划线,数字组成,且不能以数字开头)。
<形参说明> :是“ , ”分隔的多个变量的说明形式,通常简称为形参。
在<函数名称>之前的 * 符号,说明该函数返回一个地址常量。
return [(表达式)]:语句中表达式的值要和函数的<数据类型>保持一致,如果函数数据类型为void则可以省略或则无表达式结果返回(即写为:return ;)
4、举例:
注意:由于函数返回值是地址,故传递方式为地址传递,地址传递需要保证地址是有效的,需要没有被回收或释放掉的地址,否则会段错误,非法访问内存。
#include <stdio.h>
char *string() //定义的一个char字符型的指针函数
{
static char ch[32] = {0};
return ch;
}
int main(int argc, char *argv[])
{
printf("%p\n",string());//打印函数返回的地址
return 0;
}
A、上图打印结果可以看出返回值是一个16进制的地址,并且该地址为字符数组ch的首地址。注:该地址被主函数调用并成功打印:表示该地址有效,可以正常访问。
B、如果去掉代码中的 static ,如下:
#include <stdio.h>
char *string()
{
char ch[32] = {0};
return ch;
}
int main(int argc, char *argv[])
{
printf("%p\n",string());
return 0;
}
运行结果:这个时候不仅编译时系统会报警告:返回的地址无效,并且打印结果是空 ,这个时候主函数调用时就会非法访问内存了。
5、因此有以下几种方式在指针函数里面地址传递为有效的情况:
A、static :静态存储类型,可延长变量使用周期,但仅限于本文件内,不可跨文件使用;
#include <stdio.h>
char *string()
{
static char ch[32] = {"hello world"};//初始化数组
return ch;//返回字符数组ch的首地址
}
int main(int argc, char *argv[])
{
printf("%s\n",string());//%s打印字符串
return 0;
}
打印结果:成功打印hello world 字符串。
B、定义全局变量:
#include <stdio.h>
#include <string.h>//strcpy 函数头文件
char ch[32] = {0};//全局变量
char *string()
{
strcpy(ch,"hello world");/向ch中写入字符串
return ch;//返回地址
}
int main(int argc, char *argv[])
{
printf("%s\n",string());
return 0;
}
C、定义内部指针接收:
#include <stdio.h>
int *sum(int a,int b)//求和函数
{
int m =0;
int *p = &m;//定义一个指针保存地址
m = a+b;
return p;
}
char *string()
{
char *ch = "ni hao";//字符串常量不会被立即回收
return ch;
}
int main(int argc, char *argv[])
{
printf("%d\n",*sum(2,6));//sum返回的是p为地址;所以相当于*p
printf("%s\n",string());
return 0;
}
D、malloc开辟的地址空间:需要用户自己释放或者所以程序运行完后系统才释放;(堆区地址)
#include <stdio.h>
#include <stdlib.h>//malloc头文件
#include <string.h>//strcpy头文件
char *string()
{
char ch[64];
char *p = (char *)malloc(sizeof(ch));//为指针p开辟64个字节空间大小
strcpy(p,"helllo world");//写入字符串
return p;
}
int main(int argc, char *argv[])
{
char *s = string();//定义接收指针方便释放地址
printf("%s\n",s);
free(s);//释放地址
s = NULL;//指向NULL,防止野指针
return 0;
}
二、函数指针
1、函数指针:实质上它是一个指针,只不过指针装的是一个函数的地址;函数地址是一个函数的入口地址。
2、当一个函数指针指向了另一个函数,就可以通过该指针来调用该函数,可以将函数作为参数传递给函数指针。
3、函数指针一般形式:
<数据类型> (<*函数指针名称>)(<参数说明列表>)
{
语句序列;
}
其中:
<数据类型>:函数指针所指向的函数的返回值类型保持一致;
<函数指针名称>:符合标识符命名规则;
(<*函数指针名称>):* 符号说明为指针,( )不可缺少,表明为指向函数的指针;
<参数说明列表>:应该与函数指针所指向的函数的形参说明保持一致;
4、举例:
#include <stdio.h>
//函数sum
int sum(int a,int b)
{
return a+b;
}
//函数multiply
int multiply(int a,int b)
{
return a*b;
}
int main(int argc, char *argv[])
{
//打印地址,其中加上取地址&和不加都行
printf("%p\n",sum);//&sum
printf("%p\n",multiply);//&multiply
return 0;
}
A、既然函数指针存放的是函数的地址,首先我们先打印看了一下这两个函数,计算机确实给函数名开辟了一个地址空间作为访问该函数的入口。
B、接下来便使用函数指针调用函数:
#include <stdio.h>
//求a与b的和函数
int sum(int a,int b)
{
return a+b;
}
//求a与b的乘积函数
int multiply(int a,int b)
{
return a*b;
}
//其中int (*pfun)(int ,int)为函数指针
int test(int a,int b,int (*pfun)(int ,int))
{
return (*pfun)(a,b);//返回的是调用函数指针所指向的函数地址的函数
}
int main(int argc, char *argv[])
{
printf("a+b = %d\n",test(1,6,sum));//指针pfun放的sum函数的地址
printf("a*b = %d\n",test(2,3,multiply));//指针pfun放的mutiply函数的地址
return 0;
}
三、函数指针的重命名
用上面的例子简单做简单改写,说明一下;
#include <stdio.h>
typedef int (*mfun)(int ,int);//重命名函数指针
//此时mfun pfun 等价于 int (*pfun)(int ,int);-->mfun装的为变量
int sum(int a,int b)
{
return a+b;
}
int test(int a,int b,mfun pfun)
{
return (*pfun)(a,b);
}
int main(int argc, char *argv[])
{
printf("a+b = %d\n",test(1,6,sum));
return 0;
}
四、总结
总的来说,函数指针和指针函数关键抓住它存放的和返回的是什么;整体看来的话,函数指针要比指针函数用起来简单一点,指针函数虽然看起来简单,但是类型多,容易出错,关于地址传递是否有效建议看虚拟内存图,了解计算机存储数据的方式。作为一个Linux嵌入式初学者,有什么问题,欢迎大家一起交流。