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:
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?
A void pointer can hold address of any type and can be typcasted to any type. int a = 10; char b = 'x'; void *p = &a; p = &b; Advantages of void pointers: 1) malloc () and calloc () return void * type and this allows these functions to be used to allocate memory of any data type (just because of void *) int main (void)
In C++, we must explicitly typecast return value of malloc to (int *). 2) void pointers in C are used to implement generic functions in C. For example compare function which is used in qsort (). 1) void pointers cannot be dereferenced. For example the following program doesn’t compile. The following program compiles and runs fine.
2) The C standard doesn’t allow pointer arithmetic with void pointers. However, in GNU C it is allowed by considering the size of void is 1. For example the following program compiles and runs fine in gcc.
That is bad. It's bad because calling a void function, the caller doesn't need to provide a return value space for it, while puts expects one to be there, so puts will write the return value where it expects to find it, but in reality that space belongs to something else. This is called stack corruption. 5.
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 avoid
or of a function as1
.A consequence of this is that
sizeof
is also allowed onvoid
and on function types, and returns1
.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
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With