-2

There're already a bunch of related questions such as

Function pointers and address of a function

and

Why do function pointer definitions work with any number of ampersands '&' or asterisks '*'?

But I'm curious about the reason of why it's like so, rather than simply memorizing all those rules.

Below is a trivial C code snippet and corresponding arm assembly code:

int foo() {
    return 42;
}

int main() {
    int (*bar)() = foo; // same assembly code if swapping with '&foo' 
    int a, *b;
    a = foo();
    *b = bar();
}

foo:
        str     fp, [sp, #-4]!
        add     fp, sp, #0
        mov     r3, #42
        mov     r0, r3
        add     sp, fp, #0
        ldr     fp, [sp], #4
        bx      lr
main:
        push    {fp, lr}
        add     fp, sp, #4
        sub     sp, sp, #16
        ldr     r3, .L5
        str     r3, [fp, #-8]
        bl      foo
        str     r0, [fp, #-12]
        ldr     r3, [fp, #-8]
        blx     r3
        mov     r2, r0
        ldr     r3, [fp, #-16]
        str     r2, [r3]
        mov     r3, #0
        mov     r0, r3
        sub     sp, fp, #4
        pop     {fp, pc}
.L5:
        .word   foo

As can be seen in ldr r3, .L5 the function name is presented as a label which is equivalent to address of code block, if I swap foo with &foo, the assembly code is still the same, because foo has no storage address. But pointer b and the value it points to have their dedicated storage. Therefore it's reasonable to not distinguish foo and &foo but necessary for b and *b.

However, I can't find any solid reference to support my hypothesis; all the answers I've found are merely describing the phenomenon rather than explaining the reason?

mzoz
  • 1,273
  • 1
  • 14
  • 28
  • 3
    What could you do with the function as object in C, except directly calling it? – Ruslan Feb 27 '20 at 06:55
  • 1
    `b` and `*b` are an `int *` and `int` respectively; `bar` is the pointer to function. But `bar` is a pointer variable, like `b` — it has storage. In contrast, `foo` is an address constant, like the name of an array is an address constant. It decays to a pointer to function when referenced. There's no need to remember much in the way of rules. Repeated referencing or dereferencing of a function is silly (no multiple `*` or `&`). I still like `(*pointer)(arg)` to indicate I'm using a pointer to function, but `pointer(arg)` works fine too. There's no need to use `&` on a function. – Jonathan Leffler Feb 27 '20 at 07:03
  • @JonathanLeffler Hi John I just added a comment in the code to make my intention more clear, so if I swap `foo` with `&foo`, the assembly code is still the same, because `foo` has no storage address, I wonder if that's the reason of `foo` being equivalent to `&foo`? – mzoz Feb 27 '20 at 07:11
  • It does not matter what a single compiler creates from your code. They are not the same because a compiler doesn't allocate memory. It is the other way around. The compiler doesn't need to allocate memory because the C standard tells us they are the identical. – Gerhardh Feb 27 '20 at 07:14
  • The name of a function like `foo` maps to the address at which it is found; the only reasonable interpretation for `&foo` is the address at which it is found. Yes, they're the same. Outside of the function call notation `(*pointer)(arg)` (where the pointer could be a function name, but that would be silly), there's no reason to dereference a function pointer. Basically, there are just two things you can do with functions; get their address and call them. – Jonathan Leffler Feb 27 '20 at 07:54
  • I think the reason why functions "decay" into function pointers is that they should behave like arrays do. As for arrays, we know the reason they decay is that Ritchie wanted to change the way that the predecessor languages B/BCPL stored an address together with the array - something like that. I don't remember all the details but you can read about it here: https://www.bell-labs.com/usr/dmr/www/chist.html – Lundin Feb 27 '20 at 08:00
  • What's a function value? Can you allocate a function on the stack or on the heap? Can you assugn a function? Can you compare two functions? – n. m. could be an AI Feb 27 '20 at 11:54
  • @Ruslan then why `&foo` still compiles, what's the semantics of that, address of a non-object? – mzoz Feb 27 '20 at 12:16
  • @mzoz `foo` decay to pointer is implicit. `&foo` is explicitly an address from the beginning. They are equivalent. I don't know of any context where they'd be different in standard C. I guess this explicit syntax is allowed for syntactic symmetry with taking address of variables, and the same with arrays. – Ruslan Feb 27 '20 at 13:16

1 Answers1

0

There is no alternative to learning "all those rules", I'm afraid (if by "all those rules" you mean the single rule that there is no need to include the ampersand when obtaining a pointer to a named function).

This is a language design decision, based on the fact that if you wish to refer to a function without calling it, a pointer is the only way to do so and so the ampersand can be inferred (and a similar argument holds for arrays). Questioning this design decision is as legitimate as questioning any other design decision but is unlikely to get you to any kind of profound truth.

Additionally, the comment above that the output of any given compiler is irrelevant is absolutely true - this is about the language specification, not about any given implementation of it.

cooperised
  • 2,404
  • 1
  • 15
  • 18
  • The problem is if we treat function as 'callable' only (which they're supposed to be, though) then expression like `&foo` should not pass compilation at all because it makes no sense but confusion. – mzoz Feb 27 '20 at 12:12
  • You appear to be the only one here who sees any confusion! If you want to use an ampersand, to make your code read the same as when you take the address of a variable, then use it. The design decision is simply that the ampersand should be optional because `int (*bar)()=foo;` can't possibly mean anything else. – cooperised Feb 28 '20 at 09:37