8

In my answer I mention that dereferencing a void pointer is a bad idea. However, what happens when I do this?

#include <stdlib.h>
int main (void) {
 void* c = malloc(4);
 *c;
 &c[0];
}

Compilation:

gcc prog.c -Wall -Wextra
prog.c: In function 'main':
prog.c:4:2: warning: dereferencing 'void *' pointer
  *c;
  ^~
prog.c:5:4: warning: dereferencing 'void *' pointer
  &c[0];
    ^
prog.c:5:2: warning: statement with no effect [-Wunused-value]
  &c[0];
  ^

Here is an image from Wandbox for those who say it didn't happen:

enter image description here

and a Live demo in Ideone.

It will actually try to read what the memory that c points to has, and then fetch that result, but actually do nothing in the end? Or this line will simply have no effect (but then GCC wouldn't produce a warning).

I am thinking that since the compiler doesn't know anything about the data type, it won't be able to do much, without knowing the size of the type.

Why derefencing a void* does not produce an error, but just a warning?


If I try an assignment, I will get an error:

invalid use of void expression

but shouldn't the dereferencing alone produce an error?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 9
    Since `void` is an incomplete type that cannot be completed, dereferencing a `void *` should generate a compiler diagnostic. – Jonathan Leffler Sep 15 '17 at 13:43
  • 2
    some compilers assume 1 for size of void when pointed on (at least with pointer arithmetic) – Jean-François Fabre Sep 15 '17 at 13:43
  • @JonathanLeffler yes that's what I am saying, I would expect an error to occur. Jean, let's focus on GCC. – gsamaras Sep 15 '17 at 13:45
  • 5
    I think it is a GNU extension. Well, at least the [`void` pointer arithmetic](https://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html#Pointer-Arith) – Eugene Sh. Sep 15 '17 at 13:45
  • 1
    maybe related/duplicate: https://stackoverflow.com/questions/13113301/how-void-pointer-arithmetic-is-happening-in-gcc – Jean-François Fabre Sep 15 '17 at 13:46
  • I'd also be interested in how a compiler treats `&c[0];` – Bathsheba Sep 15 '17 at 13:46
  • umm..not really, with `-std=c99 -pedantic`, only warning, not error. – Sourav Ghosh Sep 15 '17 at 13:46
  • 1
    If that is a (non-compliant) gcc extension, you chould use that tag. `memory` is pointless. Sure this compiles with gcc? IIRC, it only allows arithmetics, but not dereferencing. With warnings enabled you should get an "unused value" (or so) message. Try an assignment. – too honest for this site Sep 15 '17 at 13:48
  • @EugeneSh. I compile in Wandbox.org, with GCC and G11 (not the GNU extension). Olaf, that's what I am asking, an assigment will trigger an error to be generated, but my code doesn't! – gsamaras Sep 15 '17 at 13:52
  • 1
    @gsamaras: Once more: Enable **all** recommended warnings. There is definitively one which will warn about such empty statements (you can circumvent them by casting to `void`, but that is fine).. – too honest for this site Sep 15 '17 at 13:56
  • 3
    Due the nature of the question your [mcve] should include complete compilation command and compiler message output. – user694733 Sep 15 '17 at 13:59
  • 2
    This question is not about understanding void pointers, it's about understanding [a GCC extension](https://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html), which does not help understand anything about what the C standard says about `void*` (if anything it probably confuses things and makes you understand less). – Jonathan Wakely Sep 15 '17 at 14:08
  • @SouravGhosh, _"with `-std=c99 -pedantic`, only warning, not error"_ yes, that's because `-pedantic` causes warnings (which are still diagnostics!), not errors. Use `pedantic-errors` to get errors. – Jonathan Wakely Sep 15 '17 at 14:10
  • 1
    @JonathanLeffler-- are you sure that dereferencing a pointer to `void` must generate a compiler diagnostic? [§6.5.3.2/2](http://port70.net/~nsz/c/c11/n1570.html#6.5.3.2p2) only says: "The operand of the unary * operator shall have pointer type." Doesn't `(void *)` qualify, even though it is incomplete? – ad absurdum Sep 15 '17 at 14:28
  • 1
    @DavidBowling "shall have a pointer type" doesn't mean all pointer types are valid ("dereference → ptr" != "ptr → dereferencable") – Kevin Sep 15 '17 at 17:22
  • @Kevin-- I didn't say that it is valid to dereference a `void` pointer. It doesn't look to me like the Standard requires a _diagnostic message_ if an attempt to dereference a `void` pointer is made. The passage I cited is a constraint, and constraint violations must trigger diagnostic messages.... – ad absurdum Sep 16 '17 at 00:52

4 Answers4

12

The C standard explicitly states in 5.1.1.3p1:

A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined. Diagnostic messages need not be produced in other circumstances. 9)

With footnote 9 saying that

The intent is that an implementation should identify the nature of, and where possible localize, each violation. Of course, an implementation is free to produce any number of diagnostics as long as a valid program is still correctly translated. It may also successfully translate an invalid program.

So, GCC complies with the letter of the C standard. Your program is an invalid program. Only a diagnostics message is required - and the compiler is allowed to successfully translate your invalid program. As GCC has a non-standard extension for void pointer arithmetic:

In GNU C, addition and subtraction operations are supported on pointers to void and on pointers to functions. This is done by treating the size of a void or of a function as 1.

A consequence of this is that sizeof is also allowed on void and on function types, and returns 1.

The option -Wpointer-arith requests a warning if these extensions are used.

it decided that it might do something "sensible" with your invalid program and translated it successfully.


Notice that non-evaluated dereference of pointer-to-void is already required in sizeof, because:

void *foo;
sizeof *foo;

must match that of

sizeof (void);

They both evaluate to 1, so it is just easier allow the discarded dereference of pointers to void everywhere.


As Lundin says, if you want actual errors for constraint violations, use -std=c11 -pedantic-errors.

Community
  • 1
  • 1
  • The OP is not properly compiling as standard C, in which case you get an error about void pointer used in arithmetic. As I understand it, the question is about non-standard GCC only. – Lundin Sep 15 '17 at 14:07
  • Compiling properly with `-std=c11 -pedantic-errors` gives: `warning: dereferencing 'void *' pointer| error: pointer of type 'void *' used in arithmetic [-Wpointer-arith]| warning: dereferencing 'void *' pointer| warning: statement with no effect [-Wunused-value]|`. The OP does not compile like this so what the standard says doesn't apply. – Lundin Sep 15 '17 at 14:08
  • 3
    @Lundin a compliant compiler is allowed to successfully compile the code. It just is required that diagnostics is produced. Therefore `-pedantic` will be enough to achieve compliance. – Antti Haapala -- Слава Україні Sep 15 '17 at 14:09
  • @Lundin I am asking for Standard C! I compiled in Wandbox with `$ gcc prog.c -Wall -Wextra -std=c11 -pedantic-errors` and got the warning only. – gsamaras Sep 15 '17 at 14:12
  • 2
    @Lundin as this answer clearly says, only **a diagnostic message** is required, so a warning is standards conforming. – Jonathan Wakely Sep 15 '17 at 14:13
  • Yes I know. That's my point, the OP **did not get a diagnostic message for invalid pointer arithmetic**. – Lundin Sep 15 '17 at 14:14
  • 1
    @gsamaras your question doesn't mention `-pedantic-errors`. If that doesn't lead to an error, I'd report a bug against GCC... – Antti Haapala -- Слава Україні Sep 15 '17 at 14:14
  • It does give an error for one line: `prog.c:5:4: error: pointer of type 'void *' used in arithmetic [-Wpointer-arith]` – Jonathan Wakely Sep 15 '17 at 14:17
  • 2
    @Lundin, a warning is a diagnostic message – Jonathan Wakely Sep 15 '17 at 14:17
  • Yes because I didn't use. I saw @Lundin's comment and tried it out. I compiled with a simpler attempt `gcc prog.c -Wall -Wextra -pedantic-errors`, but yet failed to get an error...A bug you say? – gsamaras Sep 15 '17 at 14:17
  • 1
    @gsamaras link or it didn't happen. Your original code gives a pedantic error. Dereferencing the pointer and ignoring the result has no effect so is ignored. – Jonathan Wakely Sep 15 '17 at 14:18
  • @gsamaras `-std=c11`, otherwise you didn't pick a standard, and it uses `gnu11` or sth. – Antti Haapala -- Слава Україні Sep 15 '17 at 14:19
5

From C11 6.3.2.3 "void":

The (nonexistent) value of a void expression (an expression that has type void) shall not be used in any way, and implicit or explicit conversions (except to void) shall not be applied to such an expression. If an expression of any other type is evaluated as a void expression, its value or designator is discarded. (A void expression is evaluated for its side effects.)

So an expression can have void type, you just can't do anything with the result of that expression (such as assign it to something). *c is a valid expression that does nothing.

6.2.5/19 "Types"

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

6.5.6/2 "Additive operators"

For addition, either both operands shall have arithmetic type, or one operand shall be a pointer to a complete object type and the other shall have integer type.

And array subscripting is defined in terms of pointer arithmetic. So normally, the expression &c[0] would not be allowed.

GCC allows pointer arithmetic on (and therefore subscripting arrays of) void types as an extension.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
3

The only reason this compiles is because you are using the wrong GCC options - you are not compiling this with a strictly conforming C compiler but with GCC extensions.

Compiled properly with -std=c11 -pedantic-errors, we get:

warning: dereferencing 'void *' pointer|
error: pointer of type 'void *' used in arithmetic [-Wpointer-arith]|
warning: dereferencing 'void *' pointer|
warning: statement with no effect [-Wunused-value]|

void pointer de-referencing and arithmetic are gcc extensions. When such non-standard extensions are enabled, void* is treated as uint8_t* in terms of arithmetic, but you still can't de-reference it, because it is regarded as an incomplete type still.

As for what GCC does with this code in non-standard mode, with no optimizations enabled (Mingw/x86), nothing terribly exciting happens:

0x004014F0  push   %rbp
0x004014F1  mov    %rsp,%rbp
0x004014F4  sub    $0x30,%rsp
0x004014F8  callq  0x402310 <__main>
0x004014FD  mov    $0x4,%ecx
0x00401502  callq  0x402b90 <malloc>
0x00401507  mov    %rax,-0x8(%rbp)
0x0040150B  mov    $0x0,%eax
0x00401510  add    $0x30,%rsp
0x00401514  pop    %rbp
0x00401515  retq
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Check my updated question. Isn't that the command you are saying I should use? Maybe it's all Wandbox's fault? – gsamaras Sep 15 '17 at 14:25
  • @gsamaras The code in your screen shot is not the same as in the question. Use the same code and you should get the warnings/errors posted in this answer. – Lundin Sep 18 '17 at 09:36
2

in gcc, it's possible to perform pointer arithmetic on void * pointers (How void pointer arithmetic is happening in GCC)

And it's even possible to print sizeof(void) which is 1.

Your example issues a warning, but the line does nothing (like when you ignore a parameter by doing (void)a to avoid the "unused parameter" warning).

Try assigning something and gcc will complain

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

gives me 1 warning, and 1 error

test.c:9:3: warning: dereferencing 'void *' pointer
   *c = 'a';
   ^~
test.c:9:3: error: invalid use of void expression
   *c = 'a';
   ^

Or even use a volatile char cast on it:

test.c:9:3: error: invalid use of void expression
   (volatile char)*c;

So you can dereference it, but you cannot use the dereferenced value (also tried reading/assigning it, no way: you get "void value not ignored as it ought to be")

EDIT: semi-valid example (lifted from memcpy: warning: dereferencing ‘void *’ pointer)

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

  memcpy(d, &c[2], 2);

here you're dereferencing with an offset then you take the address again to get c+2 : that works because of gcc pointer arithmetic. Of course:

memcpy(d, ((char*)c)+2, 2);

is the way to avoid the warning and is compliant to the standard.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • 2
    It makes sense that this should be just optimized away if speaking of normal memory. But what if we define it as `volatile`? – Eugene Sh. Sep 15 '17 at 13:54
  • I think that @EugeneSh. comment is the essence here, since I already knew that about the assignment (sorry, question updated). Because my code does nothing the actual derefencing does not happen? Or it does, and somehow the compiler is able to work this trough, even though it doesn't know the underlying type? – gsamaras Sep 15 '17 at 13:56
  • 1
    The question is: what is the point of the void pointer dereferencing? Abuse of the language? gcc allows arithmetic only for the convenience sake. – 0___________ Sep 15 '17 at 13:58
  • Moreover, what @EugeneSh. said may be reflected in my updated code, which invokes a "no effect" warning for `&c[0];`, but not for `*c;`. – gsamaras Sep 15 '17 at 14:00
  • you have the "right" to dereference it probably because it does nothing useful or harmful. Since I always remove warnings in my code, for me this is a clear error. – Jean-François Fabre Sep 15 '17 at 14:01
  • Can you have a volatile rvalue? – Martin James Sep 15 '17 at 14:01
  • @gsamaras I have found a "useful" usage of dereferencing void*, see my edit – Jean-François Fabre Sep 15 '17 at 14:08
  • 1
    @gsamaras it does not actually dereference it. As I wrote it is only the pointer arithmetic assuming the byte size. IMO it is the abuse anyway. http://www.tutorialspoint.com/compile_c_online.php?PID=0Bw_CjBb95KQMbXZ6M3Y4UVlMSHM – 0___________ Sep 15 '17 at 14:10