Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Towards understanding void pointers

Tags:

c

pointers

gcc

void

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?

like image 434
gsamaras Avatar asked Sep 15 '17 13:09

gsamaras


People also ask

What are the advantages of using a void pointer?

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)

What is the difference between malloc and void pointers in C++?

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.

Is it possible to do arithmetic arithmetic with a void pointer?

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.

Why is it bad to call a void function from a put?

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.


2 Answers

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.

like image 144

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.

like image 27
Michael Burr Avatar answered Sep 18 '22 06:09

Michael Burr