Checking for the existence of externally defined identifiers in C
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:
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 applysizeof
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()
.
精彩评论