0

I am trying to understand the pointer concepts in-depth. In the following code,

#include <stdio.h>
int main()
{
    int i = 10;
    int *iptr = &i;
    printf("(float)* : %f\n", (float)*iptr);
    printf("(float*) : %f\n", (float*)iptr);
    printf("*(float*) : %f\n", *(float*)iptr);
    return 0;
}

output:

 (float)* : 10.000000
 (float*) : 10.000000
*(float*) : 0.000000

I also got a warning for the type-cast (float*).
I find it difficult to even analyse the difference. If anyone can help me to analyse what is the exact usage of all three, it would be helpful.

  • 1
    The second `printf()` shouldn't compile. – Iharob Al Asimi Jun 04 '15 at 10:13
  • 7
    @iharob Yeah it should, specifier mismatches (and aliasing violations) are undefined behaviour but not constraint violations – M.M Jun 04 '15 at 10:15
  • It had compiled with a warning message @iharob –  Jun 04 '15 at 10:16
  • 1
    Denise, you have to get the `printf` format specifiers right on your own , otherwise you get garbage. It doesnt automatically detect and convert for you. For this example you need to change the `%f` on the second line to `%p`. Then we can go on to discuss the output – M.M Jun 04 '15 at 10:16
  • 4
    Then, Do not ignore warning messges!!!!!! – Iharob Al Asimi Jun 04 '15 at 10:16
  • Yeah its done now @MattMcNabb. Now, the warning is removed.Thank you –  Jun 04 '15 at 10:26
  • Here after will take warnings too in account, thanks. @iharob –  Jun 04 '15 at 10:27
  • @Denise there is a reason why `-Werror` flag exists. – Iharob Al Asimi Jun 04 '15 at 10:28
  • @Denise do not edit the original question if that invalidates already posted answers. – Iharob Al Asimi Jun 04 '15 at 10:31
  • @iharob disagree - I advised the question to be edited :P The real question is "Difference between `(float *)` & `*(float*)` in C", not "format specifier mismatch UB". You can update your answer – M.M Jun 04 '15 at 10:31
  • @MattMcNabb Although it's true, I believe it was one issue with this particular question which my answer tries to fix, and there was a comment on my answer saying that point 2 is wrong because the OP used `"%p"`. – Iharob Al Asimi Jun 04 '15 at 10:33
  • @iharob consider un-rollbacking; imho the typo is just a pointless distraction from the main question being asked, because the output does not show anything useful – M.M Jun 04 '15 at 10:34

2 Answers2

3

The difference is

  1. You are dereferencing the int and casting it to float in

    printf("(float)* : %f\n", (float)*iptr);
    

    which is fine.

  2. You are casting the int pointer to a float pointer, and printing the float pointer with the "%f" specifier is undefined behavior, the correct specifier for printing pointers is "%p", so

    printf("(float*) : %f\n", (float*)iptr);
    

    is wrong, it should be

    printf("(float*) : %p\n", (void *) iptr);
    

    casting to float * here is not meaningful, because the void * address is the same as the float * address and also the int * address, the difference would be when you do pointer arithmetic.

  3. You are casting the int pointer to a float and dereferencing the resulting float pointer, although it will violate strict aliasing rules in

    printf("(float*) : %f\n", *(float*)iptr);
    

    which is also undefined behavior

Community
  • 1
  • 1
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • Still needs to cast it `void*` to match `%p`. – P.P Jun 04 '15 at 10:33
  • @iharob, understood till whatever you had explained. Even though it is undefined behavior, I did not get any errors or even warnings. I wonder how is that possible for " *(float*)ptr ". Is the address which iptr pointing before changed ? Could not exactly understand how I get 0.000000 for " *(float*) " –  Jun 04 '15 at 11:05
  • When undefined behavior happens, the behavior is _unpredictable_ and inexplicable. – Iharob Al Asimi Jun 04 '15 at 11:11
  • Thanks @iharob, got it :) –  Jun 04 '15 at 11:23
  • Please refer the site : http://www.sanfoundry.com/c-quiz-pointers-addresses/ - question number : 3. They say it is not undefined behavior. @iharob –  Jun 05 '15 at 05:23
  • Whether it is undefined behavior or not, can anyone tell me how the value for (* (float* )) is always 0.000000 –  Jun 05 '15 at 05:24
  • @Denise It doesn't matter what the site says, the standard does not specify a behavior in that case, and why it's always `0.000000` is not possible to know. Just for the formattnig of the code at the link I just don't trust it, and the are breaking aliasing rules by doing something called [type punning](http://www.cocoawithlove.com/2008/04/using-pointers-to-recast-in-c-is-bad.html). – Iharob Al Asimi Jun 05 '15 at 10:55
  • Thanks a lot for your explanations @iharob –  Jun 05 '15 at 13:24
3

The first one is correct.

i is an int variable, and iptr a pointer to that int.

  1. (float)*iptr: *iptr dereferences iptr, which returns an int. Then that int is converted to a temporary float containing the same value. And that float is used by printf.

  2. *(float*)iptr: Attempts to cast a pointer-to-int into a pointer-to-float. This is invalid, and should produce a compiler warning or error. It creates a pointer with the same address, but with the type saying that it points to a float value.

    The * operator then dereferences it, so the int is read as if it were a float. So the resulting float would be invalid, and it could result in a segfault because floats are longer than ints, so it reads more memory than there is allocated for the int.

  3. (float*)iptr: Same problem, but it doesn't dereference the (invalid) pointer, and passes a pointer-to-float into printf, instead of a float. But printf expects a float. Some compilers should also produce a warning/error here because the format string indicates what value types are expected.

    If the format specifier indicates %p, it expects a pointer (void*, float*, or any other). It will then print out the address, and not the value it points to. This can be useful in debugging for example.

Community
  • 1
  • 1
tmlen
  • 8,533
  • 5
  • 31
  • 84
  • The warning the compiler generates on the 2nd printf is: `warning: format '%f' expects argument of type 'double', but argument 2 has type 'float *' [-Wformat=]` this warning doesn't mean that is incorrect the conversion from `int *` to `float *`, but that printf expects a float value and not a pointer value. The compiler is only saying that the parameter is not of the same type that is declared in the format string. – Sir Jo Black Jun 04 '15 at 10:36
  • But for *(float*)iptr - I found no error or even a warning message regarding the same and it prints 0.000000 (for %f) (might be the default val of the float as per my knowledge) @tmlen –  Jun 04 '15 at 11:16
  • @tmlen. ... In other words, if (in the 2nd `printf`) you use `%p` instead of `%f` the compiler gives no warning, although, in this case, the declaration `(float *)` has no sense! ... Then the problem is not the undefined behaviour; undefined behaviour may affect an eventual assignment I.E.: (relevant to the code in the question) `i=*(float *)iptr;`, but the compiler doesn't signal any warning! – Sir Jo Black Jun 04 '15 at 11:18
  • For a code like `int i = 10; float f = *(float*)&i`;, the variable `f` will not contain `10.0f`, because it never converts the int value to a float value. It just reads the binary int value in memory as if it were a float. What value `f` then represents is undefined behavior, depends on how floats are encoded. C allows for explicit pointer casts (`(float*)iptr`), but not implicit pointer casts (`float* fptr = iptr`). In some cases they are useful, for example if you have a function that takes a `void*` argument, and you know it actually is a pointer to an `int`. For example `qsort`. – tmlen Jun 04 '15 at 11:27
  • @tmlen, Nothing to say about this! I'm saying something different! – Sir Jo Black Jun 04 '15 at 11:29