C – 地址运算符中的函数指针“不必要”
在C中使用qsort我们传递比较函数,例如
int cmp(const void*, const void*);
qsort的protoype期望一个int(*)(const void *,const void *)所以我们调用:
qsort(..., cmp);
但同样有效的是:
qsort(..., &cmp);
如果我们在C ++中传入静态成员函数,这就是我们必须要做的。 Kernighan&Ritchie(第2版,5.11“Pointers To Functions”p119)声明“因为[cmp]已知是一个函数,所以&运算符不是必需的,就像在数组名称之前不需要它一样。 “
是否有人对此感到有点不舒服(特别是关于型号安全性)?
无论您是否感到不舒服,都不会改变C不被视为类型安全语言的事实。 例证:
int main() { int integer = 0xFFFFFF; void (*functionPointer)() = (void(*)())integer; functionPointer(); return 0; }
这在编译时完全有效,但显然不安全。
嗯,答案是按值传递函数会产生一个函数指针,就像通过value传递数组一样,会产生一个指向其第一个元素的指针。 一个人说arrays和function“衰变”。 只有少数情况下不会发生衰变。 例如sizeof(array)产生数组的大小,而不是它的第一个元素指针之一。 sizeof(函数)无效(函数不是对象),你必须做sizeof(&function)。 其他场合绑定参考:
void baz(); void foo(void (&bar)()) { bar(); } // doesnt work, since a reference to a function is requested. // you have to pass 'bar' itself, without taking its address // explicitely. foo(&baz);
这个,顺便说一句就是你能做到的
template void ByRef(T (&foo)[N]) { ... }
因为在考虑参考参数时arrays还没有衰减。
我怀疑你只是觉得不舒服,因为你不习惯像C允许的那样编码’接近金属’。 C不需要函数地址运算符的原因是除了调用它们或传递它们之外,没有什么可以用于函数。
换句话说,没有合理的定义来操纵函数的“价值”。
使用整数,您可以添加或减去该值,这对函数没有意义。
在任何情况下,C都早于C ++和它自己的标准化 – 原始的K&R C甚至没有原型,ANSI必须确保它们的标准化不会尽可能地破坏现有代码。
这是一个语法细节。 如果您不喜欢不使用&来指示函数指针,那么请始终使用它。 如果你喜欢保存额外的按键而不是在你的编码中那么明确,那就利用它。
至于类型安全,我看不出它有什么区别。 编译器被赋予一个函数作为参数,它可以从名称中找出函数签名的全部内容。 你没有调用它(使用()语法)的事实给出了编译器需要你在这个位置需要函数指针的所有指示。 &只是一种语法精确,向人类表明他们正在寻找一些描述的指针。
如果您正在使用C ++,那么我建议将boost仿函数看作是一种更好的传递函数的方法。 这些可爱的实体为C ++中的所有函数提供了统一且相当清晰的语法:)
对于有效但有冲突的语法选项的厌恶并不是一种不适感。 要么cmp
是指针,要么应该一致地对待它,或者它是其他类型 – 恕我直言,语法错误。
更进一步,我还需要调用取消引用指针(以突出显示它是指针的事实)或不(因为函数名称是指针)…但不允许两者。
函数指针可以非常强大,但它们似乎是新手和有经验的程序员的混淆源。 部分难度可以通过要求单一,有意义的语法来澄清其使用而不是模糊它来缓解。
请注意,“解除引用”(即调用)函数指针也是如此; 你不必
(*funcptr)(arg1, arg2);
如
funcptr(arg1, arg2);
就足够了。