views:

39

answers:

1

I ran into this problem while developing in Objective-C for iOS, but this should apply to any C/C++/Objective-C code using the Mac OS X/iOS linker. The solution is covered by another question, but I'm interested in the why.

Let's say I'm using a linking to a library which defines a constant. In a header file there is a declaration like this:

extern char * const BrandNewIdentifier;

I want to compile my application and run it on a system with an earlier version of the library, where that constant has no definition, so to be safe I don't assume it has been defined.

Now, if there's a function that is only defined in the most recent version of the library, I can do this:

if (BrandNewFunc) {
    BrandNewFunc("foobar", 42);
} else {
    printf("Your system does not support some thing.");
}

Instead of containing the address of function code, BrandNewFunc evaluates to NULL. I would think that the constant would behave the same way, but if I try the same pattern, the app dies while performing a check (throws EXC_BAD_ACCESS on iOS). Specifically:

if (BrandNewIdentifier) ... // blows up here

What works instead is checking the address of the identifier:

if (&BrandNewIdentifier) {
    printf("You got it!");
}

I can see the logic: BrandNewIdentifier has no value, so accessing it should fail. But then why does that syntax work in the case of BrandNewFunc? Shouldn't I be required to check its address also? Or is it actually consistent, and there is something I've overlooked?

+2  A: 

The relevant part of the C standard is section 6.3.2.1 "Lvalues, arrays, and function designators". Here's what it says about functions:

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator65 or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.

[footnote 65] Because this conversion does not occur, the operand of the sizeof operator remains a function designator and violates the constraint in 6.5.3.4 [ed: the constraint in 6.5.3.4 says that you may not apply sizeof to a function designator - it's a semantic error].

An identifier that names a function is the simplest sort of "expression that has function type". So what this means is, if foo has been declared as a function, the identifier foo evaluates as a pointer to that function, except when it's the operand of & (in which case the larger expression &foo evaluates as a pointer to that function) or the operand of sizeof (in which case the larger expression, sizeof(foo), provokes a compile error).

tl,dr: When foo is a function, foo and &foo are equivalent by definition. This is a special rule for functions. It's not entirely unlike the special rule for arrays, which also "decay" to pointers in many contexts (that rule is one paragraph up from the one I quoted).

Aside: Yes, this means that the function-call operator always operates on a pointer-to-function. When pfunc is a pointer-to-function variable, (*pfunc)() is processed as if it read (&(*pfunc))() ... or just pfunc().

Zack
So you're saying `BrandNewFunc` is the same as `
benzado
All I mean is, arrays do "decay" to pointers, and so do functions; you can't pass either of them by value. It's true that the type of `BrandNewIdentifier` is not the same as the type of `
Zack
Can you edit your question to explain what you mean by "context"? As far as I know, expressions in C don't change meaning based on context, that's for crazy languages like Perl.
benzado
I've edited my answer. For the most part, your 'as far as I know' is correct, but there are special rules for arrays and functions, and for that matter, the "usual arithmetic conversions" could be understood as changing the meaning of an expression based on context, too.
Zack
benzado