如何检查函数指针是否已初始化? 我可以检查NULL,但如果不是null可能是垃圾,对吗?
我有以下内容:
#include <stdio.h>
#include <stdlib.h>
typedef struct client_struct
{
char *name;
char *email;
void (*print)(struct client_struct *c);
} client;
void print_client(client *c)
{
if (c->print != NULL)
c->print(c);
}
int main()
{
client *c = (client *)malloc(sizeof(client));
c->email = (char *)malloc(50 * sizeof(char));
sprintf(c->email, "email@server.com");
c->name = (char *)malloc(50 * sizeof(char));
sprintf(c->name, "some name");
//Uncommenting line below work as expected, otherwise segmentation fault
//c->print = NULL;
print_client(c);
printf("\nEOF\n");
int xXx = getchar();
return 0;
}
如何检查此指针是否真正指向函数“void(* f)(client *)”? 比较大小不起作用,因为相同大小的垃圾可以正确吗? 我希望有一种方法可以根据C标准来实现这一目标。
答案 0 :(得分:1)
检查是否使用有效函数初始化函数指针不是一个容易解决的问题。任何解决方案,不可以跨平台移植,并且还依赖于最终的二进制格式(静态或动态可链接格式)。对于不同的二进制格式,有很多方法可以做到这一点,并且取得了不同的成功,但是我不打算重复每一个排列。希望这会让你走下那个兔子洞:-)然后你可以找出适合你的特殊解决方案。
为了使某些解决方案能够正常工作,您必须确保链接的二进制文件具有导出的符号(它可以在没有符号的情况下执行,但它更难以实现,而且我不会这样做有时间)。因此,当您关联程序时,请确保已启用动态符号。
话虽如此,这是一种可以在使用dlfcn函数的系统上使用的方法。 (见下面的历史)
正如@Deduplicator在下面的评论中指出的那样,可能会出现0xdeadbeef
可能随意发生指向有效函数的情况,在这种情况下,您最终可能会遇到最终调用< em>错误的有效功能。有一些方法可以在编译时或运行时缓解这种情况,但您必须手动构建解决方案。例如,C ++通过在命名空间中将命名空间修改为符号来实现它。你可能要求这样的事情发生。 (我会想到一个有趣的方法来做这个并发布它)
使用dladdr
(SysV)(GNU也有一个dladdr1)来确定你提供的地址属于哪个函数:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
int is_valid_function_ptr( void *func) {
Dl_info info;
int rc;
rc = dladdr(func, &info);
if (!rc) {
/* really should do more checking here */
return 0;
}
return 1; /* you can print out function names and stuff here */
}
void print(const char *value) {
fprintf(stdout, "%s", value);
}
void call_the_printer(void (*foo)(), const char *value)
{
if(is_valid_function_ptr(foo)) {
foo(value);
}
else {
fprintf(stderr, "The Beef is Dead!\n");
}
}
int main()
{
void (*funcptr)() = (void (*)()) 0xdeadbeef; /* some dead pointer */
call_the_printer(funcptr, "Hello Dead Beef\n");
funcptr = print; /* actually a function */
call_the_printer(funcptr, "Hello Printer\n");
return 0;
}
在链接过程中使用-rdynamic
或-Wl,--export-dynamic
,因此请编译:
gcc -o ex1 -rdynamic ex1.c
Windows做了自己的事情(一如既往),我还没有测试过这些,但基本概念应该有效:
同时使用GetModuleHandle
和EnumCurrentProcess
获取加载的符号信息,并在循环中运行指针,看它们是否与其中的任何地址匹配。
另一种方法是使用VirtualQuery
然后将mbi.AllocationBase
转换为(HMODULE)并查看是否获得了自己的二进制文件的路径。
答案 1 :(得分:0)
在c函数中,指针与常规指针没什么区别,按标准,它们有一个值表示不应该使用该值,这是NULL
。
使用指针的方法是将它们仅设置为有效值或NULL
。没有其他方法可以确定有一个OK值。根据定义,每个非NULL
的值都应视为有效。
答案 2 :(得分:0)
与其他评论和答案中指出的一样,没有办法检查变量是否已初始化。这就是为什么将vars初始化为NULL
然后检查被认为是一种良好的做法。
如果你真的想验证函数指针是否指向正确的位置,你可以导出函数并从ELF符号加载指针(参见:http://gcc.gnu.org/wiki/Visibility)
答案 3 :(得分:0)
如评论中所述,不可能100%确定指针是否为垃圾。
为了避免这种情况,你可以提供一个&#34;构造函数&#34;功能,像这样:
struct client_struct* client_allocate()
{
struct client_struct* object = malloc(sizeof *object);
if (object)
{
object->name = NULL;
object->email = NULL;
object->print = NULL;
}
return object;
}
然后在您的文档中写下创建&#34;客户&#34;的唯一有效方法。是通过使用您的功能。如果您这样做,则还应提供destroy
功能,并在其中致电free
。
假设您有一天向struct
添加新指针。然后更新client_allocate
函数,将此指针设置为NULL
,新指针将始终正确初始化。无需更新分配了struct
的代码中的所有位置,因为现在只有一个这样的位置。
答案 4 :(得分:-1)
首先检查空参数。
void print_client(client *c)
{
if ((c != NULL) && (c->print != NULL))
{
c->print(c);
}
}
至于你的问题,在你的客户端结构被malloc命令后,取消它。这样,您可以确保未分配的函数指针确实== NULL。
client* create_client(void)
{
client *c = malloc(sizeof(client));
if (c != NULL)
{
memset(c, 0, sizeof(c))
}
return c;
}