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?
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:
An identifier that names a function is the simplest sort of "expression that has function type". So what this means is, if
foohas been declared as a function, the identifierfooevaluates as a pointer to that function, except when it’s the operand of&(in which case the larger expression&fooevaluates as a pointer to that function) or the operand ofsizeof(in which case the larger expression,sizeof(foo), provokes a compile error).tl,dr: When
foois a function,fooand&fooare 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
pfuncis a pointer-to-function variable,(*pfunc)()is processed as if it read(&(*pfunc))()… or justpfunc().