Friday, April 29, 2011

How does casting a function actually work in C?

int foo(char *c)  {...}

main() {
     int (*thud)(void *);

     thud = (int (*)(void *))(foo);
}

What actually happens during the evaluation of the assignment?

There is a difference between the cast type and foo; the cast type is a pointer and foo is a function. So, does the compiler convert what's in '(foo)' into a pointer to foo and only then make the cast? Because nothing else seems to make sense; the other option is that the function itself is converted to a pointer to a function that gets a void* and returns an int, and as far as I know a function is a label to a piece of code in memory and thus cannot become a pointer, which is a variable.

From stackoverflow
  • In C, absolutely nothing. It is just compiler glue to prevent you from doing something stupid. In C, the caller is responsible for maintaining the stack frame, so the cast is necessary when you invoke the function (i.e. arguments and the return value are pushed onto the stack). This makes it safe(r) since the caller's stack will not likely be mutated improperly. However, the invoked function can still mess up the caller's stack in certain rare cases.

    I should clarify that the assignment copies the function pointer. But in C, all function pointers are just pointers. The type and casting is all compiler glue.

    Another clarification: The standard specifies (in 6.5.2.2) that the behavior is undefined if the caller uses incompatible types. For example, casting a function which returns void into one which returns an int and then calling that function, the "returned" value is meaningless. It's a good idea to cast the function into a compatible type before calling it, or you may see unexpected results.

    Bastien LĂ©onard : Are you sure the standard guarantee that all function pointers use the same representation?
    Rick C. Petty : The standard only describes the "pointer to function" type. I've seen many programs rely upon this behavior, specifically in Java VMs or similar where all you know upfront is you have a function pointer.
  • The name of a function is a pointer when used as such. It's somewhat similar to how the name of an array is a pointer to its first element.

    That being said, calling a function through a pointer with a different type than the function's actual prototype (as your example does) is undefined behavior. Don't do it.

    Addendum

    If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.

    from section 6.3.2.3 of the C standard.

    Rick C. Petty : That's not actually true. The calling convention in C is well-defined. In fact there are cases where you absolutely must cast the prototype (e.g. variable arguments) and I rely on this often.
    avakar : I'm pretty sure it's not defined in the C standard, especially since it varies from platform to platform.
    Rick C. Petty : I stand corrected. According to 6.5.2.2, "If the function is defined with a type that includes a prototype, and ... the types of arguments after promotion are not compatible with the types of the parameters, the behavior is undefined." *calling* with different types = undefined behavior
    Adisak : FWIW, changing the number of parameters will not work on certain ABI's. For example, the Windows Win32 "stdcall" ABI specifies that the caller pushes values onto the stack but the callee pops the stack when it returns. If the number of parameters varies, you will corrupt the stack pointer by casting a pointer to a function with a different number of paramters than specified in the signature for the function pointer type. If you're going to try to be "clever" doing this, you need to specify functions as "cdecl" on Windows.
  • A pointer in C is an address, i.e. a number stored in some place. A function in C is an address to some code. Those two are one of the same.

    ephemient : The representation of function in C varies by platform. In particular, it is a 8-byte structure containing a code pointer and a data pointer on 32-bit PowerPC -- not just an address.
    ephemient : On PowerPC, a function pointer is a pointer to such a structure. Odd, but I believe it's also true on SPARC and a few other ABIs of RISC architectures.
    Johannes Schaub - litb : a function has type "function". like "function returning int". just like ephemient says, if the value of it is evaluated, it is converted to a "pointer to function", like "pointer to function returning int". like with the array case. this is why *******f; with f being a function works.
  • The correct term is decay. Function foo decays to a pointer to foo before the cast. The cast itself will be a no-op on all platforms I can think of.

    Note, however, that the behavior of a program containing such a cast is undefined by the C standard.

  • This was going to be a comment to Rick C. Petty's answer - but it doesn't fit in 300 characters.

    The C standard is not very restrictive - pointers to objects (and functions aren't objects) can be converted to 'pointer to void' and back without problem. POSIX requires that pointers to functions are all the same size and can be converted to pointer to void.

    POSIX 2008 - General Information - The Compilation Environment

    2.12.3 Pointer Types

    All function pointer types shall have the same representation as the type pointer to void. Conversion of a function pointer to void * shall not alter the representation. A void * value resulting from such a conversion can be converted back to the original function pointer type, using an explicit cast, without loss of information.

    Note: The ISO C standard does not require this, but it is required for POSIX conformance.

0 comments:

Post a Comment