我正在开发一个项目,我必须为多个函数实现一个处理程序,所有函数都有一个与之关联的数字。与它们关联的数字是如何调用不同的函数。知道了这一点,我希望能够使用数字作为函数的索引将它们分组到一个数组中。问题是,数组内部需要具有相同类型的元素吗?那么如何将这些函数放入数组中呢?
以下是函数的编号方式:
enum
{
SYS_HALT, /* Halt the operating system. */
SYS_EXIT, /* Terminate this process. */
SYS_EXEC, /* Start another process. */
SYS_WAIT, /* Wait for a child process to die. */
SYS_CREATE, /* Create a file. */
SYS_REMOVE, /* Delete a file. */
SYS_OPEN, /* Open a file. */
SYS_FILESIZE, /* Obtain a file's size. */
SYS_READ, /* Read from a file. */
SYS_WRITE, /* Write to a file. */
SYS_SEEK, /* Change position in a file. */
SYS_TELL, /* Report current position in a file. */
SYS_CLOSE, /* Close a file. */
};
以下是函数原型列表:
void halt (void) NO_RETURN;
void exit (int status) NO_RETURN;
pid_t exec (const char *file);
int wait (pid_t);
bool create (const char *file, unsigned initial_size);
bool remove (const char *file);
int open (const char *file);
int filesize (int fd);
int read (int fd, void *buffer, unsigned length);
int write (int fd, const void *buffer, unsigned length);
void seek (int fd, unsigned position);
unsigned tell (int fd);
void close (int fd);
主要问题
是否有一种简单的方法可以将所有函数放入C中的数组或其他数据结构中?
任何帮助都会非常感激,这是针对学校项目的,所以一个小例子或链接会非常有用,但我想自己创建解决方案。
由于
编辑:我希望能够做什么
我希望能够定义一些数组,并在枚举序号的索引处存储指向函数的指针。
//I don't know how to handle the type here
<some_type> system_call_arr[128];
system_call_arr[SYS_HALT] = halt;
system_call_arr[SYS_EXIT] = exit;
system_call_arr[SYS_EXEC] = exec;
// and so on....
修改2
由于Ben说所有的论证都适合32位的论证,我不能定义这样的东西:
typedef int (*sys_func) (uint32_t, uint32_t, uint32_t);
sys_func syscall_array[128];
syscall_array[SYS_HALT] = (sys_func)halt;
//etc....
这样的好习惯吗?
答案 0 :(得分:2)
经典的UNIX方法是为所有这些函数提供相同的签名(比如说int sys_func(struct args *a)
然后将它们全部放入一个函数数组中。当你为这些调用编组参数时,你只需将它们放入arg1
中的arg2
,struct args
等等,每个系统调用都会以不同的方式使用它们。或者您可以在其上添加一个特殊结构,以便为它们提供有意义的名称。
在你的编辑中,你会问,“这样的做法是不是很好?”我在你的问题的上下文中给出了我的答案,这似乎是一个简单的操作系统内核的实现。操作系统会以正常应用程序永远不应该的方式作弊。它可以使用关于字大小的假设(例如,所有内容都是32位,如ILP-32中所知)并了解ABI的内容,因为实现这些内容是操作系统工作的一部分。如果您的操作系统可以移植到其他平台,它将编译为syscall
之类的不同变体来处理这些情况。
为什么我建议使用struct args
而不是您的方法?因为在操作系统中,在使用系统调用的用户和从函数指针表运行的实际代码之间将有几层。如果您通过每个图层显式传递这些参数,则会多次复制它们。如果第一层可以简单地提供一个指针,该指针通过中间函数“跳转”参数。如果你正在处理一个ABI,其中调用约定是在栈上推送参数,你可以避免完全复制参数,只需要一个合适的堆栈地址作为args。
您可以在McKusick等人的 4.4 BSD操作系统的设计和实现中找到对此以及操作系统设计的几乎所有其他方面的讨论。人。如果您能找到较旧的 4.3 版本,那么它实际上相当小,并且与您正在处理的内容完全相关。
答案 1 :(得分:2)
除非你正在做一些提供接口的东西,否则通常没有必要创建一个具有你需要实现的基本操作并设置适当指针的结构。对于这样的事情(如果你不想让它变得通用),只需对枚举值使用switch语句。在大多数情况下,在C中,这会被优化为跳转表,因为所有枚举值都是顺序的,因此在根据某个请求参数检查要执行的函数时,不会出现管道停顿。
在Linux内核中,有一些基本的操作结构,它们具有预定义的函数原型,您需要做的就是实现这些函数并导出一个结构,该结构具有为您的实现设置的相应函数指针,并且它是即插即用的但是它似乎这样做对你想要的东西来说太过分了。
答案 2 :(得分:2)
在任何现代系统中,您只需创建一个void *数组来保存函数指针。但是,这在ANSI C中是技术上未定义的行为。相反,您可以使用类似void(*)()的泛型函数指针类型,并在指向See the C FAQ时将指针强制转换为正确的函数指针类型。但是无论如何你都需要一个switch语句才能进行演员表达,所以正如耶稣提到的那样,你可以同时调用正确的函数并完全忘记数组。
答案 3 :(得分:1)
如果您愿意避免使用类型安全性,可以创建一个联合类型,如下所示:
union Args {
size_t st;
ssize_t sst;
int i;
unsigned u;
pid_t pt;
mode_t mt;
char *pc;
const char* cpc;
void *pv;
const void *cpv;
/* etc */
};
然后,对于每个函数,让它们接受union Args *
作为参数列表,并size_t
作为参数个数(open可以接受3个参数 - 它使用va_ *来检查它们根据需要,让他们返回union Args
。
注意:对于void
个功能,只需拥有return 0
。
E.g。
union Args my_open(union Args *args, size_t nargs) {
union Args a;
switch (nargs) {
case 2:
a.i = open(args[0].cpc, args[1].i);
break;
case 3:
a.i = open(args[0].cpc, args[1].i, args[2].mt);
break;
default:
errno = EINVAL;
a.i = -1;
}
return a;
}
然后你可以有以下几点:
typedef union Args (*My_func)(union Args *, size_t);
My_func *fptrs;