当我学习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#中的委托相比是不安全的。
谢谢:)
答案 0 :(得分: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;
我被问到一个非常愚蠢的例子。 Here goes:
#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
的C#同样可以实现这种愚蠢。
答案 1 :(得分:1)
你不应该把每本书的内容都用于它的面值:C的函数指针和C#的代表之间的相似性是相当肤浅的。比较它们的特征到功能将产生一个差异的清单,“安全”将远离该列表的顶部。
使用函数指针模拟C#委托的最大安全问题是函数指针操作的数据的所有权。回想一下,委托不限于静态函数:您可以从成员函数生成委托,在这种情况下,对象将“嵌入”委托中。 C中没有类似的功能:你需要做很多工作才能将函数指针“绑定”到一段数据。如果数据稍后变得不可用,并且该函数尝试使用该数据,则可能会触发崩溃。但这种差异是垃圾收集与手动管理的内存环境的一个属性。