函数指针如何类型不安全 [英] How are function pointers type unsafe

查看:639
本文介绍了函数指针如何类型不安全的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



现在,我听说函数指针不是类型安全的,不管什么时候我试图使用他们不正确的编译器报告错误为我。



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

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

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

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

代码只能编译:

  SortElements(MyArray,10,sizeof(DataType),& MySortAsc); //编译
SortElements(MyArray,10,sizeof(DataType),& MyFunct2); //无效

任何想法如何在这里误用函数指针?



这是因为:

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

答案:
我可以看到的是,C ++中的函数指针是类型安全的。当然,他们可以不安全的方式通过铸造它不切实际,但不使他们成为被称为类型不安全的理由。 .NET代表也是强类型,对我来说它看起来都是类型安全的。

解决方案


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




  void SortElements(void * MyArray, 
unsigned int N,//是否真的有N个元素?
size_t size,//大小是否正确?
int(* cmp)(void *,void *)); //这是正确的函数吗?

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



这是不安全的原因是因为调用者完全有责任传递正确的参数,编译器不能确保指针 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)
}
int main(){
Apple apples [10];
SortElements(apples,20,sizeof(Pear),compare_pears);
}

虽然编译器能够验证函数指针的签名是否匹配



与其他选项进行比较:

 模板< 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); //错误!
SortElements(array,compare_apples); //好!

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


First of all type-safe means that anything that a compiler can catch straight away if done incorrectly.

Now, I heard function pointers are not type safe however whenever I tried to use them incorrectly the compiler did report errors for me. So, how is it type unsafe ?

E.g This is a function prototype that takes in a function pointer

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

I have defined few functions to pass to it as:

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

The code only compiles for:

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

Any idea how can I mis-use function pointers here ?

Is it because of this:

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

Answer: What I got to see is that function pointers in C++ are type safe. Ofcourse, they can be used in an unsafe manner by casting it incorectly but that does not make them a reason to be called as type unsafe. .NET delegates are strongly typed as well and to me it looks like both are type safe.

解决方案

So, how is it type unsafe ?

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?

The code that you present is type-unsafe, not because of the function pointer but rather because of the use of void* in both the SortElements signature and the signature of the function pointer.

The reason why this is unsafe is because the caller has the whole responsibility of passing the right arguments, and the compiler cannot ensure that the pointer MyArray points to a contiguous memory region that holds iNumofElems each of which has the size offered in the interface. If the programmer makes a mistake, the compiler will not be able to help there, if a maintainer modifies the type stored in the array (size changes) or the number of elements, the compiler will not be able to detect it and tell you that you need to update the call to SortElements. Finally, because the function pointer that is passed also uses void*, the signature of a comparator that compares apples and pears is exactly the same, and the compiler cannot help if you pass the incorrect function pointer.

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 );
}

While the compiler is able to verify that the signature of the function pointer matches the signature that the function needs, the function pointer itself is unsafe, and allows you to pass a comparator for basically anything.

Compare that with this other alternative:

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

Here the compiler will infer the type of the elements T and the size of the array N from the call. There is no need to pass the size of T, as the compiler knows it. The comparator function passed to this version of SortElements is strongly typed: it takes two constant references to the type of the element stored in the array and returns an int. If we tried this in the previous program:

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!

You cannot mistake the size of the array or the size of the elements, if someone changes the type Apple, the compiler will pick it up, if the size of the array changes, the compiler will pick it up. You cannot mistake the comparator that is passed to the function as the compiler will also pick it up. Now the program is type safe, even if it uses function pointers (that might have an impact in performance as they inhibit inlining, which is why std::sort is usually faster than qsort)

这篇关于函数指针如何类型不安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆