天天看點

技術原理: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);           

最終結果為: