0

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?

Community
  • 1
  • 1
benzado
  • 82,288
  • 22
  • 110
  • 138
  • I don't think this applies to C in general since it'd be dependent on the shared library implementation. For statically linked libraries, you'd simply have linker errors. – jamesdlin Oct 12 '10 at 02:56
  • Fair enough, I've edited the question. – benzado Oct 12 '10 at 04:58

1 Answers1

2

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().

Community
  • 1
  • 1
zwol
  • 135,547
  • 38
  • 252
  • 361
  • So you're saying `BrandNewFunc` is the same as `&BrandNewFunc`? And how are arrays like that? `BrandNewIdentifier` is the address of the first byte; `&BrandNewIdentifier` is the address of the address of the first byte. – benzado Oct 12 '10 at 04:48
  • 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 `&BrandNewIdentifier` if `BrandNewIdentifier` is an array; that's not so for functions, the types of `BrandNewFunc` and `&BrandNewFunc` are the same if used in a context that expects a pointer-to-function. – zwol Oct 12 '10 at 05:14
  • 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 Oct 12 '10 at 06:45
  • 1
    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. – zwol Oct 12 '10 at 21:15
  • Aha! So function identifiers are the exception. To be consistent I should be using &BrandNewFunc when I check, also. Thanks! – benzado Oct 12 '10 at 23:32