小白初学指针《二》

我们接着从上一篇文章,来看一些其他指针。

函数指针

首先,什么是函数指针?
数据都是有地址的,函数也是数据,自然也有地址。也就可以创建一个变量p用来存储函数的地址,这个变量p就叫做函数指针。

#include <stdio.h>
void test()
{
    
    
 printf("hehe\n");
}
int main()
{
    
    
 printf("%p\n", test);
 printf("%p\n", &test);
 return 0;
}

运行结果相同,这个地址就是函数的地址。
下面我们来定义一个函数指针。
哪一个可以完成定义呢?

void (*pfun1)();
void *pfun2();

pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。

阅读两段有趣的代码:
注 :推荐《C陷阱和缺陷》
这本书中提及这两个代码。

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
代码2太复杂,如何简化:
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);

函数指针数组

数组是存放同类型数据的存储空间,上一章简述了指针数组,那么函数指针数组怎么定义呢?

int (*parr1[10]])();
int *parr2[10]();
int (*)() parr3[10];

是不是很难办?
下面介绍一下我自己的判断方法,只要学会了,再复杂的组合也能准确判断。
根据操作符优先级和结合性判断
1.关于指针:读取名字的时候,根据结合性,第一个结合的类型名放在最后面,依次逆序存放(先结合的放后面),最后一个结合的类型名放在最前面。然后按顺序读,再判断类型。
例子:int (arr[5])[10]
最先结合[5].数组1。放在最后面。
再结合
.指针。放在倒数第二个
最后结合[10].数组2。放在第一个
成为——-数组2指针的数组1——
所以arr是一个存放5个元素的数组1,存放的是元素个数为10的整型数组2的指针。
函数指针数组的应用:转移表。
转移表的应用:计算器。

#include <stdio.h>
int add(int a, int b)
{
    
    
           return a + b;
}
int sub(int a, int b)
{
    
    
           return a - b;
}
int mul(int a, int b)
{
    
    
           return a*b;
}
int div(int a, int b)
{
    
    
           return a / b;
}
int main()
{
    
    
     int x, y;
     int input = 1;
     int ret = 0;
     int(*p[5])(int x, int y) = {
    
     0, add, sub, mul, div }; //转移表
     while (input)
     {
    
    
          printf( "*************************\n" );
          printf( " 1:add           2:sub \n" );
          printf( " 3:mul           4:div \n" );
          printf( "*************************\n" );
          printf( "请选择:" );
      scanf( "%d", &input);
          if ((input <= 4 && input >= 1))
         {
    
    
          printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = (*p[input])(x, y);
         }
          else
               printf( "输入有误\n" );
          printf( "ret = %d\n", ret);
     }
      return 0;
}

指向函数指针数组的指针

什么是指向函数指针数组的指针?
用我上面的方法来理解,首先说他是一个指针,那么他最先和指针结合,再依次和数组,指针,函数结合。再根据名字来理解。
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是 函数指针 。

void test(const char* str)
{
    
    
 printf("%s\n", str);
}
int main()
{
    
    
 //函数指针pfun
 void (*pfun)(const char*) = test;
 //函数指针的数组pfunArr
 void (*pfunArr[5])(const char* str);
 pfunArr[0] = test;
 //指向函数指针数组pfunArr的指针ppfunArr
 void (*(*ppfunArr)[10])(const char*) = &pfunArr;
 return 0;
}

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
说的简单直白一点,就是一个函数里,调用了另外一个函数,通过什么方式调用的呢?通过指针。这个指针指向的函数,叫做回调函数。
下面说一个例子:
qsort函数:

#include <stdio.h>
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{
    
    
  return (*( int *)p1 - *(int *) p2);
}
int main()
{
    
    
    int arr[] = {
    
     1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    int i = 0;
    
    qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
    for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
   {
    
    
       printf( "%d ", arr[i]);
   }
    printf("\n");
    return 0;
}

谁是回调函数呢?
当然是int int_cmp(const void * p1, const void * p2)
这个函数当中的两个参数,代表着待排序序列的任意一个元素的地址,这里的元素也是多样化的,可以是数组,指针等。
qsort函数是一个排序函数,它可以给任意数据类型排序,整型,浮点型,字符型,都可以。其中细节颇多,下一章,我会模拟实现一个qsort函数,其中再讲述细节。
来看几道有关指针有意思的题目:
第一题:

int main()
{
    
    
    int a[4] = {
    
     1, 2, 3, 4 };
    int *ptr1 = (int *)(&a + 1);
    int *ptr2 = (int *)((int)a + 1);
    printf( "%x,%x", ptr1[-1], *ptr2);
    return 0;
}

附上图解:
在这里插入图片描述
第二题:

int main()
{
    
    
 char *c[] = {
    
    "ENTER","NEW","POINT","FIRST"};
 char**cp[] = {
    
    c+3,c+2,c+1,c};
 char***cpp = cp;
 printf("%s\n", **++cpp);
 printf("%s\n", *--*++cpp+3);
 printf("%s\n", *cpp[-2]+3);
 printf("%s\n", cpp[-1][-1]+1);
 return 0;
}

附上图解:
在这里插入图片描述
本文章多数来自于自己老师上课时的讲解。其中添加了一部分自己的理解,如有错误,欢迎指正。

猜你喜欢

转载自blog.csdn.net/Zhou000815/article/details/110006388