1

当我学习 C# 的委托时,我的书说委托与 C 中的函数指针相同,但更安全。读完那行之后,我想:啊,所以C编译器不会检查函数指针指向的原型。我完全错了。

int add(int a, int b){ return a + b; }
float add_f(float a, float b){ return a + b; }

int (*f)(int,int);
f = add; // no compile-error
f = add_f; //compile-error

所以,请告诉我为什么,并举一些例子来证明 C 函数指针与 C# 中的委托相比是不安全的。

谢谢 :)

4

2 回答 2

4

在 C++ 中,函数指针是类型安全的。你必须做一些非常愚蠢的事情来打破它们(通常涉及铸造、联合、memcpy或类似的东西;它不会偶然发生)。

但是,在 C 中,函数类型没有那么严格。你可以这样做:

int add(int a, int b){ return a + b; }
int add_f(float a, float b){ return a + b; }

int (*f)(); // empty argument list does NOT mean zero arguments.
            // for zero arguments, say int (*)(void)
f = &add;
f = &add_f;

http://ideone.com/Y6mCc


我被问到一个非常愚蠢的例子。 这里是

#include <iostream>

int add(int a, int b){ return a + b; }
int add_f(float a, float b){ return a + b; }

union {
    int (*f1)(int, int);
    int (*f2)(float, float);
} fp;

int main(void)
{
    fp.f1 = &add;
    std::cout << "Expected (1): " << add(1, 3) << std::endl;
    std::cout << "Actual   (1): " << (fp.f1)(1, 3) << std::endl;

    std::cout << "Expected (2): " << add(1.0f, 3.0f) << std::endl;
    std::cout << "Actual   (2): " << (fp.f2)(1.0f, 3.0f) << std::endl;

    fp.f2 = &add_f;
    std::cout << "Expected (3): " << add_f(1.0f, 3.0f) << std::endl;
    std::cout << "Actual   (3): " << (fp.f2)(1.0f, 3.0f) << std::endl;

    std::cout << "Expected (4): " << add_f(1, 3) << std::endl;
    std::cout << "Actual   (4): " << (fp.f1)(1, 3) << std::endl;

    return 0;
}

请注意,这种愚蠢在带有Marshal.Copy.

于 2012-07-09T18:53:04.253 回答
1

你不应该把每本书都说成是表面上的东西:C 的函数指针和 C# 的委托之间的相似之处是相当肤浅的。将它们逐个功能进行比较会产生一份差异清单,而“安全性”将远离该清单的顶部。

使用函数指针模拟 C# 委托的最大安全问题是函数指针操作的数据的所有权。回想一下,委托并不局限于静态函数:您可以从成员函数生成委托,在这种情况下,对象将“嵌入”到委托中。C 中没有类似的功能:您需要做很多工作才能将函数指针“绑定”到一段数据。如果稍后数据变得不可用,并且函数尝试使用该数据,则可能会触发崩溃。但这种差异是垃圾收集与手动管理的内存环境的一个属性。

于 2012-07-09T18:56:16.237 回答