函数指针如何键入不安全

Fra*_* Q. 10 c++

首先,类型安全意味着如果做错了,编译器可以立即捕获的任何内容.

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

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

void SortElements(void* MyArray, unsigned int iNumofElems,size_t size, int(*compare_funct)(void* First,void* SecondElem))
Run Code Online (Sandbox Code Playgroud)

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

int MySortAsc(void* First, void* Second);
void MyFunct2();
void MyFunct3(void* First);
Run Code Online (Sandbox Code Playgroud)

该代码仅编译为:

SortElements(MyArray, 10, sizeof(DataType), &MySortAsc); //Compiles
SortElements(MyArray, 10, sizeof(DataType), &MyFunct2);  //Fails
Run Code Online (Sandbox Code Playgroud)

任何想法我怎么能在这里误用函数指针?

是因为这个:

void (*functionPointer)();
...
int integer = 0xFFFFFFFF;     
functionPointer = (void(*)())integer;      
functionPointer();
Run Code Online (Sandbox Code Playgroud)

答:我要看到的是C++中的函数指针是类型安全的.当然,它们可以通过非常不安全的方式使用它们,但这并不能使它们成为被称为类型不安全的理由..NET委托也是强类型的,对我来说它看起来都是类型安全的.

Dav*_*eas 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?
Run Code Online (Sandbox Code Playgroud)

您提供的代码是类型不安全的,不是因为函数指针,而是因为在函数指针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 );
}
Run Code Online (Sandbox Code Playgroud)

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

与其他替代方案相比:

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

这里编译器将根据调用推断元素的类型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!
Run Code Online (Sandbox Code Playgroud)

你不能误认为数组的大小或元素的大小,如果有人改变了类型Apple,编译器就会把它拿起来,如果数组的大小发生变化,编译器就会把它拿起来.你不能误认为传递给函数的比较器,因为编译器也会把它拿起来.现在该程序是类型安全的,即使它使用函数指针(可能会影响性能,因为它们禁止内联,这就是为什么std::sort通常比它更快qsort)

  • 不,我不会这么说.函数指针是类型安全的.开始强制转换时,任何代码(不限于函数指针)都可以是类型不安全的.这是使得事情不安全的铸件.函数指针绝对没有与类型安全性相关的特殊属性. (5认同)
  • @FrankQ.代码中的类型安全问题来自函数接口的设计,它消除了类型信息,而不是传递函数指针的事实.通过隐式强制转换为"void*"来删除正确的类型意味着在没有编译器的任何帮助来验证正确性的情况下,管理程序员(强制转换)中负责管理内存中每个位的类型.另请注意,之前未提及的是,该函数只能将数据作为位进行管理,这意味着除POD之外的任何其他操作都会导致未定义的行为. (3认同)

wal*_*lyk 6

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


Dav*_*nan 5

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