我最近在C语言中有一些函数指针的经验。
因此,按照回答自己问题的传统,我决定对最基本的问题做一个简短的总结,以供那些需要快速切入主题的人使用。
我最近在C语言中有一些函数指针的经验。
因此,按照回答自己问题的传统,我决定对最基本的问题做一个简短的总结,以供那些需要快速切入主题的人使用。
当前回答
我最喜欢的函数指针用途之一是迭代器-
#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);
}
其他回答
由于函数指针通常是类型化回调,因此您可能需要查看类型安全回调。这同样适用于非回调函数的入口点等。
C相当善变,同时也很宽容:)
“从头开始”功能具有“开始执行的内存地址”。在汇编语言中,它们被称为(调用“函数的内存地址”)。现在回到C。如果函数有内存地址,那么它们可以由C中的指针操作
1.首先需要声明一个指向函数的指针2.传递所需函数的地址
****注意->函数应为相同类型****
这个简单的计划将说明一切。
#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");
}
之后,让我们来看看机器是如何理解它们的。32位体系结构的上述程序的机器指令一瞥。
红色标记区域显示地址如何在eax中交换和存储。然后是eax上的调用指令。eax包含函数所需的地址。
C中函数指针的一个主要用途是调用在运行时选择的函数。例如,C运行时库有两个例程,qsort和bsearch,它们获取一个指向函数的指针,该函数被调用来比较正在排序的两个项目;这允许您根据希望使用的任何条件分别对任何内容进行排序或搜索。
一个非常基本的例子是,如果有一个名为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;
}
输出为:
值为:410值为:390
C中的函数指针
让我们从一个我们将要指向的基本函数开始:
int addInt(int n, int m) {
return n+m;
}
首先,让我们定义一个指向接收2个int并返回一个int的函数的指针:
int (*functionPtr)(int,int);
现在我们可以安全地指出我们的功能:
functionPtr = &addInt;
现在我们有一个指向函数的指针,让我们使用它:
int sum = (*functionPtr)(2, 3); // sum == 5
将指针传递给另一个函数基本上是相同的:
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 int
int (*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;
}
函数指针的另一个好用法:在版本之间轻松切换
当您需要在不同的时间或不同的开发阶段使用不同的功能时,它们非常方便。例如,我正在一台有控制台的主机上开发一个应用程序,但该软件的最终版本将放在Avnet ZedBoard(它有显示器和控制台的端口,但最终版本不需要这些端口)上。所以在开发过程中,我将使用printf查看状态和错误消息,但当我完成后,我不希望打印任何内容。以下是我所做的:
版本.h
// 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
在版本c中,我将定义版本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_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;
}
注意函数指针在版本.h中如何原型化为void(*zprintf)(constchar*,…);当它在应用程序中被引用时,它将在其指向的任何位置开始执行,这一点尚未定义。在版本.c中,注意在board_init()函数中,zprintf被分配了一个唯一的函数(其函数签名匹配),这取决于版本中定义的版本
运行代码将如下所示:
主程序c
#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;
}
如果处于调试模式,上述代码将使用printf,如果处于发布模式,则不执行任何操作。这比浏览整个项目并注释或删除代码要容易得多。我所需要做的就是在版本.h中更改版本,其余的都由代码完成!