技术原理:C语言中函数指针数组浅析

发现问题

今天,在阅读Linux内核中关于socket的源代码时,遇到了下面一段代码:

struct proto_ops {
    int family;
    struct module *owner;
    int (*release)   (struct socket *sock);
    int (*bind)      (struct socket *sock,
                      struct sockaddr *myaddr,
                      int sockaddr_len);
    int (*connect)   (struct socket *sock,
                      struct sockaddr *vaddr,
                      int sockaddr_len, int flags);
    int (*socketpair)(struct socket *sock1,
                      struct socket *sock2);
    int (*accept)    (struct socket *sock,
                      struct socket *newsock, int flags);
    int (*getname)   (struct socket *sock,
                      struct sockaddr *addr,
                      int *sockaddr_len, int peer);
    unsigned int (*poll)     (struct file *file, struct socket *sock, 
                              struct poll_table_struct *wait);
    int (*ioctl)     (struct socket *sock, unsigned int cmd, unsigned long arg);
    int (*listen)    (struct socket *sock, int len);
    int (*shutdown)  (struct socket *sock, int flags);
    int (*setsockopt)(struct socket *sock, int level,
                      int optname, char __user *optval, int optlen);
    int (*getsockopt)(struct socket *sock, int level,
                      int optname, char __user *optval, int __user *optlen);
    int (*sendmsg)   (struct kiocb *iocb, struct socket *sock,
                      struct msghdr *m, size_t total_len);
    int (*recvmsg)   (struct kiocb *iocb, struct socket *sock,
                      struct msghdr *m, size_t total_len,
                      int flags);
    int (*mmap)      (struct file *file, struct socket *sock,
                      struct vm_area_struct * vma);
    ssize_t (*sendpage)  (struct socket *sock, struct page *page,
                      int offset, size_t size, int flags);
};

在这段代码中,我们注意到proto_ops结构体的成员包括下面这样的成员变量:

int (*release)   (struct socket *sock);

这边是函数指针作为结构体成员变量的使用方法。

问题分析

首先,我们对C和C++中结构体以及C++类的区别进行一些说明:

C中的结构体和C++中结构体的不同之处:
在C中的结构体只能自定义数据类型,结构体中不允许有函数;
而C++中的结构体可以加入成员函数。

C++中的结构体和类的异同:
相同之处:
结构体中可以包含函数;也可以定义public、private、protected数据成员;定义了结构体之后,可以用结构体名来创建对象。但C中的结构体不允许有函数;也就是说在C++当中,结构体中可以有成员变量,可以有成员函数,可以从别的类继承,也可以被别的类继承,可以有虚函数。

不同之处:
结构体定义中默认情况下的成员是public,而类定义中的默认情况下的成员是private的。类中的非static成员函数有this指针,(struct中没有是错误的,一直被误导啊,经过测试struct的成员函数一样具有this指针),类的关键字class能作为template模板的关键字,而struct不可以。

实际上,C中的结构体只涉及到数据结构,而不涉及到算法,也就是说在C中数据结构和算法是分离的,而到C++中一类或者一个结构体可以包含函数(这个函数在C++我们通常中称为成员函数),C++中的结构体和类体现了数据结构和算法的结合。
因此,我们在阅读纯C代码时,应该注意代码中使用函数指针成员变量来等效地实现成员函数过程。

示例代码

这里,我们使用一段代码来对函数指针成员进行相关说明:

#include <stdio.h>
#include <stdlib.h>

int func1(int n)
{
    printf("func1: %d\n", n);
    return n;
}

int func2(int n)
{
    printf("func2: %d\n", n);
    return n;
}

int main()
{
    int (*a[2])(int);
    a[0] = func1;
    a[1] = func2;
    a[0](1);
    a[1](2);

    return 0;
}

我们注意上面代码中的

int (*a[2])(int);

在这句代码中,我们定义了这样一个数组:

数组保存指针,什么样的指针呢?
形如 int func(int input) 的 func函数指针,形参为int变量,返回int变量。
因此,数组保存的是形参为单一int变量和返回值为int值得函数指针。

现在,我们定义了这样一个数组,然后

    a[0] = func1;
    a[1] = func2;

由于我们在main函数前声明和定义了func1和func2两个函数(这两个函数满足前面所提及的函数条件),这时,我们便可以使用这两个函数指针赋值函数指针数组。
然后,我们便可以使用数组成员来实现函数调用:

    a[0](1);
    a[1](2);

最终结果为:
技术原理:C语言中函数指针数组浅析

上一篇:运维调试记录:无法远程连接MySQL数据库


下一篇:Python系列直播——深入Python与日志服务,玩转大规模数据分析处理实战