-2

I wrote a void pointer function to find the square of an integer.


void * square(const void * num);

int main(){

    int x = 6;
    void * ptr = &x;
    printf("%d", square(ptr));

}

void * square(const void * num){
    return (*(int * )num) * (*(int *)num);
}

According to my knowledge, this shouldn't work as the function should return a void pointer to the result of (*(int * )num) * (*(int *)num), not the result itself. However, the code still works. Someone please explain.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
StaticESC
  • 79
  • 5
  • 1
    Does this help by any chance? - https://stackoverflow.com/a/30519731/13151323 – Dstarred Aug 05 '21 at 00:25
  • 3
    Compile with warnings on maybe? Any sane compiler should warn you that the return type does not match the function signature. – Marco Bonelli Aug 05 '21 at 00:26
  • Pointers are numbers. You are returning a number, so it is implicitly converted back to a `void*`, and then in your `printf` it is implicitly converted into an `int`. – Pablochaches Aug 05 '21 at 00:27
  • 4
    "_the code still works_" - That is one possible outcome of undefined behavior, which is what your program has. – Ted Lyngmo Aug 05 '21 at 00:28
  • @Pablochaches, pointers are *not* numbers as far as C is concerned. They can be *converted* to and from numbers, but those implementations that perform such conversions without an explicit cast are thereby providing an extension. – John Bollinger Aug 05 '21 at 00:51
  • 1
    Was there are a particular reason you tried this? Normally, a `square` function would accept `int` and return `int`, or accept `double` and return `double`. Are you just trying to learn about void pointers? A more realistic example would probably be more instructive. – Steve Summit Aug 05 '21 at 04:18

2 Answers2

1

In the expression given to the function's return statement:

(*(int * )num) * (*(int *)num);

num is a void * which contains the address of an int. This pointer is then properly converted back to an int *:

(int * )num

and dereferenced:

(*(int * )num)

giving us the value the value stored in x, specifically 6. This happens twice in this expression giving us 6 * 6 which is 36. This is the value that the return statement uses. Since the return type of the function is void *, the int value 36 is converted in an implementation defined way to a void * (on most implementation, this will be a pointer with value 36).

Then the result of square, which has type void *, is passed to printf which is using the %d format specifier. Since this format specifier expects an int but is instead given a void *, this triggers undefined behavior. This basically means that there's no guarantee what the program will do.

One of the ways undefined behavior can manifest itself is that things appear to work properly, which is what you're seeing. Now let's look into what's probably happening.

On systems with a flat memory model a pointer is simply a number, typically either 4 or 8 bytes in size. So that being the case a pointer will probably be passed to a function the same way an integer type is passed. Assuming your system is little-endian, i.e. least significant byte first, the first 4 bytes passed to the function will be the lowest order bytes. So assuming a pointer is 8 bytes, the hex values 0x24 0x0 0x0 0x0 0x0 0x0 0x0 0x0 will be pushed onto the stack. The first 4 of these bytes will then be read by printf as an integer, resulting in 36 being printed.

Just to reiterate, there's no guarantee that the correct result will be printed by this code. It just happened to be that way due to implementation details.

dbush
  • 205,898
  • 23
  • 218
  • 273
0

First of all, it is illegal in c to implicitly cast integer to pointer.

    return (*(int * )num) * (*(int *)num);

Your compiler should give at least a warning on this. If your compiler does not give you any warning, i think there must be some compiler flags to make it warn you.

However, many C compilers don't just stop compiling when they meet those kind of things. They warn you and just compile it as you wrote like this:

return (void *)(((int * )num) * (*(int *)num));

You expected that square() must return void pointer to the result of the calculation, but casting an integer to void pointer does not mean you get pointer to that integer. It means your compiler will interpret your bit arrays of that integer as void pointer. If that integer is 9, the resulting void pointer will point to address 9.

This is not illegal, but this has undefined behavior when you actually try to access where the void pointer points to. Undefined behavior means that you can get different result when you compile it on other compilers.

Also, printf("%d", square(ptr)) cause undefined behavior because you specified you will give int to your second parameter but you didn't. This also can result to anything depends on your compiler and computer, but in this case, your compiler just interpreted your bit array of that void pointer as integer.

If you want to return void pointer that points to the result, you should do like this:

void *square(const void *num)
{
    int *ret;

    ret = malloc(sizeof(int));
    *ret = (*(int *)num) * (*(int *)num);
    return ret;
}

If you want to know more about what is undefined behavior, you can find c standard documents from here. It is not a good practice to rely on what your compiler do when you want to know how would some code snippet works.

kjh6b6a68
  • 499
  • 2
  • 8