0

I have some confusion about reading the value of a function pointer variable.

#include <stdio.h>

char* cat_sound()
{
    return "meow";
}

char* dog_sound()
{
    return "hav";
}

typedef char* (*fptr_sound)(void);


typedef struct 
{
    int age;
    fptr_sound sound;
}creature_t;


int main()
{
    creature_t cat, dog;
    cat.sound = cat_sound;
    dog.sound = dog_sound;

    printf("cat.sound()= %s, dog.sound()= %s\n", cat.sound(), dog.sound());
    printf("cat.sound= %s, dog.sound= %s\n", cat.sound, dog.sound);
    printf("*cat.sound= %s, *dog.sound= %s\n", *cat.sound, *dog.sound);
    printf("*cat.sound()= %c, *dog.sound()= %c\n", *cat.sound(), *dog.sound());

    printf("value of cat.sound= %x, dog.sound= %x\n", cat.sound, dog.sound);
    printf("value of *cat.sound= %x, *dog.sound= %x\n", *cat.sound, *dog.sound);
    printf("value of cat.sound()= %x, dog.sound()= %x\n", cat.sound(), dog.sound());
    printf("value of *cat.sound()= %x, *dog.sound()= %x\n", *cat.sound(), *dog.sound());

    return 0;
}


cat.sound()= meow, dog.sound()= hav
cat.sound= U▒▒]▒U▒▒]▒U▒▒S▒▒▒▒ ▒D$▒D$&▒D$▒ЉËD$▒Љ\▒D$▒$▒▒▒▒▒T$▒D$▒T▒D$▒$▒x▒▒▒▒T$▒D$▒T▒D$▒$<▒\▒▒▒▒D$▒▒▒, dog.sound= U▒▒]▒U▒▒S▒▒▒▒ ▒D$▒D$&▒D$▒ЉËD$▒Љ\▒D$▒$▒▒▒▒▒T$▒D$▒T▒D$▒$▒x▒▒▒▒T$▒D$▒T▒D$▒$<▒\▒▒▒▒D$▒▒▒
*cat.sound= U▒▒]▒U▒▒]▒U▒▒S▒▒▒▒ ▒D$▒D$&▒D$▒ЉËD$▒Љ\▒D$▒$▒▒▒▒▒T$▒D$▒T▒D$▒$▒x▒▒▒▒T$▒D$▒T▒D$▒$<▒\▒▒▒▒D$▒▒▒, *dog.sound= U▒▒]▒U▒▒S▒▒▒▒ ▒D$▒D$&▒D$▒ЉËD$▒Љ\▒D$▒$▒▒▒▒▒T$▒D$▒T▒D$▒$▒x▒▒▒▒T$▒D$▒T▒D$▒$<▒\▒▒▒▒D$▒▒▒
*cat.sound()= m, *dog.sound()= h
value of cat.sound= 804841c, dog.sound= 8048426
value of *cat.sound= 804841c, *dog.sound= 8048426
value of cat.sound()= 80485f0, dog.sound()= 80485f5
value of *cat.sound()= 6d, *dog.sound()= 68

As seen below, cat.sound holds the address of cat_sound function. So how come *cat.sound returns the same value? (If comparing to a pointer to integer, de-referencing returns the value of to which it points rather than the value itself.)

And why does cat.sound() point to the value returned from the function rather than the function address itself?

08048430 <main>:
 8048430:       55                      push   ebp
 8048431:       89 e5                   mov    ebp,esp
 8048433:       53                      push   ebx
 8048434:       83 e4 f0                and    esp,0xfffffff0
 8048437:       83 ec 20                sub    esp,0x20
 804843a:       c7 44 24 1c 1c 84 04    mov    DWORD PTR [esp+0x1c],0x804841c





0804841c <cat_sound>:
 804841c:       55                      push   ebp
 804841d:       89 e5                   mov    ebp,esp
 804841f:       b8 f0 85 04 08          mov    eax,0x80485f0
 8048424:       5d                      pop    ebp
 8048425:       c3                      ret





08048426 <dog_sound>:
 8048426:       55                      push   ebp
 8048427:       89 e5                   mov    ebp,esp
 8048429:       b8 f5 85 04 08          mov    eax,0x80485f5
 804842e:       5d                      pop    ebp
 804842f:       c3                      ret
manish ma
  • 1,706
  • 1
  • 14
  • 19

2 Answers2

2

cat.sound() calls the function pointed to by cat.sound and returns its return value, which is a char* pointer to beginning of the string literal "meow". Printing it with %s is fine and gives the expected result meow. Printing it with %x gives an implementation defined representation of the address of the literal or undefined behavior. Better use %p instead, which will remove the possibility of undefined behavior.

*cat.sound() dereferences the pointer to the first character of the string literal "meow" and therefore gives the char 'm' which %c will print directly, while %x will format to its hexadecimal (ascii) representation and print it.

cat.sound is a pointer to a function, printing it with %s is undefined behavior. Effectively, as your output suggests, the compiler chose to print the function's object code located at the pointer address until it encountered a null byte.

*cat.sound is the same as cat.sound. * applied to a function pointer doesn't really do anything. It yields a function designator, which then the way you are using it decays again to a function pointer. This is different behavior than for non-function pointers. Again printing it with %s is undefined behavior.

Printing the function pointers with %x is also undefined behavior, see this question on how to legally print function pointers.

walnut
  • 21,629
  • 4
  • 23
  • 59
1

Like arrays, function designators are automatically converted to pointers to their functions. C 2018 6.3.2.1 4 says:

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”.

Thus the compiler automatically converts cat_sound (which, as the name of a function, designates the function) to &cat_sound.

Also, since cat.sound is a pointer to a function, *cat.sound designates the function. But then, since it is a function designator, it is automatically converted to a pointer to the function, producing &*cat.sound, which is just the address of the function, the same as cat.sound.

cat.sound() is a pointer to the data returned by the function because the parentheses, (), cause the function to be called, and the result is the pointer returned by the function.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312