From the C17 standard draft (6.3.2.1 ¶4; footnote removed):
A function designator is an expression that has function type. Except when it is the operand of the
sizeof
operator, 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".
sizeof f
(for a function designator f
) is non-standards-conformant._Alignof
operator.typeof
operator.As a result (here: f
is a function designator; fp
is a function pointer):
&f
(explicit) or f
(short).(&f)(args)
or f(args)
fp(args)
or (*fp)(args)
Here,
(&f)(args)
/fp(args)
is consistent with function calls technically operating on function pointers, andf(args)
/(*fp)(args)
is consistent with the naive assumption that function calls operate on functions (instead of pointers to functions).Stylistically,
&f
as "always redundant",(&f)(args)
is virtually unused in practice,f
/f(args)
/(*fp)(args)
is the style used in K&R (2e), andf
/f(args)
/fp(args)
is the shortest style.(For completeness' sake (and in case anyone else wants a nice summary), here is a longer list of expressions that are equivalent to f
: (&f)
, (*******f)
, (***&***f)
, (***&**&f)
, (&**&***f)
, (&**&**&f)
.)
But:
When is it ever necessary or meaningful to
&f
(where f
is a function designator) or*fp
(where fp
is a function pointer)?(By "meaningful", I mean that the behavior of the program is altered.)
sizeof &f
. (sizeof f
is illegal, even though the GCC dialect of C permits it (at least as of version 12.2.0), but with different semantics: It seemingly returns 1
in all(?) cases. Why?)
f
auto-convert to &f
would let us simplify the standard's phrasing and give sizeof f
useful semantics too. (Perhaps this restriction exists to maintain parallelism with typeof
, which has been floating around as a compiler extension for a while.)_Alignof
, the committee had made a mistake in C11, so they rectified it in C17. Basically: Unlike sizeof
, _Alignof
only accepts a parenthesized type as its argument, but not an expression (C11 draft, 6.5.3 ¶1). See also this post on SO.typeof
is officially added to C with the next standard, a distinction between typeof &f
/typeof fp
and typeof f
/typeof *fp
would be obviously meaningful.Since a function designator f
is automatically converted to the address of the function in many situations, the only times when f
is semantically different from &f
is when it is not converted. Per C 2018 6.3.2.1 4, these are when it is the operand of sizeof
or unary &
. Thus we have:
sizeof f
requires a diagnostic message because it violates the constraints in C 2018 6.5.3.4 1 (sizeof
shall not be applied to an expression with function type) and its behavior is not defined by the C standard (because of the constraint violation), whereas sizeof &f
yields the size of a pointer to the function. (sizeof f
is nonetheless conforming code per C 2018 4 7, albeit not strictly conforming, since there exists a C compiler that accepts it.)&f
yields the address of f
whereas & &f
requires a diagnostic message because it violates the constraints in C 2018 6.5.3.2 1 (the operand of unary &
shall be a function designator, the result of array subscripting or unary *
, or an lvalue other than a bit-field or one declared with register
). I am unaware of a C compiler that accepts & &f
, so this is not strictly conforming code, except to the extent we could imagine and construct a C compiler that accepts it. (The space in & &f
serves to separate the tokens. Otherwise &&
is a single token, for the logical AND operator.)My guess at why GCC accepts sizeof f
is that it is part of allowing address arithmetic on pointers generally, even void *
and pointers to functions, to support code used in operating systems, debuggers, program loaders, and similar software that operates on functions and arbitrary memory. Making sizeof f
be 1 is a part of designing GCC’s arithmetic on function pointers to operate in units of bytes. (This is an unnecessary feature of GCC because we could support arithmetic on pointers to functions and void *
by requiring they be first converted to a pointer to a character type. However, I suppose it was done to support code practices at the time it was done and is now legacy.)
At a syntax level, &f
differs from f
in being longer, so it could run afoul of environmental limits of the compiler. For example, C 2018 5.2.4.1 1 requires a compiler support 4095 characters in a logical source line (in at least one program), so changing f
to &f
could push the line over the length that a compiler supports.
With *fp
versus fp
, similar reasoning applies: *fp
is converted to the equivalent of fp
in many situations, so semantic differences arise only when it is not. So sizeof *fp
and sizeof fp
differ as discussed above. However, for &
, &*fp
is the address of the function, whereas &fp
is the address of the pointer. (This supposes that fp
is an object, not some other expression that is not an lvalue. If it is not an object, it raises the same issues as discussed for &
above.)
And *fp
versus fp
has the same environmental limit consideration as &f
versus f
.