2

I have test the following code:

#include <stdio.h>
void f(void g()) {
    g();
}

void g(void) {
    printf("hello, world\n");
}

int main() {
    f(g);
    return 0;
}

It works well. In the code above, why in c we can take the function name g as paramter and take the function prototype as parameter?

In book K&R, it says that we can use the pointer to function: So I can define the function f like this:

void f(void (*g)()) {
    g();
}
void g(void) {
    printf("hello, world\n");
}

int main() {
    void (*pf)() = g;
    f(pf);
    f(g);   // Here f(g) also works.
    return 0;
}

So in the first f definition form, the parameter prototype is void g(), in the second form the parameter prototype is void (*g)(). Why the two forms of definition of f are the same?

And in the second form, the parameter is a pointer to function, why f(g) also works?

zhenguoli
  • 2,268
  • 1
  • 14
  • 31
  • Function names are converted to function pointers in most situations. – Ian Abbott Mar 30 '17 at 09:11
  • In other words, we can use function names as pointers in most situations, so can I say *there is no need to define pointer to functions*? Then I can use function names as other languages directly. – zhenguoli Mar 30 '17 at 09:13
  • @zhenguoli: It is like `void f(int a[])`, where `a` will actually be of type `int *`, not `int[]`. – Blagovest Buyukliev Mar 30 '17 at 09:15
  • No you can't say that. – n. m. could be an AI Mar 30 '17 at 09:20
  • @n.m. Why? Any reason or the advantage of pointer to function? – zhenguoli Mar 30 '17 at 09:23
  • Btw you should stop reading K&R, it is a bad book which is already teaching you lots of bad things, such as declaring functions as `void func()` rather than `void func (void)`, or as `main()` rather than `int main (void)`. Meaning that some C programmer will have to unteach you all the K&R crap later on, big waste of everyone's time. Read a modern book instead. – Lundin Mar 30 '17 at 09:27
  • Reductio ad absurdum. If we could just ignore function pointers, the creators of C that added rhem to the language, and all the people who have used this language feature, would look like complete idiots. Statistically, not all of them may be complete idiots, therefore we must use function pointers for *some* reason. – n. m. could be an AI Mar 30 '17 at 09:30
  • Direct demonstration. There are no variables of function type. They are explicitly forbidden by the language standard. The only way to have a variable that refers to a function is to declare a function pointer. – n. m. could be an AI Mar 30 '17 at 09:33
  • @Lundin, in the 2nd version K&R, there has said that "if the function takes arguments, declare them; if it takes no arguments, use `void` with new programs". But can you recommend me a book about c as a reference? Thanks. – zhenguoli Mar 30 '17 at 09:35
  • @n.m. I don't believe K&R were complete idiots, they just had poor knowledge of the C90/ANSI-C language. For example code such as `main() { printf("hello, world\n"); }` is not correct for any version of the C language. It invokes undefined behavior in C90 and will not even compile in modern C. If you have undefined behavior in the very first code example in your C programming book, well... maybe you shouldn't be writing such books. – Lundin Mar 30 '17 at 09:35
  • @zhenguoli There's some book recommendation list somewhere on SO. Most beginner-level C books published are rather bad, unfortunately. – Lundin Mar 30 '17 at 09:36
  • @Lundin We can excuse them for that. It would take some effort to learn C90 back in 1972. – n. m. could be an AI Mar 30 '17 at 09:40
  • @Lundin besides, implicit int or () are not UB in C90. – n. m. could be an AI Mar 30 '17 at 09:44
  • `()` is not UB in any version of C. – JeremyP Mar 30 '17 at 10:00
  • @n.m. This is from the 2nd edition which was supposed to cover C90 and which was published after the release of C90. **It is undefined behavior in C90 because it is a function that returns an `int` but lacks a return statement**. – Lundin Mar 30 '17 at 10:07
  • @JeremyP some people believe specifically `main()` is UB, because the standard says it should only be `(void)` or `(int argc, char* argv[])` or "equivalent". I do not because I believe the intent of the standard is that `int main()` is equivalent to `int main(void)` for purposes of that passage. – n. m. could be an AI Mar 30 '17 at 10:08
  • @n.m. No that's not why, read my previous comment. – Lundin Mar 30 '17 at 10:09
  • @Lundin no it is not. It is only undefined if the return value is subsequently used, 3.6.6.4. – n. m. could be an AI Mar 30 '17 at 10:11
  • @Lundin It looks like you believe that C89/C90 invalidated 99% of pre-standard C code. It didn't. That was not the intention of the standard. Quite the opposite is true. – n. m. could be an AI Mar 30 '17 at 10:17
  • @zhenguoli K&R is still the best C book out there, just a bit dated. Be aware that you shouldn't be using the bare `main()`. Write `int main(void)` and use explicit `return return_code` or `exit(return_code)`. – n. m. could be an AI Mar 30 '17 at 10:21
  • @n.m., Thanks for you advice. – zhenguoli Mar 30 '17 at 10:28
  • @n.m. K&R is not the best C book out there - or if it is, the state of C books out there must be pretty shocking. The second issue of K&R describes what is now known as C89 (or C90). There have been two major revisions to the standard since then. – JeremyP Mar 30 '17 at 10:33
  • @JeremyP The state of C books does look shocking to me. – n. m. could be an AI Mar 30 '17 at 10:49
  • @n.m. The return value is of course used by the OS, the program cannot assume otherwise. Citing ISO/IEC 9899:1990, the standard that K&R 2nd edition claims to follow: 6.6.6.4 `"If a return statement without an expression is executed and the value of the function call is used by the caller, the behavior is undefined. Reaching the } that terminates a function is equivalent to executing a return statement without an expression."` Compile K&R examples with `gcc -std=c90` and see for yourself why the book is bad. – Lundin Mar 30 '17 at 14:07
  • @Lundin the standard specificallly addresses returning no value from main. The value returned to OS is undefined in this case, that's all what the standard says about it. This implies nothing whatsoever about the program. Values returned to the OS and possible OS reactions are implementation-defined anyway. – n. m. could be an AI Mar 30 '17 at 15:02
  • @Lundin The book contains its share of errors. So? If every book that is not ideal is bad, then K&R is bad. I will still recommend it. – n. m. could be an AI Mar 30 '17 at 15:06
  • @Lundin I have just compiled K&R code downloaded from github. After removal of // comments added by the repo owner, there are zero errors or warnings. `main()` declarations are still in place. If you have some particularly bad example in mind, please share. – n. m. could be an AI Mar 30 '17 at 15:23
  • @n.m. "the standard specificallly addresses returning no value from main. The value returned to OS is undefined in this case" This is false (in C11). "reaching the `}` that terminates the `main` function returns a value of 0" (5.1.2.2.3 point 1) – JeremyP Mar 30 '17 at 15:29
  • @JeremyP we're talking about C90. K&R doesn't claim to be updated to C11. – n. m. could be an AI Mar 30 '17 at 15:42
  • @n.m. Why are you defending it then? It's a book that teaches to a standard that has been out of date for 17 years. In fact, it doesn't teach to that standard, it teaches to a draft of that standard. – JeremyP Mar 30 '17 at 15:46
  • @JeremyP Why souldn't I? It's still one of the best books out there, all its shortcomings notwithstanding. – n. m. could be an AI Mar 30 '17 at 15:53
  • @n.m. But it does not conform to modern standards (even the very first example elicits a compiler warning with a modern compiler) and it doesn't necessarily point out areas of danger (e.g. it references `gets()` as if it is something you can use. – JeremyP Mar 30 '17 at 16:13
  • @JeremyP I know about its shortcomings. They are minor. You are welcome to find and propose a better book. – n. m. could be an AI Mar 30 '17 at 16:23
  • @n.m. It's a minor shortcoming to tell people about `gets` without also telling them why they should never ever use it? – JeremyP Mar 30 '17 at 16:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/139515/discussion-between-n-m-and-jeremyp). – n. m. could be an AI Mar 30 '17 at 17:24
  • @n.m. There's pretty much not a single example in the book that compiles cleanly. `#include main() { printf("hello, world\n"); }` then `gcc -std=c90` (no extra warning flags). `warning: return type defaults to 'int'` and `warning: control reaches end of non-void function`, where the latter is UB. 5 lines of very simple code resulting in 2 diagnostics where 1 is undefined behavior. That is impressively poor programmer performance. – Lundin Mar 31 '17 at 06:28
  • The _only_ reason why people keep defending this book is subjective nostalgia. It was indeed the best C programming book ever written, at the point when no other books existed. It was still highly relevant during all of the 1980s. Checking the date in my calendar, we appear to be in the year 2017. – Lundin Mar 31 '17 at 06:32
  • I don't get any of those warnings with gcc6 -std=c90. Reaching the end of a non-void function is **not** UB in C90, read the standard, I have already mentioned chapter and verse. It is **well defined** behaviour for `main` in C11, equivalent to returning 0. – n. m. could be an AI Mar 31 '17 at 11:23
  • The other good reason why people keep defending this book is that all others are worse, even now in 2017. – n. m. could be an AI Mar 31 '17 at 11:26
  • @lundin http://coliru.stacked-crooked.com/a/06d0bbd3f902eb0e – n. m. could be an AI Mar 31 '17 at 11:44
  • @n.m. Ok apparently my IDE put in `-Wall` even when I told it not to. Still, I would expect code in programming books to compile cleanly even with warnings enabled. http://coliru.stacked-crooked.com/a/739e0a7528630f8c. And the code invokes undefined behavior as per the previously cited chapter from ISO 9899:1990. – Lundin Mar 31 '17 at 13:22
  • @Lundin I have tried to explain numerous times that in C90 this is not UB. I won't do it any more. Have a nice day. – n. m. could be an AI Mar 31 '17 at 13:54
  • @n.m. No you haven't, you have cited some mysterious "3.6.6.4" which I assume is the X3J11 draft for pre-C90. Which, as it turns out, contains the identical text as C90, labelling this UB: `If a return statement without an expression is executed, and the value of the function call is used by the caller, the behavior is undefined. Reaching the } that terminates a function is equivalent to executing a return statement without an expression.` – Lundin Mar 31 '17 at 14:19
  • @Lundin We obviously read different things from this passage. I have no idea how to continue from here, s I'll just leave it at that. – n. m. could be an AI Mar 31 '17 at 16:53

4 Answers4

4

From the C Standard (6.3.2.1 Lvalues, arrays, and function designators)

4 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’’.

and (6.7.6.3 Function declarators (including prototypes))

8 A declaration of a parameter as ‘‘function returning type’’ shall be adjusted to ‘‘pointer to function returning type’’, as in 6.3.2.1.

Thus these function declarations

void f(void g());

and

void f(void ( *g )());

declare the same one function.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

Because a function decays into a function pointer when it is not used within a function calling context:

void f(void a()) { a(); }    //void a() decays into void (*a)()
void g(void (*a)()) { a(); }    //equivalent to the above
void h(void a()) { (*a)(); }    //*a is equivalent to a
void i(void a()) { (**a)(); }    //**a is equivalent to *a
void j(void a()) { (***a)(); }    //...
void k() { };

int main() {
    f(k);
    g(k);
    h(k);
    i(k);
    j(k);
}

This compiles and runs fine, even though I've been using liberal amounts of the dereferencing operator on the argument function. Each time I use it, the resulting function decays back into a pointer when it hits the next dereferencing operator. Likewise, the function argument void a() decays into a pointer just like int array[] decays into int* array when used as a function argument.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
1

When used as a function parameter, function name behaves as a pointer to the function itself.
These all are valid prototype

void f(void (g)());
void f(void (*g)());
void f(void (**g)());
void f(void (*****g)());

When you declare a function pointer then you can make a call using that pointer as

g();
(*g)();
(*****g)();
haccks
  • 104,019
  • 25
  • 176
  • 264
  • So as far as I'm concerned, maybe there is no need to define pointer to functions. And there is something material explaining this standard? I am reading K&R, there hasn't talked about it. – zhenguoli Mar 30 '17 at 09:22
  • 1
    @zhenguoli If you want a variable that can be changed, you have to use a function pointer. – Lundin Mar 30 '17 at 09:23
1

The C standard ISO/IEC 9899:2011, section 6.3.2.1 item 4 says:

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator, the _Alignof operator,65) 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”.

So normally a function name (or other expression with function type, i.e. a function designator) is converted to a pointer to a function, except in the circumstances mentioned above.

So what happens when you apply the & operator to a function, as that is one of the cases excluded from the above clause. The answer is given by section 6.5.3.3 item 3, the relevant parts of which are:

The unary & operator yields the address of its operand. If the operand has type “type”, the result has type “pointer to type”. [...] Otherwise, the result is a pointer to the object or function designated by its operand.

(The "[...]" part I missed out discusses applying the & operator to the result of a unary * operator and to the result of the [] operator.)

So applying & to a function name (or other function designator) also gives you a pointer to the function.

So what happens when you dereference a function pointer with the unary * operator? It produces a function designator (section 6.5.3.3 item 4), which in most circumstances (as discussed above) gets converted back to a pointer to a function!

EDIT: I neglected to mention the case of declaring a parameter of a function to be a function returning type. As discussed in Vlad from Moscow's answer referring to section 6.7.6.3 item 8, that gets automatically adjusted to be a pointer to a function returning type.

Ian Abbott
  • 15,083
  • 19
  • 33