4

I wonder why is it necessary to cast a void pointer to an int * or char * before printing the contents of the address in memory, even though we tell the printf() function how to interpret the data in memory?

Let's say that we have the following code:

int main (void)
{
 void* c = malloc(4);
 printf("%d",*c);
 return 0;
}

Why it is not possible to do this?

So to be clear my question is what is the reason for this being not possible?

EDIT: After all the answers and research I am still not convinced exactly where the fatal error in the compilation is. The main reason I am confused is that my compiler (gcc) only gives a WARNING when telling about "dereferencing a void pointer". Is this the actual error? From what I know the program should still compile even with warnings.

EDIT2 I am still confused about the reasons for which we have an ERROR and a WARNING that appear to be completely separate but are generated by the same piece of code:

pointer.c:7:13: warning: dereferencing 'void *' pointer                                                                                                                                                           
 printf("%d",*p);                                                                                                                                                                                                 
             ^~                                                                                                                                                                                                   
pointer.c:7:13: error: invalid use of void expression                                                                                                                                                             
 printf("%d",*p);

Some users say that the error appears only when we try to use the result of the derefenciation and that the WARNING is when we actually alocate memory for the VOID pointer.

This is clearly NOT the case since if we remove the printf line we do indeed get only a warning but a COMPLETELY UNRELATED ONE.

pointer.c:6:8: warning: unused variable 'p' [-Wunused-variable]                                                                                                                                                   
 void * p=malloc(4);    
  • 3
    with `*c` you are dereferencing a `void` pointer. – JFMR Sep 15 '17 at 08:10
  • You aren't doing what it says in your title. You can't dereference a `void *` in any context, not just this one. – user207421 Sep 15 '17 at 08:12
  • @眠りネロク I know but I already told the printf function how to interpret the bits so I just want the bits at that memory address. –  Sep 15 '17 at 08:14
  • 1
    You told print to expect a number, but dereferncing the void * is another op that happens before sending it to printf. – doctorlove Sep 15 '17 at 08:15
  • @JenniferAnderson that's just for `printf()` (string formatting), not for the compiler. – JFMR Sep 15 '17 at 08:17
  • 1
    This code has undefined behavior since the value of the allocation is indeterminate. You need to store some values, or use `calloc()` to zero-initialize the allocation. And note that usually you would use `int *c = malloc(4);`. – ad absurdum Sep 15 '17 at 08:18
  • 2
    @JenniferAnderson The problem is "which bits" and how many of them. Dereferencing a pointer extracts extracts a value from the memory the pointer is pointing to. So the printf call isn't aware of any pointer, it's just aware of a value. The only way the compiler knows how the value is stored is through the type of the pointer. `void` specifically means "no idea how many bits, how they are stored and what to do with them". – Art Sep 15 '17 at 08:28
  • 2
    Possible duplicate of [Dereference void pointer](https://stackoverflow.com/questions/15468441/dereference-void-pointer) – msc Sep 15 '17 at 08:31
  • @眠りネロク I have an other curiosity. Why in the error list, the "dereferencing void pointer" is only considered a warning and not an error, but the program crashes anyway due to "invalid pointer expression ? –  Sep 15 '17 at 08:34
  • 1
    Possible duplicate of [Regarding dereferencing 'void \*' pointer](https://stackoverflow.com/questions/44328870/regarding-dereferencing-void-pointer) – Julien Lopez Sep 15 '17 at 08:46
  • @JenniferAnderson as said in https://stackoverflow.com/a/46234769/8012646 "gcc has an extension which treats a `void*` the same as `char *`" – JFMR Sep 15 '17 at 08:50
  • There are no syntax errors here, so the compiler does not report an error. There is undefined behavior, since it is UB to use an indeterminate value, and UB to dereference a void pointer. Undefined behavior means anything, including a crash (or the program could even _appear_ to work) could happen. – ad absurdum Sep 15 '17 at 08:51
  • @DavidBowling your first comment is clearly wrong since if the pointer is declared from the beginning as an 'int *' there is no error given. So the problem is not that the memory space is unitialized. –  Sep 15 '17 at 12:12
  • @JenniferAnderson-- I don't think that you understood my comment. The [`malloc()`](http://port70.net/~nsz/c/c11/n1570.html#7.22.3.4) function returns a pointer to uninitialized memory. The [`calloc()`](http://port70.net/~nsz/c/c11/n1570.html#7.22.3.2) function zero-initializes all bits of the allocation. Your code has two undefined behaviors: it is UB to dereference a pointer to `void` (or to use a `void` value), and it is UB to use an indeterminate value (which is what is stored in the `malloc`ed locations). The compiler reports syntax errors, not programmer errors. – ad absurdum Sep 15 '17 at 12:22
  • @DavidBowling I understand perfectly what you say. The problem is clearly not cause by the fact that the memory area returned by the malloc function is not initialized. This has nothing to do with my question. –  Sep 15 '17 at 12:29
  • The warning is for attempting to dereference a pointer to `void`, which is not a syntax error, but is UB, so the compiler is kind enough to warn you. The error is for attempting to use a `void` value. This is also not a syntax error, but the compiler must consider this important enough to stop compilation here. Both lead to UB. I don't think that the compiler is _required_ to issue a diagnostic for either one.... – ad absurdum Sep 15 '17 at 12:30
  • I never suggested that the warnings have anything to do with the uninitialized memory, but nonetheless, the memory is uninitialized, and the code attempts to use these indeterminate values. The code is not valid C code because it is undefined. If it did compile, it could do anything at all, including reformat your hard drive. The errors and warnings are attempts to get you to not run this code. – ad absurdum Sep 15 '17 at 12:33
  • "Some users say that the error appears only when we try to use the result of the derefenciation and that the WARNING is when we actually alocate memory for the VOID pointer."-- I certainly never said this. But, when you remove the `printf()` the warning and error go away because you never attempt to dereference `p`, and never attempt to use the dereferenced value. The unused variable warning is because now `p` is unused. If you change to `int *c = malloc(4);` (and add the `#include`s) there will be no warnings, but still UB. – ad absurdum Sep 15 '17 at 13:07
  • 1
    @DavidBowling I understand now. thanks for the clarification and sorry for misunderstanding. –  Sep 15 '17 at 13:45
  • Sure. To add: compilers must issue diagnostics for syntax errors and constraint violations. Whether these are reported as errors or warnings is an implementation detail. Also, compilers may issue diagnostics for other reasons, again an implementation detail. Syntax errors can't compile, so lead to compiler error messages (but other things may lead to error messages too). The Standard doesn't say too much about this, but you can [read about it here](http://port70.net/~nsz/c/c11/n1570.html#5.1.1.3). You can adjust compiler warnings with, e.g., `-Wall`, `-Wextra`, `-Wpedantic`, -`Werror`.... – ad absurdum Sep 15 '17 at 14:00

6 Answers6

8

The problem is not the interpretation of printf(), it's with the dereference.

In the case of a statement like

 printf("%d",*c);

you are attempting to dereference a void *. Now, void is a forever-incomplete type. You cannot dereference a pointer to "something incomplete" and get something meaningful as a result.

Quoting C11, chapter §6.2.5 , P19

The void type comprises an empty set of values; it is an incomplete object type that cannot be completed.

To put it in other words (though a bit of my own), C is a strongly typed language and for each defined type, the compiler knows (or, must know) how many bytes (size) from memory it needs to read / operate on. For a void type, the compiler is clueless.

For the very same reason, pointer arithmetic is also not allowed on void *s. gcc has an extension which treats a void* the same as char *, but that's an extension, not standard.


EDIT:

My compiler produces an error for the statement, check online.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • @SpuravGhosh thanks for the answer, but why does the compiler only gives a warning for "dereferencing void pointer" and NOT an error? –  Sep 15 '17 at 08:35
  • @JenniferAnderson I got an error, check updated answer. – Sourav Ghosh Sep 15 '17 at 09:25
  • 1
    @JenniferAnderson, in C there is not really a distinction between warnings and errors. The C standard calls them both "diagnostic". Generally you should attempt to have a clean compile without any diagnostics whatsoever. – Jens Gustedt Sep 15 '17 at 22:39
5

*c dereferences the pointer, which is of type void (an incomplete type). You cannot dereference void, since the compiler doesn't know the type (thus its size). As a result, dereferencing a void pointer invokes Undefined Behavior.

In other words, it's not reasonable to dereference a void pointer. Imagine you are the compiler, how would you interpter the memory that the pointers is pointing to (since it can be anything)? Casting a void* to the proper type will do the trick (provide the nessecairy information to the compiler).

Why does the compiler only gives a warning and not an error?

It does, notice that it will generate a warning and a companioned error:

main.c:8:14: warning: dereferencing 'void *' pointer
  printf("%d",*c);
              ^~
main.c:8:14: error: invalid use of void expression
printf("%d",*c);
^~~~~~

(gcc) gives a WARNING when telling about "dereferencing a void pointer". Is this the actual error?

No.

But attempt to use the result of this derefencization and you are in bad luck.

I mean that:

void* c = malloc(4);
*c;

will not cause an error (but only a warning):

main.c:6:2: warning: dereferencing 'void *' pointer
  *c;
  ^~

However if you attempt to use the result of *c, an error will be generated.

Read more about this in Towards understanding void pointers.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • thanks for the information :) Now it is more clear to me.Is it possible to tell the compiler how many bytes to dereference without making a cast? Le'ts say I want to dereference 3 bytes for example. –  Sep 15 '17 at 08:16
  • 1
    @JenniferAnderson not really, the right way is to cast. – gsamaras Sep 15 '17 at 08:19
  • @JenniferAnderson Dereferencing pointers requires the compiler to know more than just the number of bytes it needs to read. The type itself determines how that data should be interpreted. For example, if you have a function `int foo(int x)` and you have a `float *y` variable, calling `foo(*y)` doesn't reinterpret the bytes at address `y` as an `int`, but instead first interprets this data as a `float`, then casts the `float` value to an `int`, truncating the number. – Candy Gumdrop Sep 15 '17 at 08:27
  • @gsamaras I tested your code and the warning it gives in case I remove the printf it is a "unused variable" warning so it has NO meaning to dereferencing a void pointer actually. I am still confused. Why do we have an error and a warning at the SAME place? How do the error and the warning differ? –  Sep 15 '17 at 12:18
  • @JenniferAnderson dereferencing the pointer itself is not an error (since you do not really do anything with that and the pointer points to valid memory). That's why this is just a warning. However, when you are about to use it, then what you are trying to do will cause the compiler to generate an error, since you cannot use that in any reasonable way. – gsamaras Sep 15 '17 at 12:37
  • @gsamaras But how can one dereference a pointer to a location whose size is not known? Isn't the size of the data the problem? Because the compiler does not know if it should use 1,2,4,8 bytes, etc –  Sep 15 '17 at 12:41
  • @JenniferAnderson because the standard allows it to, check my updated answer! – gsamaras Sep 15 '17 at 14:25
3

Since c is a void pointer it could be anything. To dereference the compiler needs to know the type, including the size.

Without the malloc and void *, consider what happens here:

int main()
{
    int i  = 555555;
    char * c = (char *)&i;
    printf("%c\n", *c);
    short * s = (short *)&i;
    printf("%d\n", *s);
}

You get very different things printed. The same is going on with the void * - what is it supposed to be?

You have told printf to expect a number, but that is a separate operation to dereferncing the void * in the first place.

doctorlove
  • 18,872
  • 2
  • 46
  • 62
0

The c pointer has been declared void*. Before accessing/dereferencing void pointer you need to must type cast,so that it points to relevant type which you are expecting in this pointer.

msc
  • 33,420
  • 29
  • 119
  • 214
0

To sum up, as stated by previous answers, void is an incomplete type in all C standards & therefore you will definitly get an error whenever you try to dereference it. However, GNU C treats void with size of 1 (char) & therefore it only shows a warning but GCC carries on the compilation according to the GNU extension.

If you want GCC to strictly follow the standard you have to add -std=c11 flag (perhaps with -pedantic).

Karim Manaouil
  • 1,177
  • 10
  • 24
0

printf doesn't take pointers to things to print, mostly so that it can accept the result of expressions:

printf("%d",4*my_function());  // value has no user-visible address

As such, the compiler must know the type in order to fetch the value and give that to printf. (You need the format string too because the language offers printf's implementation no way of discovering what type of object you gave it.)

Davis Herring
  • 36,443
  • 4
  • 48
  • 76