【c语言】带你真正走进指针的世界——函数指针

2019年1月1日,趁着大家都在过年,偷偷学习!

顺便预祝下新年快乐~

————————————————下面是正文——————————————————

一. 定义  

       函数数组的声明定义为: 类型名 (* 指针变量名) (函数参数表列)

       首先说一下函数,当我们定义了一个函数之后,函数的源代码就会被存到内存里面去,然而代码是不可能直接就往内存里面塞的,所以源代码存到内存里面会变成数据,而编译器每次要用到函数时就会把数据提取出来,翻译成代码再执行。而每块内存都是由自身独特的地址的,所以说,函数名的用意其实就和数组名的用意很像,都是指向了函数(数组)的首地址。用官方的话来说,就是:函数名就是函数的指针,代表了函数的其实地址。 

二. 作用

    (1)初级作用

             当然,函数指针存在就一定会有它自身的用处,所以现在先来看看函数指针的初级应用———调用函数    

             请看下面代码!

#include <stdio.h>

int fun(int x,int y)
{
	return x+y;
}

int main()
{
	int (*p)(int,int); //定义一个函数指针 *p

    p = fun; //把函数名为 fun 的首地址赋值给指针

    int x = p(2,3); //直接用指针来代替函数名进行赋值

	printf("%d\n",x);

	return 0;
}

              编译结果如下

       编译结果简单明了,清晰易懂,就是输出结果 5 。

       所以说函数指针的最基本的用法可以通过指针变量来调用它所指向的函数。em......不过呢,这样做的意义何在呢??看起来好像意义不大的样子......愚蠢!用处可大了!看完中级应用再说!

     (2)中级作用

        身为指针家族的一员,函数指针有其特定的用法,一个最主要的用法是:对于一个程序,如果单纯用函数名来调用函数,就只能调用一个指定的函数,所以如果通过函数指针来调用函数的话,就更加地灵活了。简单来说就是如果直接用函数名的话只能调用特定的函数,但是如果我用函数指针的话就可以随便乱指,然后胡作非为啦哈哈哈哈哈哈哈哈哈哈哈哈~~~

       然后请看下面的例子

#include <stdio.h>

int fun(int x,int y)
{
	return x+y;
}

int fun1(int x,int y)
{
	return x-y;
}
int main()
{
	int (*p)(int,int);
	int n,a,b;

	printf("来来来,你是想加还是减?加请按“1”,减请按“2”!\n");
	scanf("%d",&n);
	printf("那么你是想要加(减)哪两个数呢?\n");
	scanf("%d,%d",&a,&b);

	if(n == 1) p = fun;
	else if(n == 2) p = fun1; 

	int x = p(a,b);

	printf("%d\n",x);

	return 0;
}

        编译结果如下

       从上面代码可以看到,我们可以用手动输入的方法进行分支选择,选择我们想要输出的结果,当然,分支选择也可以用其他语句例如 if 或者 switch 语句来代替,不过用函数指针来进行选择分支更加清晰。然后,函数指针也可以当成是一个函数的形参进行传递,但函数指针作为形参写进函数时,那么在该函数里面就可以调用函数指针指向的函数啦~~

       下面请看例子

#include <stdio.h>

int fun(int x,int y,int(*p)(int,int))
{
	int answer = (*p)(x,y);
	return answer;
}

int add(int x,int y)
{
	return x+y;
}

int sub(int x,int y)
{
	return x-y;
}

int main()
{
	int n,a,b,x;

	printf("来来来,你是想加还是减?加请按“1”,减请按“2”!\n");
	scanf("%d",&n);
	printf("那么你是想要加(减)哪两个数呢?\n");
	scanf("%d,%d",&a,&b);

	if(n == 1) x=fun(a,b,add);
	else if(n == 2) x=fun(a,b,sub);
	
	printf("%d\n",x);

	return 0;
}

        编译的结果如下

      由上面代码可以得知,当函数指针作为形参写在函数形参时,该函数时可以直接调用函数指针所指向的函数的!

  (3)高级作用

        em......说起高级应用,首先要提前知道一丢丢小知识。编译器在运行程序时,会把程序的代码按照分类保存到内存的各种分区里面,比如数组的话就会把元素放到 数据区,常量或者全局变量这些会在静态储存区,代码就在代码区等,扩展结束。

       有了上面的小知识之后,我们来写一个超级简单的函数,如下

#include <stdio.h>

int fun(int x,int y)
{
    return x+y;
}

void main()
{
    int (*p)(int,int);
    p=fun;

    int x = p(2,3);

    printf("%d\n",x);
}

         编译结果如下

        然后,我们看下函数 fun 的硬编码

         之后,我们用个数组把函数 fun 的硬编码存起来

#include <stdio.h>

unsigned char arr[]={
0x55,
0x8B, 0xEC,                
0x83, 0xEC, 0x40,              
0x53,                   
0x56,                  
0x57,                   
0x8D, 0x7D, 0xC0,             
0xB9, 0x10, 0x00, 0x00, 0x00,      
0xB8, 0xCC, 0xCC, 0xCC, 0xCC,      
0xF3, 0xAB,              
0x8B, 0x45, 0x08,            
0x03, 0x45, 0x0C,
0x5F,
0x5E,   
0x5B,                
0x8B, 0xE5,             
0x5D,                 
0xC3
}

int fun(int x,int y)
{
    return x+y;
}

void main()
{
    int (*p)(int,int);
    p=fun;

    int x = p(2,3);

    printf("%d\n",x);
}

          重点来了!!!不要闭眼睛了!!下面我把函数 fun 删除,然后在函数指针的赋值做一些修改!!!

#include <stdio.h>

unsigned char arr[]={
0x55,
0x8B, 0xEC,                
0x83, 0xEC, 0x40,              
0x53,                   
0x56,                  
0x57,                   
0x8D, 0x7D, 0xC0,             
0xB9, 0x10, 0x00, 0x00, 0x00,      
0xB8, 0xCC, 0xCC, 0xCC, 0xCC,      
0xF3, 0xAB,              
0x8B, 0x45, 0x08,            
0x03, 0x45, 0x0C,
0x5F,
0x5E,   
0x5B,                
0x8B, 0xE5,             
0x5D,                 
0xC3
}

void main()
{
    int (*p)(int,int);
    p=(int(*)(int,int))&arr;

    int x = p(2,3);

    printf("%d\n",x);
}

          然后你们就可以看到,编译的结果如下

        没错!功能是和函数 fun 的功能是一样的!

       这是为什么呢??这里涉及到了汇编和破解和硬编码的知识,如果我们直接把代码写出来的话,编译器会把代码放到代码区,这是如果有人破解你的程序的话就可以直接从代码区入手然后一步一步逆向破解,然而,但我用函数指针来写代码,再把重要代码以数组元素的形式存到数据区,需要用到时调用数组,没有用到时将数组清零,这是也就大大地提高了防破解能力,让比人找不到你的主要代码!!

       至于汇编和硬编码部分......有缘再聊~ 

猜你喜欢

转载自blog.csdn.net/qq_41884002/article/details/85523323
今日推荐