warning: dereferencing `void *' pointer

P

Pushkar Pradhan

I have some functions which take as i/p a buffer (it can be float, char,
or 16 bit, int etc.). The result is another o/p buffer, its type is also
flexible (it could be a float, char etc.).

I try to pass both as "void *buf" so that it can accept any data type.
But since I access the buffer and try to assign its elements to another
I get compile errors (I have pasted at the end).

Now my question is how can I pass the i/p and o/p buffers as args to the
function without hardcoding the data type, someone earlier suggested to
pass another argument which tells the type, e.g.
myfunction(void * buf, 1, ...) 1 may indicate that buf is char type, 2
means 16 bit etc.

But that means having if .. else's inside the function to do the same
thing. Is there any other way?
Maybe this is not possible in C.
Pushkar Pradhan


resample.c: In function `resample':
resample.c:26: warning: dereferencing `void *' pointer
resample.c:26: warning: dereferencing `void *' pointer
resample.c:26: invalid use of void expression
resample.c:35: warning: dereferencing `void *' pointer
resample.c:35: warning: dereferencing `void *' pointer
resample.c:35: invalid use of void expression
 
C

Chris Torek

I have some functions which take as i/p a buffer (it can be float, char,
or 16 bit, int etc.). The result is another o/p buffer, its type is also
flexible (it could be a float, char etc.).

I try to pass both as "void *buf" so that it can accept any data type.
But since I access the buffer and try to assign its elements to another
I get compile errors (I have pasted at the end).

Now my question is how can I pass the i/p and o/p buffers as args to the
function without hardcoding the data type, someone earlier suggested to
pass another argument which tells the type, e.g.
myfunction(void * buf, 1, ...) 1 may indicate that buf is char type, 2
means 16 bit etc.

But that means having if .. else's inside the function to do the same
thing. Is there any other way?

Here is a bit pattern, 32 bits long:

11000101001110101001110101000110

What does it mean? What value(s) does it represent?

Does your answer change if I tell you it is the raw bits of a 32-bit
"float"? What if I say it is a 32-bit VAX float, or perhaps a 32-bit
SPARC float? Suppose I say it is, instead, a 32-bit "int", or a
32-bit "signed int". Now what value(s) can it represent? What if
it is an array of four "char"s or four "unsigned char"s?

(Finding answers -- there may be more than one -- to the above
questions is a good and useful exercise, that will help you understand
how the system(s) you use interpret bits. Note that different
systems interpret the same bits differently! One reason we use
"high level" source code -- although C is relatively low level for
a high level language -- is to avoid having to specify the precise
set of bits, which change from system to system, when we can get
away with specifying only what we want the bits to mean. The
source-code number "3.1415" or "42" has a definite meaning, and
the fact that machines A and B use different bit-patterns to
represent them is -- we hope -- irrelevant.)

Note that all of these are 32 bits long. They all look exactly the
same when treated as a 32-bit-long bit-string. But they all represent
different values.

When you use "void *" in C, you are -- usually -- implicitly using
a "bag of uninterpreted bits" type. Any interpretation necessarily
arises from giving the "bag of bits" some more-specific type. You
must decide whether you intend to interpret the bits, and if so, how.
This will dictate the type(s) you must provide.

Note that a series of if/else's, or a switch, inside a function
that receives a "void *" but wants to interpret the bits as one
of (say) the two possibilities "int" and "float" will result in C
code looking like:

enum whichtype { TY_INT, TY_FLOAT };
/*
* Here p points to the first bit(s) in the bag-o-bits, "len"
* is the number of items of some interpreted type, and "ty"
* is the enumeration saying which type.
*/
void f(void *p, size_t len, enum whichtype ty) {
size_t i;
int *ip;
float *fp;

switch (ty) {
case TY_INT:
ip = p;
... work with ip, where i is between 0 and len-1, e.g:
for (i = 0; i < len; i++)
ip *= 2;
break;

case TY_FLOAT:
fp = p;
... work with fp, where i is between 0 and len-1 ...
for (i = 0; i < len; i++)
fp *= 2.0;
break;

default:
panic("invalid type argument %d to f()", (int)ty);
/* NOTREACHED */
}
}

Not only do you really, truly have to write the if/else or switch,
you will also find that, on typical machines, it compiles to code
that uses different machine instructions to manipulate the data.
The code using ip might look like, e.g.:

# this doubles ip, where i is in %eax and ip is in %edx
movl (%edx,%eax,4),%ecx
addl %ecx,%ecx
movl %ecx,(%edx,%eax,4)

versus:

# this doubles fp, where i is in %eax and fp is in %edx
flds (%edx,%eax,4)
fadds (%edx,%eax,4)
fstps (%edx,%eax,4)

These completely-different machine instructions are utterly necessary
here -- using the "addl %ecx,%ecx" instructions will not result in
doubling a "float" value in fp, but rather will just make a mess
of the value.

Thus, different C code is required because different machine code
is required. Using "void *" absolves neither you nor the compiler
of this.

If you do enough of this sort of thing (or analyse your systems
carefully), you will find that there are cases where the C source
code must be different, yet the underlying machine code on your
machine(s) is the same. This happens because (and when) the abstract
meaning defined in C for two different types happens to be implemented
using a single underlying machine-level mechanism. But in general,
the C code should remain separate, because on some *other* machine(s),
this may no longer be the case. In other words, in the C code,
you write *what* you want to have happen, not *how* to *make* it
happen. If you want different things, at the C level, that all
happen to be done the same way at the machine level, it is the
compiler's job to discover this and optimize the code appropriately.
(Some compilers are better than others at this, and you may sometimes
find yourself having to "help" some compilers a bit. Do this only
when you really know what you are doing. :) )
 
P

pete

Pushkar said:
I have some functions which take as i/p a buffer (it can be float, char,
or 16 bit, int etc.). The result is another o/p buffer, its type is also
flexible (it could be a float, char etc.).

I try to pass both as "void *buf" so that it can accept any data type.
But since I access the buffer and try to assign its elements to another
I get compile errors (I have pasted at the end).

Now my question is how can I pass the i/p and o/p buffers as args to the
function without hardcoding the data type, someone earlier suggested to
pass another argument which tells the type, e.g.
myfunction(void * buf, 1, ...) 1 may indicate that buf is char type, 2
means 16 bit etc.

But that means having if .. else's inside the function to do the same
thing. Is there any other way?
Maybe this is not possible in C.

It depends on exactly what your are trying to do with the buffer.
If you wanted to do something simple,
like reverse the order of the elements,
it could be done this way:

#include <stddef.h>
void some_functions(void *buf, size_t nmemb, size_t size)
{
unsigned char *first = buf;
unsigned char *last = (nmemb - 1) * size + first;
unsigned char *p1, *p2, swap, *end;

while (last > first) {
p1 = last;
p2 = first;
end = p2 + size;
do {
swap = *p1;
*p1++ = *p2;
*p2++ = swap;
} while (p2 != end);
last -= size;
first += size;
}
}

If your function is going to do something very different,
for each different data type that the buffer may contain,
then you'll need something like a switch;
perhaps an ifelse tree, or an array of function pointers.

qsort has this prototype:

#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));

The arguments base, nmemb and size,
tell qsort everything that it needs to know about the array.
The compar pointer, tells qsort exactly how to deal with the array.

If your function
is going to do something different with each data type,
then you can pass it a function pointer also,
in a manner similar to the way that one is passed to qsort.
 
P

pete

pete said:
Pushkar Pradhan wrote:

To do just that one thing,
first, calculate the total number of bytes,
and then #include <string.h>, and use memcpy() or memmove().
 
G

Guest

| I have some functions which take as i/p a buffer (it can be float, char,
| or 16 bit, int etc.). The result is another o/p buffer, its type is also
| flexible (it could be a float, char etc.).
|
| I try to pass both as "void *buf" so that it can accept any data type.
| But since I access the buffer and try to assign its elements to another
| I get compile errors (I have pasted at the end).
|
| Now my question is how can I pass the i/p and o/p buffers as args to the
| function without hardcoding the data type, someone earlier suggested to
| pass another argument which tells the type, e.g.
| myfunction(void * buf, 1, ...) 1 may indicate that buf is char type, 2
| means 16 bit etc.

Passing them as (void*) should be OK. The problem is in _using_ them as
(void*). You need to first cast the pointer to the desired type, or assign
the pointer to a variable defined to have that type. If you are interpreting
the data as if it were the byte/bit pattern for (double) in your machine's
(double) format, then cast your pointer to (double*) and dereference. Note
that some machines will require the alignment be correct and will warn even
if the alignment really is correct since it might not know what the run time
really would done. And if it might not be correct, your program could fail.
In that case you might have to memcpy() the data from the buffer to a variable
of the correct type. Or you can use a union of mixed types.

But even if you want to merely copy the data raw without interpreting it,
you are really still "interpreting" it as characters (in order to step
through each one by one to do the copy). So you'll need to cast to (char*)
to do that, or call memcpy() which does it for you (it's arguments are
(void*) but it treats them as (char*) internally).


| But that means having if .. else's inside the function to do the same
| thing. Is there any other way?
| Maybe this is not possible in C.

Possible and I have done it many times.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top