10

首先,类型安全意味着编译器如果执行不正确可以立即捕获的任何内容。

现在,我听说函数指针不是类型安全的,但是每当我尝试错误地使用它们时,编译器都会为我报告错误。那么,它的类型如何不安全?

例如,这是一个函数原型,它接受一个函数指针

void SortElements(void* MyArray, unsigned int iNumofElems,size_t size, int(*compare_funct)(void* First,void* SecondElem))

我已经定义了几个函数来传递给它:

int MySortAsc(void* First, void* Second);
void MyFunct2();
void MyFunct3(void* First);

该代码仅编译用于:

SortElements(MyArray, 10, sizeof(DataType), &MySortAsc); //Compiles
SortElements(MyArray, 10, sizeof(DataType), &MyFunct2);  //Fails

知道如何在这里误用函数指针吗?

是不是因为这个:

void (*functionPointer)();
...
int integer = 0xFFFFFFFF;     
functionPointer = (void(*)())integer;      
functionPointer();

答:我看到的是 C++ 中的函数指针是类型安全的。当然,它们可以通过不正确地转换来以不安全的方式使用,但这并不使它们成为被称为不安全类型的理由。.NET 委托也是强类型的,在我看来两者都是类型安全的。

4

4 回答 4

10

那么,它的类型如何不安全?

void SortElements(void* MyArray,              // what type is pointed here?
                  unsigned int N,             // Are there really N elements?
                  size_t size,                // Is the size correct?
                  int(*cmp)(void*,void*));    // Is this the correct function?

您提供的代码是类型不安全的,不是因为函数指针,而是因为在签名和函数指针的签名void*中都使用了。SortElements

这是不安全的原因是因为调用者有传递正确参数的全部责任,并且编译器不能确保指针MyArray指向一个连续的内存区域,该内存区域包含接口中提供的iNumofElems每个内存区域size。如果程序员犯了错误,编译器将无法提供帮助,如果维护者修改了存储在数组中的类型(大小更改)或元素数量,编译器将无法检测到并告诉您您需要将调用更新为SortElements. 最后,由于传递的函数指针也使用void*,所以比较苹果和梨的比较器的签名完全相同,如果传递了错误的函数指针,编译器将无法提供帮助。

struct Apple {
   int weight;
};
struct Pear {
   double weight;
};
int compare_pears( void * pear1, void * pear2 ) {
   return static_cast<Pear*>(pear1)->weight - static_cast<Pear*>(pear2)->weight;
}
int main() {
   Apple apples[10];
   SortElements( apples, 20, sizeof(Pear), compare_pears );
}

虽然编译器能够验证函数指针的签名是否与函数所需的签名匹配,但函数指针本身是不安全的,并且允许您为基本上任何事情传递比较器。

将其与其他替代方案进行比较:

template <typename T, std::size_t N>
void SortElements( T (&array)[N], int (*cmp)( T const &, T const & ) );

在这里,编译器将从调用中推断元素的类型T和数组的大小。正如编译器所知,N不需要传递 的大小。T传递给这个版本的比较器函数SortElements是强类型的:它采用两个常量引用存储在数组中的元素的类型,并返回一个int. 如果我们在之前的程序中尝试过:

int compare_pears( Pear const & lhs, Pear const & rhs );
int compare_apples( Apple const & l, Apple const & r );
Apple array[10];
//SortElements( array, compare_pears );   // Error!!!!
SortElements( array, compare_apples );    // Good!

你不能弄错数组的大小或元素的大小,如果有人改变了类型Apple,编译器会选择它,如果数组的大小改变了,编译器会选择它。您不能误认为传递给函数的比较器,因为编译器也会拾取它。现在程序是类型安全的,即使它使用函数指针(这可能会影响性能,因为它们禁止内联,这就是为什么std::sort通常比 快qsort

于 2012-04-20T00:38:08.310 回答
6

函数指针是类型安全的。然而,许多环境迫使程序员需要重铸它们。不正确的铸造可能会导致严重的问题。

于 2012-04-19T22:41:58.830 回答
5

函数指针实际上是经过类型检查的并且是类型安全的。

于 2012-04-19T22:40:33.867 回答
1

在 nesC(TinyOs 中使用的 C 方言)中强烈建议不要使用函数指针,因为它们会阻碍优化。在这里,静态代码分析(或者更确切地说是缺乏其适用性)是比类型安全更大的问题,但我不确定这些问题是否会被混淆。

另一个问题可能是使用函数指针作为事件处理程序。使用通用事件调度程序时,您可能希望从正确的类型中抽象出来,这意味着您可以考虑存储函数指针,void*以实现模块化。这将是函数指针的类型不安全使用而不是类型安全的动态绑定使用的一个突出示例。

于 2012-04-19T22:49:35.287 回答