1

Consider the following code:

#include <stdio.h>

int ret_five() {
  return 5;
}

int main() {
  int x[5] = {1,2,3,4,5};

  int (*p)();
  p = &ret_five;
  printf("%d\n", p());              // 1
  p = ret_five;
  printf("%d\n", p());              // 2

  printf("%d\n", sizeof ret_five);  // 3
  printf("%d\n", sizeof &ret_five); // 4

  printf("%d\n", (*p)());           // 5
  printf("%d\n", (****p)());        // 6

  printf("%p\n", p);                // 7   // edited: replaced %d with %p
  printf("%p\n", *p);               // 8   //   same here and in (8), (10)
  printf("%p\n", **p);              // 9
  printf("%p\n", *******p);         // 10

  printf("%p\n", x);                // 11
  printf("%p\n", &x);               // 12
  return 0;
}

My questions are:

  1. Lines (1) and (2) print the same result. Do ret_five and &ret_five have the same data type? It seems like no, because lines (3) and (4) print different results.

  2. From a syntactical point of view, it seems to me that line (5) should be the right way to call the function that p points to, but of course lines (1) and (2) print 5 just fine. Is there a technical reason for this, or was it a design decision made because the calls in (1) and (2) look cleaner? Or something else?

  3. Line (5) makes perfect sense to me (because p is a function pointer, its dereferenced value is the function, we call the function, it returns 5, we print 5). I was very surprised to find that (6) prints 5 as well! Why is this?

  4. Similarly, lines (7)--(10) all print the same value, namely &ret_five. Why does (10) work?

  5. Lines (11) and (12) print the same value, namely the address where the first element of x lives in memory. Line (12) makes sense, but I don't quite understand exactly what is technically happening in line (11). Does x automatically get cast or interpreted as an int* in this context?

  6. To get the location in memory where x is stored, I typically do &x[0], but it seems like &x works just fine as well, and because x is an array and not a pointer, it seems like in fact &x may be the more canonical way of getting this memory address. Is there a reason to prefer one to the other?

  7. In general, are there best-practices in the above situations? For example, if p = ret_five; and p = &ret_five really do the exact same thing, is there a reason to prefer one to the other?

  8. And, if the two assignments in question 7 really do the exact same thing, why, in a language that is otherwise so rigid, was this laxity built-in?

Andrey Mishchenko
  • 3,986
  • 2
  • 19
  • 37
  • @haccks No, since the questions are correlated. – this Dec 06 '13 at 21:03
  • 1
    Sorry, I considered that, but I feel like these questions are very related to one another, and 8 separate answers would duplicate one another a lot. – Andrey Mishchenko Dec 06 '13 at 21:03
  • 2
    I don't see why the downvote, this is actually asking something new/unusual. – this Dec 06 '13 at 21:04
  • Furthermore considering this question made me realize that the behavior of function pointers and array pointers is very similar in C, and understanding one lends itself to an understanding of the other very well, which I feel can be valuable for others who had missed this as well. – Andrey Mishchenko Dec 06 '13 at 21:05
  • @haccks I Never said you did. – this Dec 06 '13 at 21:08

3 Answers3

4

Do ret_five and &ret_five have the same data type?

ret_five is a function designator and &ret_five is a function pointer. In an expression ret_five is converted to a function pointer whose value and type are the same asret_five.

printf("%d\n", sizeof ret_five);  // 3
printf("%d\n", sizeof &ret_five); // 4

sizeof &ret_five is correct. And it yields the size of a function pointer of type int (*)().

sizeof ret_five is invalid C code and it is accepted in gcc as a GNU extension.

printf("%d\n", p);                // 7
printf("%d\n", *p);               // 8
printf("%d\n", **p);              // 9
printf("%d\n", *******p);         // 10

If p is a function pointer, p, *p or *****p are equivalent in C.

printf("%p\n", x);                // 11
printf("%p\n", &x);               // 12

x is an array of 5 int elements. In an expression (except in a few exceptions like if it is the operand of the &operator), it is converted to a pointer to its first element (type of x after conversion is int *).

&x is a pointer to an array of 5 int elements (type of &x is int (*)[5].

ouah
  • 142,963
  • 15
  • 272
  • 331
  • By that logic lines 7-10 are invalid as well? – this Dec 06 '13 at 21:08
  • Yes lines 7--10 should probably have just had a `%p` instead of `%d`. I made the edit. The question remains, WHY are those expressions equivalent? Did someone make a decision that they should be equal, or is there a technical reason? – Andrey Mishchenko Dec 06 '13 at 21:11
1
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«.

ret_five and &ret_five both evaluate to the same function pointers. sizeof ret_five is a constraint violation and your compiler should output a diagnostic. So, ret_five is a function designator that is in all (but two (see above)) situations converted to a pointer to said function, *ret_five is again a function designator, which is AGAIN converted to a pointer to said function if you use it in any context except the two above, so **ret_five is again a function designator, and so on. Printing such a pointer with %d is undefined behavior since %d is for ints.

p = ret_five is correct in modern C. Using &ret_five instead is old fashioned, 1980s C.

Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type »array of type« is converted to an expression with type »pointer to type« that points to the initial element of the array object and is not an lvalue.

x and &x have the same numerical value (they are pointers to x's first element) but different types. x evaluates to a pointer to int, but &x evaluates to a pointer to an array of five ints.

Community
  • 1
  • 1
Cartan
  • 36
  • 5
-1

When using %p specifier you need to cast the argument to void * as it expects void * type argument.

7.21.6 Formatted input/output functions:

p The argument shall be a pointer to void. The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner.

 printf("%p\n", (void *) &x);  

Lines (11) and (12) print the same value, namely the address where the first element of x lives in memory. Line (12) makes sense, but I don't quite understand exactly what is technically happening in line (11). Does x automatically get cast or interpreted as an int* in this context?

Yes it will. Array name x decays to the pointer to first element of arrayx. It will give you the location of first element and having type int *. &x is the address of entire array x and it will always print the starting address of the array and is of type int (*)[5]. Since address of first element of the array is same as the staring address of array x that's why you are getting the same value.

To get the location in memory where x is stored, I typically do &x[0], but it seems like &x works just fine as well, and because x is an array and not a pointer, it seems like in fact &x may be the more canonical way of getting this memory address. Is there a reason to prefer one to the other?

Answer is similar to previous one. &x[0] is the address of first element while &x is the address of entire array.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • 3
    Thanks for your answer but this is not really the point of the question. – Andrey Mishchenko Dec 06 '13 at 21:13
  • If the behavior is undefined then no point to discuss. Further I am working on your other questions. – haccks Dec 06 '13 at 21:15
  • I disagree, I think it is obvious what the code means that that the `printf` statements are 100% boilerplate and not to the point. I am asking why `*****p == p`, whether to use `p = &ret_five` or `p = ret_five`, etc. – Andrey Mishchenko Dec 06 '13 at 21:17
  • 1
    *"you need to cast the argument to `void *` as it expects `void *` type argument"* does this mean that `void`-pointer conversion (as in C99 §6.3.2.3.2) does not apply to function arguments? – Kninnug Dec 06 '13 at 21:44
  • @Kninnug; The standard says: *A pointer to any object type **may be** converted to a pointer to `void` and back again;* What does it mean? Is it necessary? – haccks Dec 06 '13 at 21:51
  • @haccks I was just wondering, since casts are not necessary when assigning to or from `void *`s I thought they might also not be necessary when a function expects `void *` arguments. – Kninnug Dec 06 '13 at 21:55
  • @Kninnug; There is a difference when you are not casting `malloc` and an argument of a function. In first case you would not get any warning from your compiler but in latter case most of the compilers will give you warning: `[Warning] format '%p' expects argument of type 'void *', but argument 2 has type 'int *' [-Wformat=]`. To avoid these warnings you must have to cast it with `void *`. – haccks Dec 06 '13 at 22:08
  • 2
    @haccks Ok, but the warnings are caused by the violation of a rule (that I'm trying to find out). Avoiding warnings shouldn't be the *only* reason to add a cast (as it can sometimes hide a bug, as with the cast to `malloc`'s result). Though I suppose that `printf` and family are a special case what with the variadic arguments & the fact that the compiler knows more about how they handle their arguments. I shall make this into a separate question, unless you know of one where this is already asked. – Kninnug Dec 06 '13 at 22:23
  • @haccks As it is a picky answer, here is a picky comment: `(void *) p` where `p` is a function pointer is not valid in C. Even if most compilers allow it, you cannot convert a function pointer to an object pointer type or to a `void *`. – ouah Dec 06 '13 at 22:31
  • @ouah; Surprising!! What about `#include int main(void) { int z = 9; printf("%p", &z); }` ? – haccks Dec 06 '13 at 22:36
  • @haccks Well UB as you mentioned it in your answer but there is no function pointer in this program. – ouah Dec 06 '13 at 22:39