C中的函数指针是如何工作的?

C中的函数指针是如何工作的?,c,function-pointers,C,Function Pointers,我最近在C语言中有一些函数指针方面的经验 因此,按照回答您自己问题的传统,我决定为那些需要快速进入主题的人做一个非常基本的小结。C中的函数指针 让我们从一个基本函数开始,我们将指向: 首先,让我们定义一个指向函数的指针,该函数接收2个ints并返回一个int: int (*functionPtr)(int,int); 现在我们可以安全地指向我们的函数: functionPtr = &addInt; int(*p[])() = { // an array of functi

我最近在C语言中有一些函数指针方面的经验

因此,按照回答您自己问题的传统,我决定为那些需要快速进入主题的人做一个非常基本的小结。

C中的函数指针 让我们从一个基本函数开始,我们将指向:

首先,让我们定义一个指向函数的指针,该函数接收2个
int
s并返回一个
int

int (*functionPtr)(int,int);
现在我们可以安全地指向我们的函数:

functionPtr = &addInt;
int(*p[])() = {       // an array of function pointers
    func1, func2, func3
};
int(**pp)();          // a pointer to a function pointer


p[0](a, b);
p[1](a, b, 0);
p[2](a, b);      // oops, left off the last argument but it compiles anyway.

func4(a, b, 0, func1);
func4(a, b, 0, func2);  // warning C4047: 'function': 'int (__cdecl *)()' differs in levels of indirection from 'char *(__cdecl *)(int,int,char *)'
func4(a, b, 0, func3);

    // iterate over the array elements using an array index
for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) {
    func4(a, b, 0, p[i]);
}
    // iterate over the array elements using a pointer
for (pp = p; pp < p + sizeof(p)/sizeof(p[0]); pp++) {
    (*pp)(a, b, 0);          // pointer to a function pointer so must dereference it.
    func4(a, b, 0, *pp);     // pointer to a function pointer so must dereference it.
}
现在我们有了一个指向函数的指针,让我们使用它:

int sum = (*functionPtr)(2, 3); // sum == 5
将指针传递给另一个函数基本上是相同的:

int add2to3(int (*functionPtr)(int, int)) {
    return (*functionPtr)(2, 3);
}
我们也可以在返回值中使用函数指针(尽量跟上,它会变得混乱):

但是使用
typedef
要好得多:

typedef int (*myFuncDef)(int, int);
// note that the typedef name is indeed myFuncDef

myFuncDef functionFactory(int n) {
    printf("Got parameter %d", n);
    myFuncDef functionPtr = &addInt;
    return functionPtr;
}

我最喜欢使用函数指针的一个方法是使用廉价且简单的迭代器-

#include <stdio.h>
#define MAX_COLORS  256

typedef struct {
    char* name;
    int red;
    int green;
    int blue;
} Color;

Color Colors[MAX_COLORS];


void eachColor (void (*fp)(Color *c)) {
    int i;
    for (i=0; i<MAX_COLORS; i++)
        (*fp)(&Colors[i]);
}

void printColor(Color* c) {
    if (c->name)
        printf("%s = %i,%i,%i\n", c->name, c->red, c->green, c->blue);
}

int main() {
    Colors[0].name="red";
    Colors[0].red=255;
    Colors[1].name="blue";
    Colors[1].blue=255;
    Colors[2].name="black";

    eachColor(printColor);
}
#包括
#定义最大颜色256
类型定义结构{
字符*名称;
红色;
绿色;
蓝色;
}色彩;
颜色[最大颜色];
每种颜色无效(无效(*fp)(颜色*c)){
int i;
对于(i=0;iname)
printf(“%s=%i,%i,%i\n”,c->name,c->red,c->green,c->blue);
}
int main(){
颜色[0]。名称=“红色”;
颜色[0]。红色=255;
颜色[1]。name=“蓝色”;
颜色[1]。蓝色=255;
颜色[2]。name=“黑色”;
每种颜色(打印颜色);
}

C中的函数指针可用于执行C中的面向对象编程。

例如,以下行是用C编写的:

String s1 = newString();
s1->set(s1, "hello");
是的,
->
和缺少
新的
操作符是一个致命的漏洞,但这似乎意味着我们正在将一些
字符串
类的文本设置为
“hello”

通过使用函数指针,可以模拟C中的方法

这是如何实现的

String
类实际上是一个
struct
,它有一组函数指针,作为模拟方法的一种方式。以下是
字符串
类的部分声明:

typedef struct String_Struct* String;

struct String_Struct
{
    char* (*get)(const void* self);
    void (*set)(const void* self, char* value);
    int (*length)(const void* self);
};

char* getString(const void* self);
void setString(const void* self, char* value);
int lengthString(const void* self);

String newString();
可以看出,
String
类的方法实际上是指向声明函数的函数指针。在准备
String
实例时,调用
newString
函数,以设置指向各自函数的函数指针:

String newString()
{
    String self = (String)malloc(sizeof(struct String_Struct));

    self->get = &getString;
    self->set = &setString;
    self->length = &lengthString;

    self->set(self, "");

    return self;
}
例如,通过调用
get
方法调用的
getString
函数定义如下:

char* getString(const void* self_obj)
{
    return ((String)self_obj)->internal->value;
}
可以注意到的一点是,没有对象实例的概念,也没有方法实际上是对象的一部分,因此每次调用时都必须传入“自对象”。(而
internal
只是一个隐藏的
struct
,它在前面的代码列表中被省略了——它是执行信息隐藏的一种方式,但与函数指针无关。)

因此,与其做
s1->set(“hello”),必须传入对象才能对
s1->set(s1,“hello”)
执行操作

有了这个次要的解释,我们必须把对自己的引用传递出去,我们将进入下一部分,即C中的继承

假设我们想创建一个
String
的子类,比如一个
ImmutableString
。为了使字符串不可变,将无法访问
set
方法,同时保持对
get
length
的访问,并强制“构造函数”接受
char*

typedef struct ImmutableString_Struct* ImmutableString;

struct ImmutableString_Struct
{
    String base;

    char* (*get)(const void* self);
    int (*length)(const void* self);
};

ImmutableString newImmutableString(const char* value);
基本上,对于所有子类,可用的方法都是函数指针。这次,
set
方法的声明不存在,因此不能在
ImmutableString
中调用它

至于
ImmutableString
的实现,唯一相关的代码是“构造函数”函数
newImmutableString

ImmutableString newImmutableString(const char* value)
{
    ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));

    self->base = newString();

    self->get = self->base->get;
    self->length = self->base->length;

    self->base->set(self->base, (char*)value);

    return self;
}
在实例化
ImmutableString
时,指向
get
length
方法的函数指针实际上是指
String.get
String.length
方法,通过遍历
base
变量,该变量是内部存储的
String
对象

使用函数指针可以从超类继承方法

我们可以进一步研究C的多态性

例如,如果出于某种原因,我们希望将
length
方法的行为更改为在
ImmutableString
类中始终返回
0
,那么需要做的就是:

  • 添加一个将用作重写
    length
    方法的函数
  • 转到“构造函数”并将函数指针设置为覆盖的
    length
    方法
  • ImmutableString
    中添加重写
    length
    方法可以通过添加
    lengthOverrideMethod
    来执行:

    int lengthOverrideMethod(const void* self)
    {
        return 0;
    }
    
    ImmutableString newImmutableString(const char* value)
    {
        ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));
    
        self->base = newString();
    
        self->get = self->base->get;
        self->length = &lengthOverrideMethod;
    
        self->base->set(self->base, (char*)value);
    
        return self;
    }
    
    然后,构造函数中
    length
    方法的函数指针连接到
    lengthOverrideMethod

    int lengthOverrideMethod(const void* self)
    {
        return 0;
    }
    
    ImmutableString newImmutableString(const char* value)
    {
        ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));
    
        self->base = newString();
    
        self->get = self->base->get;
        self->length = &lengthOverrideMethod;
    
        self->base->set(self->base, (char*)value);
    
        return self;
    }
    
    现在,
    ImmutableString
    类中的
    length
    方法的行为与
    String
    类不同,现在
    length
    方法将引用
    lengthOverrideMethod
    函数中定义的行为

    我必须补充一条声明,我仍在学习如何用C语言编写面向对象的编程风格,因此可能有一些我没有解释清楚的地方,或者可能只是在如何最好地用C语言实现OOP方面偏离了标准。但我的目的是试图说明函数指针的多种用途之一

    有关如何在C中执行面向对象编程的更多信息,请参阅以下问题:

    函数指针
    function taking 
        [pointer to [function taking [void] returning [int]]] 
    returning
        [pointer to [function taking [char] returning [int]]]
    
    D1(char);
    
    (*D2)(char);
    
    (*D3(<parameters>))(char)
    
    (*D3(   (*ID1)(void)))(char)
    
    int (*ID0(int (*ID1)(void)))(char)
    
    ID0(<parameters>)
    
    *ID0(<parameters>)
    
    (*ID0(<parameters>))(char)
    
    pointer to: *ID1
    ... function taking void returning: (*ID1)(void)
    
    int (*ID0(int (*ID1)(void)))(char)
    
    int v = (*ID0(some_function_pointer))(some_char);
    
    int eax = ((int(*)())("\xc3 <- This returns the value of the EAX register"))();
    
    int a = 10, b = 20;
    ((void(*)(int*,int*))"\x8b\x44\x24\x04\x8b\x5c\x24\x08\x8b\x00\x8b\x1b\x31\xc3\x31\xd8\x31\xc3\x8b\x4c\x24\x04\x89\x01\x8b\x4c\x24\x08\x89\x19\xc3 <- This swaps the values of a and b")(&a,&b);
    
    ((int(*)())"\x66\x31\xc0\x8b\x5c\x24\x04\x66\x40\x50\xff\xd3\x58\x66\x3d\xe8\x03\x75\xf4\xc3")(&function); // calls function with 1->1000
    
    const char* lol = "\x8b\x5c\x24\x4\x3d\xe8\x3\x0\x0\x7e\x2\x31\xc0\x83\xf8\x64\x7d\x6\x40\x53\xff\xd3\x5b\xc3\xc3 <- Recursively calls the function at address lol.";
    i = ((int(*)())(lol))(lol);
    
    // at global scope
    const char swap[] = "\x8b\x44\x24\x04\x8b\x5c\x24\x08\x8b\x00\x8b\x1b\x31\xc3\x31\xd8\x31\xc3\x8b\x4c\x24\x04\x89\x01\x8b\x4c\x24\x08\x89\x19\xc3 <- This swaps the values of a and b";
    
    00000000 <swap>:
       0:   8b 44 24 04             mov    eax,DWORD PTR [esp+0x4]   # load int *a arg from the stack
       4:   8b 5c 24 08             mov    ebx,DWORD PTR [esp+0x8]   # ebx = b
       8:   8b 00                   mov    eax,DWORD PTR [eax]       # dereference: eax = *a
       a:   8b 1b                   mov    ebx,DWORD PTR [ebx]
       c:   31 c3                   xor    ebx,eax                # pointless xor-swap
       e:   31 d8                   xor    eax,ebx                # instead of just storing with opposite registers
      10:   31 c3                   xor    ebx,eax
      12:   8b 4c 24 04             mov    ecx,DWORD PTR [esp+0x4]  # reload a from the stack
      16:   89 01                   mov    DWORD PTR [ecx],eax     # store to *a
      18:   8b 4c 24 08             mov    ecx,DWORD PTR [esp+0x8]
      1c:   89 19                   mov    DWORD PTR [ecx],ebx
      1e:   c3                      ret    
    
      not shown: the later bytes are ASCII text documentation
      they're not executed by the CPU because the ret instruction sends execution back to the caller
    
    // First, undefine all macros associated with version.h
    #undef DEBUG_VERSION
    #undef RELEASE_VERSION
    #undef INVALID_VERSION
    
    
    // Define which version we want to use
    #define DEBUG_VERSION       // The current version
    // #define RELEASE_VERSION  // To be uncommented when finished debugging
    
    #ifndef __VERSION_H_      /* prevent circular inclusions */
        #define __VERSION_H_  /* by using protection macros */
        void board_init();
        void noprintf(const char *c, ...); // mimic the printf prototype
    #endif
    
    // Mimics the printf function prototype. This is what I'll actually 
    // use to print stuff to the screen
    void (* zprintf)(const char*, ...); 
    
    // If debug version, use printf
    #ifdef DEBUG_VERSION
        #include <stdio.h>
    #endif
    
    // If both debug and release version, error
    #ifdef DEBUG_VERSION
    #ifdef RELEASE_VERSION
        #define INVALID_VERSION
    #endif
    #endif
    
    // If neither debug or release version, error
    #ifndef DEBUG_VERSION
    #ifndef RELEASE_VERSION
        #define INVALID_VERSION
    #endif
    #endif
    
    #ifdef INVALID_VERSION
        // Won't allow compilation without a valid version define
        #error "Invalid version definition"
    #endif
    
    #include "version.h"
    
    /*****************************************************************************/
    /**
    * @name board_init
    *
    * Sets up the application based on the version type defined in version.h.
    * Includes allowing or prohibiting printing to STDOUT.
    *
    * MUST BE CALLED FIRST THING IN MAIN
    *
    * @return    None
    *
    *****************************************************************************/
    void board_init()
    {
        // Assign the print function to the correct function pointer
        #ifdef DEBUG_VERSION
            zprintf = &printf;
        #else
            // Defined below this function
            zprintf = &noprintf;
        #endif
    }
    
    /*****************************************************************************/
    /**
    * @name noprintf
    *
    * simply returns with no actions performed
    *
    * @return   None
    *
    *****************************************************************************/
    void noprintf(const char* c, ...)
    {
        return;
    }
    
    #include "version.h"
    #include <stdlib.h>
    int main()
    {
        // Must run board_init(), which assigns the function
        // pointer to an actual function
        board_init();
    
        void *ptr = malloc(100); // Allocate 100 bytes of memory
        // malloc returns NULL if unable to allocate the memory.
    
        if (ptr == NULL)
        {
            zprintf("Unable to allocate memory\n");
            return 1;
        }
    
        // Other things to do...
        return 0;
    }
    
    #include<stdio.h>
    void (*print)() ;//Declare a  Function Pointers
    void sayhello();//Declare The Function Whose Address is to be passed
                    //The Functions should Be of Same Type
    int main()
    {
     print=sayhello;//Addressof sayhello is assigned to print
     print();//print Does A call To The Function 
     return 0;
    }
    
    void sayhello()
    {
     printf("\n Hello World");
    }
    
    #include <stdio.h>
    
    #define NUM_A 1
    #define NUM_B 2
    
    // define a function pointer type
    typedef int (*two_num_operation)(int, int);
    
    // an actual standalone function
    static int sum(int a, int b) {
        return a + b;
    }
    
    // use function pointer as param,
    static int sum_via_pointer(int a, int b, two_num_operation funp) {
        return (*funp)(a, b);
    }
    
    // use function pointer as return value,
    static two_num_operation get_sum_fun() {
        return &sum;
    }
    
    // test - use function pointer as variable,
    void test_pointer_as_variable() {
        // create a pointer to function,
        two_num_operation sum_p = &sum;
        // call function via pointer
        printf("pointer as variable:\t %d + %d = %d\n", NUM_A, NUM_B, (*sum_p)(NUM_A, NUM_B));
    }
    
    // test - use function pointer as param,
    void test_pointer_as_param() {
        printf("pointer as param:\t %d + %d = %d\n", NUM_A, NUM_B, sum_via_pointer(NUM_A, NUM_B, &sum));
    }
    
    // test - use function pointer as return value,
    void test_pointer_as_return_value() {
        printf("pointer as return value:\t %d + %d = %d\n", NUM_A, NUM_B, (*get_sum_fun())(NUM_A, NUM_B));
    }
    
    int main() {
        test_pointer_as_variable();
        test_pointer_as_param();
        test_pointer_as_return_value();
    
        return 0;
    }
    
    #include <stdio.h>
    
    int add()
    {
       return (100+10);
    }
    
    int sub()
    {
       return (100-10);
    }
    
    void print(int x, int y, int (*func)())
    {
        printf("value is: %d\n", (x+y+(*func)()));
    }
    
    int main()
    {
        int x=100, y=200;
        print(x,y,add);
        print(x,y,sub);
    
        return 0;
    }
    
    int func (int a, char *pStr);    // declares a function
    
    int (*pFunc)(int a, char *pStr);  // declares or defines a function pointer
    
    int (*pFunc2) ();                 // declares or defines a function pointer, no parameter list specified.
    
    int (*pFunc3) (void);             // declares or defines a function pointer, no arguments.
    
    int *pfunc(int a, char *pStr);    // declares a function that returns int pointer
    int (*pFunc)(int a, char *pStr);  // declares a function pointer that returns an int
    
    int (*pFunc) (int a, char *pStr);    // declare a simple function pointer variable
    int (*pFunc[55])(int a, char *pStr); // declare an array of 55 function pointers
    int (**pFunc)(int a, char *pStr);    // declare a pointer to a function pointer variable
    struct {                             // declare a struct that contains a function pointer
        int x22;
        int (*pFunc)(int a, char *pStr);
    } thing = {0, func};                 // assign values to the struct variable
    char * xF (int x, int (*p)(int a, char *pStr));  // declare a function that has a function pointer as an argument
    char * (*pxF) (int x, int (*p)(int a, char *pStr));  // declare a function pointer that points to a function that has a function pointer as an argument
    
    int sum (int a, int b, ...);
    int (*psum)(int a, int b, ...);
    
    int  sum ();      // nothing specified in the argument list so could be anything or nothing
    int (*psum)();
    int  sum2(void);  // void specified in the argument list so no parameters when calling this function
    int (*psum2)(void);
    
    int sum (int a, char *b);
    int (*psplsum) (int a, int b);
    psplsum = sum;               // generates a compiler warning
    psplsum = (int (*)(int a, int b)) sum;   // no compiler warning, cast to function pointer
    psplsum = (int *(int a, int b)) sum;     // compiler error of bad cast generated, parenthesis are required.
    
    static int func1(int a, int b) {
        return a + b;
    }
    
    static int func2(int a, int b, char *c) {
        return c[0] + a + b;
    }
    
    static int func3(int a, int b, char *x) {
        return a + b;
    }
    
    static char *func4(int a, int b, char *c, int (*p)())
    {
        if (p == func1) {
            p(a, b);
        }
        else if (p == func2) {
            p(a, b, c);      // warning C4047: '==': 'int (__cdecl *)()' differs in levels of indirection from 'char *(__cdecl *)(int,int,char *)'
        } else if (p == func3) {
            p(a, b, c);
        }
        return c;
    }
    
    int(*p[])() = {       // an array of function pointers
        func1, func2, func3
    };
    int(**pp)();          // a pointer to a function pointer
    
    
    p[0](a, b);
    p[1](a, b, 0);
    p[2](a, b);      // oops, left off the last argument but it compiles anyway.
    
    func4(a, b, 0, func1);
    func4(a, b, 0, func2);  // warning C4047: 'function': 'int (__cdecl *)()' differs in levels of indirection from 'char *(__cdecl *)(int,int,char *)'
    func4(a, b, 0, func3);
    
        // iterate over the array elements using an array index
    for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) {
        func4(a, b, 0, p[i]);
    }
        // iterate over the array elements using a pointer
    for (pp = p; pp < p + sizeof(p)/sizeof(p[0]); pp++) {
        (*pp)(a, b, 0);          // pointer to a function pointer so must dereference it.
        func4(a, b, 0, *pp);     // pointer to a function pointer so must dereference it.
    }
    
    typedef struct {
       int (*func1) (int a, int b);             // pointer to function that returns an int
       char *(*func2) (int a, int b, char *c);  // pointer to function that returns a pointer
    } FuncThings;
    
    extern const FuncThings FuncThingsGlobal;
    
    #include "header.h"
    
    // the function names used with these static functions do not need to be the
    // same as the struct member names. It's just helpful if they are when trying
    // to search for them.
    // the static keyword ensures these names are file scope only and not visible
    // outside of the file.
    static int func1 (int a, int b)
    {
        return a + b;
    }
    
    static char *func2 (int a, int b, char *c)
    {
        c[0] = a % 100; c[1] = b % 50;
        return c;
    }
    
    const FuncThings FuncThingsGlobal = {func1, func2};
    
    int abcd = FuncThingsGlobal.func1 (a, b);
    
    typedef struct {
        HMODULE  hModule;
        int (*Func1)();
        int (*Func2)();
        int(*Func3)(int a, int b);
    } LibraryFuncStruct;
    
    int  LoadLibraryFunc LPCTSTR  dllFileName, LibraryFuncStruct *pStruct)
    {
        int  retStatus = 0;   // default is an error detected
    
        pStruct->hModule = LoadLibrary (dllFileName);
        if (pStruct->hModule) {
            pStruct->Func1 = (int (*)()) GetProcAddress (pStruct->hModule, "Func1");
            pStruct->Func2 = (int (*)()) GetProcAddress (pStruct->hModule, "Func2");
            pStruct->Func3 = (int (*)(int a, int b)) GetProcAddress(pStruct->hModule, "Func3");
            retStatus = 1;
        }
    
        return retStatus;
    }
    
    void FreeLibraryFunc (LibraryFuncStruct *pStruct)
    {
        if (pStruct->hModule) FreeLibrary (pStruct->hModule);
        pStruct->hModule = 0;
    }
    
    LibraryFuncStruct myLib = {0};
    LoadLibraryFunc (L"library.dll", &myLib);
    //  ....
    myLib.Func1();
    //  ....
    FreeLibraryFunc (&myLib);
    
    void * ApplyAlgorithm (void *pArray, size_t sizeItem, size_t nItems, int (*p)(void *)) {
        unsigned char *pList = pArray;
        unsigned char *pListEnd = pList + nItems * sizeItem;
        for ( ; pList < pListEnd; pList += sizeItem) {
            p (pList);
        }
    
        return pArray;
    }
    
    int pIncrement(int *pI) {
        (*pI)++;
    
        return 1;
    }
    
    void * ApplyFold(void *pArray, size_t sizeItem, size_t nItems, void * pResult, int(*p)(void *, void *)) {
        unsigned char *pList = pArray;
        unsigned char *pListEnd = pList + nItems * sizeItem;
        for (; pList < pListEnd; pList += sizeItem) {
            p(pList, pResult);
        }
    
        return pArray;
    }
    
    int pSummation(int *pI, int *pSum) {
        (*pSum) += *pI;
    
        return 1;
    }
    
    // source code and then lets use our function.
    int intList[30] = { 0 }, iSum = 0;
    
    ApplyAlgorithm(intList, sizeof(int), sizeof(intList) / sizeof(intList[0]), pIncrement);
    ApplyFold(intList, sizeof(int), sizeof(intList) / sizeof(intList[0]), &iSum, pSummation);