views:

1587

answers:

6

Hello,

Using qsort in C we pass in a comparison function e.g.

int cmp(const void*, const void*);

the protoype of qsort expects a int (* )(const void* , const void*) so we call:

qsort(..., cmp);

but it is equally valid to call:

qsort(..., &cmp);

and this is what we would have to do if we passed in a static member-function in C++. Kernighan & Ritchie (2nd Edition, 5.11 "Pointers To Functions" p119) states that "since [cmp] is known to be a function, the & operator is not necessary, in the same way that it is not needed before an array name."

Does anyone else feel slightly uncomfortable with this (esp. regarding type-safety)?

Cheers, Chris

+6  A: 

Whether you feel uncomfortable or not doesn't change the fact that C is not considered a type-safe language. Case in point:

int main()
{
   int integer = 0xFFFFFF; 
   void (*functionPointer)() = (void(*)())integer; 

   functionPointer(); 

   return 0; 
}

This is completely valid at compile time, but it is obviously not safe.

Jeff Hillman
True, but using that cast is a bludgeon to tell the compiler "trust me; I know what I'm doing, even if you think otherwise".
Jonathan Leffler
+3  A: 

It's a syntax detail. If you don't like not using the & to indicate a function pointer then always use it. If you like saving the extra keypresses and not being as explicit in your coding then take advantage of it.

As regards type safety, I don't see how it makes any difference. The compiler is given a function as an argument and it can work out what the function signature is entirely from the name. The fact that you aren't calling it (with the () syntax) gives all the indication the compiler needs that you want a function pointer in this location. The & is just a syntactic nicety to indicate to humans that they are looking at a pointer of some description.

If you are using C++, then I'd suggest looking at boost functors as a nicer method of passing around functions anyway. These lovely entities allow a unified and fairly clear syntax for all functions in C++ :)

workmad3
+1  A: 

It's not so much a sense of discomfort as a distaste for valid-yet-conflicting syntax options. Either cmp is pointer, and should be treated consistently as such, or it's some other type--which, IMHO is syntactically misleading.

Going a bit further, I'd also require a call either to dereference the pointer (to highlight the fact that it's a pointer) or not (because a function name is a pointer) ... but not allow both.

Function pointers can be extremely powerful, yet they seem to be a source of confusion to both new and experienced programmers alike. Part of the difficulty could be alleviated by requiring a single, meaningful syntax that clarifies their use, rather than obscuring it.

Adam Liss
+3  A: 

I suspect you only feel uncomfortable because you're not used to coding as 'close to the metal' as C allows. The reason why C doesn't require the address-of operator on functions is that there's nothing you can really do with functions other than call them or pass them.

In other words, there's no sensible definition for manipulating the 'value' of a function.

Whereas with ints, you can add to or subtract from the value, this makes no sense for functions.

In any case, C predates both C++ and its own standardization - the original K&R C didn't even have prototypes and ANSI had to make sure that their standardization didn't break existing code as much as possible.

paxdiablo
sure there is. You can do functionptr++ and do bad, bad things to the stack. ;)
Paul Nathan
+1  A: 

Note that the same goes for 'dereferencing' (i.e. calling) function pointers; you don't have to

(*funcptr)(arg1, arg2);

as

funcptr(arg1, arg2);

will suffice.

aib
+2  A: 

Well, the answer is that passing a function by value yields a function pointer, the same way as passing an array by value yields a pointer to its first element. one says that the array and the function "decay". there are only a few occasions where that decay doesn't happen. for example sizeof(array) yields the sizeof the array, not the one of its first element pointer. sizeof(function) is invalid (functions are no objects), you have to do sizeof(&function). Other occasions are binding to references:

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

This, btw all is the reason you can do

template<typename T, int N>
void ByRef(T (&foo)[N]) { 
    ...
}

since the array does not decay yet when considering reference parameters.

Johannes Schaub - litb