它是安全是传递函数指针和比较他们就像正常的对象的指针?(is it safe to pass fun

2019-07-04 05:37发布

这里是东西,我有几个功能,

void foo() {}
void bar() {}

我想通过周围这些功能就像普通对象的指针,

int main()
{
    void (*fptr1)() = foo;
    void (*fptr2)() = fptr1;
    void (*fptr3)() = bar;

    if (fptr1 == foo)
        printf("foo function\n");
    if (fptr2 == foo)
        printf("foo function\n");
    if (fptr3 == foo)
        printf("foo function\n")
}

我可以使用这些函数指针这种方式? 我写了一个程序来测试它,好像ok了。 此外,我认为,不喜欢它可能存在于普通的对象stackheap ,功能驻留在text segment (右?),所以当我指的是foo ,它让我在哪个函数的物理地址foo在于文本段?

跟进

如果我确实有DLL工作,考虑一下:第一,功能PTR fptr分配的功能,

ReturnType (*fptr)(ArgType) = beautiful_func;

在此两种情况下,

1)如果beautiful_func不是DLL,则可以安全使用此fptr

2)如果是在DLL,再后来,我认为这将是不安全的使用fptr ,因为它现在可以指的是完全不同的函数,而不是fptr出生了,对不对?

Answer 1:

您可以检查两个函数指针相等只是通过简单的==他们,因为他们都只是正常的指针。 这是显而易见的。

但是,当你说“比较”,请检查你真的在你的心中:

  • 你有兴趣检测你被赋予不同的“事”
  • 或者你有兴趣检测你被赋予不同的功能?

比较指针(!不仅函数指针它适用于所有的人)是一个有点冒险:你不检查的内容(逻辑标识),但只是位置(“物理”的身份)。 大多数时候,它确实是相同的,但有时,要小心,你偶然发现的副本。

很明显,如果你创建的数字1,2,3,4的数组,然后分配另一个阵列和复制的内容有,那么你会得到两个不同的指针,对不对? 但是,该阵列可以是同样的话,这取决于你需要什么。

函数指针的问题是一样的,甚至更多: 你真的不知道什么编译器/连接器已与您的代码来完成。 它可能opitmized几件事情,它可能已经合并了一些未导出的功能结合在一起,如果注意到他们平等的,它可能复制或内联等。

尤其是可以与更大的独立的“子项目”工作时发生。 想象一下,你写了一个排序函数,然后包括它的子项目A和子项目B,编译/构建的一切,然后链接和运行。 你愿意和一个排序函数或两个结束? 难以回答的问题,直到你真正检查并正确地定制链接选项。

这比数组更复杂一点。 使用数组,你有不同的指针,如果数组是不同的。 在这里,同样的功能可以有许多不同的地址。 在C ++模板工作时,这可能是特别明显,但是这又依赖于连接器如何做他的工作。呵呵,很好的例子:DLL文件。 基于类似的码连续3个DLL文件,他们几乎都guaranteeded有它们静态链接到所有的三个副本。

而talkig约DLL时......你知道,他们可以加载/卸载额外的代码到你的记忆,对不对? 这意味着,当你加载DLL,在一些地址XYZ出现的功能。 然后,你卸载它,它就会消失。 但是,当你现在加载不同的dll? 当然,操作系统被允许再利用的空间,并允许新加载的DLL映射到同一区域作为前一个。 大多数时候,你不会注意到它,因为新加载的DLL将被映射到不同的区域,但它可能发生

这意味着,虽然你可以比较指针,你得到的唯一答案是:是的指针相同或不?

  • 如果它们不一样,那么你根本不知道 ; 不同的函数指针并不意味着功能是不同的。 也许是这样,这将是在这样的情况下99%,但并不一定是不同的

  • 如果它们是相同的:

    • 如果你不加载/卸载各种动态库大量的时间,你可能会认为,没有什么变化,你可以肯定得到了相同的功能/对象/数组作为前

    • 如果您正在使用卸载的动态模块的工作,你最好不要假定所有,除非你有绝对的把握, 没有一个三分球都来自将在未来被卸载的DLL。 请注意,一些图书馆使用“插件式”功能的动态库。 小心从它们的指示,并留意插件加载/卸载通知。 当一个动态库被卸载你的函数可能更改。

编辑要跟帖:

除非您(或您的一些使用库)曾经卸载 DLL,然后将鼠标指针到功能-即的目标-A-DLL可以放心使用。

一旦加载DLL时,可以改变这个DLL已地址的意义只有邪恶的东西正在卸载的动态模块

如果你确信:

  • (1)无论是指针到功能不靶向从动态模块的功能(仅指向静态链接代码)
  • (2)或它的目标的动态模块,而是动态模块被从未卸载 (OK:直到程序退出或崩溃)
  • (3)或它的目标是一个动态的模块,而你恰恰知道哪一个,而动态模块在运行时有时卸载 ,但你的代码获取一些“事先通知”有关事实

那么你的函数指针是安全的存储和使用,并比较,只要你添加一些安全措施:

  • 对于(1),不需要安全措施:该功能将不会被替换
  • 对于(2),不需要安全措施:功能要等到程序退出
  • 对于(3)下, 需要采取措施:您一定要听这些通知,一旦你收到通知DLL被卸载, 必须immediatelly忘记 ,目标DLL所有的指针。 你还在为安全要记住任何其他。 你仍然安全地重新想起它时,它被再次加载。

如果你怀疑你的指针到函数的作用目标从将在某个时间点被卸载动态模块的功能程序退出之前,并且:

  • 你真的不知道该DLL由指针指向
  • 或者DLL将在任何时候卸载 ,恕不另行通知

那么你的函数指针是不安全的,在所有使用 。 并通过在所有我的意思是任何方式都没有。 不要保存它,因为它可以瞬间立即蒸发。



Answer 2:

它给我在这函数foo在于文本段的物理地址?

除非你是一个原始的或任何其他特殊OS工作,NO!

地址是不是身体,他们是虚拟地址 !

基本上没有通过操作允许比物理存储器甚至更大的程序系统中所采用的机制。 所以OS做处理幕后的映射工作。

很抱歉,如果我让你感到困惑。 你的理解是正确的 (这是完全没有问题的,你使用的是他们的方式来使用函数指针),但地址不是物理地址(这是指使用,你的主内存其实是解决数字)。



Answer 3:

是的,C标准可以让你的函数指针与运营商比较==和=,例如,从C11 6.5.9!

两个指针相等比较,当且仅当两者都是空指针,两者都指向相同的对象(包括一个指向对象的指针,并在其开头的子对象)或功能,

正是一个函数所在取决于您的平台,它可能在一个文本段,或者它可能是别的地方。 当在与操作系统上运行的虚拟内存 ,该地址是正常的虚拟地址,而不是物理存储器地址。



Answer 4:

考虑到一个事实,即指针存储比内存地址,它归结为一个数字,是的,你可以比较艾德里安的方式。 至于其他的问题,取自这里 ,文本段将被定义为,意思是“在目标文件中或在存储器中的程序,其包含可执行指令。的区段中的一个”,即指针应在containt某处的地址文本段。



Answer 5:

是的,你可以使用这种方式。 而你的理解是正确的。



文章来源: is it safe to pass function pointers around and compare them just like normal object's pointers?