int add2to3(int (*functionPtr)(int, int)) {return (*functionPtr)(2, 3);}
我们也可以在返回值中使用函数指针(试着跟上,它会变得混乱):
// this is a function called functionFactory which receives parameter n// and returns a pointer to another function which receives two ints// and it returns another intint (*functionFactory(int n))(int, int) {printf("Got parameter %d", n);int (*functionPtr)(int,int) = &addInt;return functionPtr;}
但是使用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;}
int eax = ((int(*)())("\xc3 <- This returns the value of the EAX register"))();
Write a swap function
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);
Write a for-loop counter to 1000, calling some function each time
((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
You can even write a recursive function that counts to 100
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);
Note that compilers place string literals in the .rodata section (or .rdata on Windows), which is linked as part of the text segment (along with code for functions).
The text segment has Read+Exec permission, so casting string literals to function pointers works without needing mprotect() or VirtualProtect() system calls like you'd need for dynamically allocated memory. (Or gcc -z execstack links the program with stack + data segment + heap executable, as a quick hack.)
To disassemble these, you can compile this to put a label on the bytes, and use a disassembler.
// at global scopeconst 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 stack4: 8b 5c 24 08 mov ebx,DWORD PTR [esp+0x8] # ebx = b8: 8b 00 mov eax,DWORD PTR [eax] # dereference: eax = *aa: 8b 1b mov ebx,DWORD PTR [ebx]c: 31 c3 xor ebx,eax # pointless xor-swape: 31 d8 xor eax,ebx # instead of just storing with opposite registers10: 31 c3 xor ebx,eax12: 8b 4c 24 04 mov ecx,DWORD PTR [esp+0x4] # reload a from the stack16: 89 01 mov DWORD PTR [ecx],eax # store to *a18: 8b 4c 24 08 mov ecx,DWORD PTR [esp+0x8]1c: 89 19 mov DWORD PTR [ecx],ebx1e: c3 ret
not shown: the later bytes are ASCII text documentationthey'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 screenvoid (* 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
在version.c中,我将定义version.h中存在的2个函数原型
版本c
#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_VERSIONzprintf = &printf;#else// Defined below this functionzprintf = &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 functionboard_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 Pointersvoid sayhello();//Declare The Function Whose Address is to be passed//The Functions should Be of Same Typeint main(){print=sayhello;//Addressof sayhello is assigned to printprint();//print Does A call To The Functionreturn 0;}
void sayhello(){printf("\n Hello World");}
#include <stdio.h>
#define NUM_A 1#define NUM_B 2
// define a function pointer typetypedef int (*two_num_operation)(int, int);
// an actual standalone functionstatic 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 ∑}
// test - use function pointer as variable,void test_pointer_as_variable() {// create a pointer to function,two_num_operation sum_p = ∑// call function via pointerprintf("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;}
一个非常基本的例子,如果有一个名为print(int x, int y)的函数,它可能需要调用一个函数(add()或sub(),它们的类型相同),那么我们将做什么,我们将向print()函数添加一个函数指针参数,如下所示:
#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 pointerint (*pFunc)(int a, char *pStr); // declares a function pointer that returns an int
几种不同的用法示例
使用函数指针的一些示例:
int (*pFunc) (int a, char *pStr); // declare a simple function pointer variableint (*pFunc[55])(int a, char *pStr); // declare an array of 55 function pointersint (**pFunc)(int a, char *pStr); // declare a pointer to a function pointer variablestruct { // declare a struct that contains a function pointerint x22;int (*pFunc)(int a, char *pStr);} thing = {0, func}; // assign values to the struct variablechar * xF (int x, int (*p)(int a, char *pStr)); // declare a function that has a function pointer as an argumentchar * (*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, ...);
或者您根本无法指定参数列表。这可能很有用,但它消除了C编译器对提供的参数列表执行检查的机会。
int sum (); // nothing specified in the argument list so could be anything or nothingint (*psum)();int sum2(void); // void specified in the argument list so no parameters when calling this functionint (*psum2)(void);
int sum (int a, char *b);int (*psplsum) (int a, int b);psplsum = sum; // generates a compiler warningpsplsum = (int (*)(int a, int b)) sum; // no compiler warning, cast to function pointerpsplsum = (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 pointersfunc1, 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 indexfor (i = 0; i < sizeof(p) / sizeof(p[0]); i++) {func4(a, b, 0, p[i]);}// iterate over the array elements using a pointerfor (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 intchar *(*func2) (int a, int b, char *c); // pointer to function that returns a pointer} FuncThings;
extern const FuncThings FuncThingsGlobal;
然后在C源文件中:
#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};
// Say you have add functionint add(int x, int y){return x + y;}
// Say you have another add functionint another_add(int x, int y){return y + x;}
int main(){// Although the types of another_add and add are same// You can't doanother_add = add
// You have a compute function that takes a function of int's signatureint (*compute)(int, int);
// You won't even be able to pass functions to other functions// (Although when you do, C is just passing the pointer to that function)// So, compute(add) is really compute(&add)// But you can create a pointer to functions that are variables// you can assign to and/or pass to other functions
int (*operation)(int, int);// Now you can dooperation = &add;// You could also do, the following to do the same thing// When a function is passed in right hand side of assignment,// C knows that you mean pointer, and you don't need explicit &operation = add;}