Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I correctly use a void pointer in C?

Can someone explain why I do not get the value of the variable, but its memory instead?

I need to use void* to point to "unsigned short" values.

As I understand void pointers, their size is unknown and their type is unknown.
Once initialize them however, they are known, right?

Why does my printf statement print the wrong value?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void func(int a,  void *res){
    res = &a;
    printf("res = %d\n", *(int*)res);
    int b;
    b = * (int *) res;
    printf("b =%d\n", b);
}


int main (int argc, char* argv[])
{
    //trial 1
    int a = 30;
    void *res = (int *)a;
    func(a, res);
    printf("result = %d\n", (int)res);
    //trial 2
    unsigned short i = 90;
    res = &i;
    func(i, res);
    printf("result = %d\n", (unsigned short)res);
    return 0;
}

The output I get:

res = 30
b =30
result = 30
res = 90
b =90
result = 44974
like image 880
user900785 Avatar asked Dec 09 '11 16:12

user900785


4 Answers

One thing to keep in mind: C does not guarantee that int will be big enough to hold a pointer (including void*). That cast is not a portable thing/good idea. Use %p to printf a pointer.

Likewise, you're doing a "bad cast" here: void* res = (int*) a is telling the compiler: "I am sure that the value of a is a valid int*, so you should treat it as such." Unless you actually know for a fact that there is an int stored at memory address 30, this is wrong.

Fortunately, you immediately overwrite res with the address of the other a. (You have two vars named a and two named res, the ones in main and the ones in func. The ones in func are copies of the value of the one in main, when you call it there.) Generally speaking, overwriting the value of a parameter to a function is "bad form," but it is technically legal. Personally, I recommend declaring all of your functions' parameters as const 99% of the time (e.g. void func (const int a, const void* res))

Then, you cast res to an unsigned short. I don't think anybody's still running on a 16-bit address-space CPU (well, your Apple II, maybe), so that will definitely corrupt the value of res by truncating it.

In general, in C, typecasts are dangerous. You're overruling the compiler's type system, and saying: "look here, Mr Compiler, I'm the programmer, and I know better than you what I have here. So, you just be quiet and make this happen." Casting from a pointer to a non-pointer type is almost universally wrong. Casting between pointer types is more often wrong than not.

I'd suggest checking out some of the "Related" links down this page to find a good overview of how C types an pointers work, in general. Sometimes it takes reading over a few to really get a grasp on how this stuff goes together.

like image 200
BRPocock Avatar answered Sep 21 '22 09:09

BRPocock


(unsigned short)res

is a cast on a pointer, res is a memory address, by casting it to an unsigned short, you get the address value as an unsigned short instead of hexadecimal value, to be sure that you are going to get a correct value you can print

*(unsigned short*)res

The first cast (unsigned short*)res makes a cast on void* pointer to a pointer on unsigned short. You can then extract the value inside the memory address res is pointing to by dereferencing it using the *

like image 22
Mansuro Avatar answered Sep 20 '22 09:09

Mansuro


If you have a void pointer ptr that you know points to an int, in order to access to that int write:

int i = *(int*)ptr;

That is, first cast it to a pointer-to-int with cast operator (int*) and then dereference it to get the pointed-to value.

You are casting the pointer directly to a value type, and although the compiler will happily do it, that's not probably what you want.

like image 32
rodrigo Avatar answered Sep 19 '22 09:09

rodrigo


A void pointer is used in C as a kind of generic pointer. A void pointer variable can be used to contain the address of any variable type. The problem with a void pointer is once you have assigned an address to the pointer, the information about the type of variable is no longer available for the compiler to check against.

In general, void pointers should be avoided since the type of the variable whose address is in the void pointer is no longer available to the compiler. On the other hand, there are cases where a void pointer is very handy. However it is up to the programmer to know the type of variable whose address is in the void pointer variable and to use it properly.

Much of older C source has C style casts between type pointers and void pointers. This is not necessary with modern compilers and should be avoided.

The size of a void pointer variable is known. What is not known is the size of the variable whose pointer is in the void pointer variable. For instance here are some source examples.

// create several different kinds of variables
int iValue;
char aszString[6];
float  fValue;

int  *pIvalue = &iValue;
void *pVoid = 0;

int iSize = sizeof(*pIvalue);   // get size of what int pointer points to, an int

int vSize = sizeof(*pVoid);     // compile error, size of what void pointer points to is unknown
int vSizeVar = sizeof(pVoid);   // compiles fine size of void pointer is known

pVoid = &iValue;        // put the address of iValue into the void pointer variable
pVoid = &aszString[0];  // put the address of char string into the void pointer variable
pVoid = &fValue;        // put the address of float into the void pointer variable

pIvalue = &fValue;      // compiler error, address of float into int pointer not allowed

One way that void pointers have been used is by having several different types of structs which are provided as an argument for a function, typically some kind of a dispatching function. Since the interface for the function allows for different pointer types, a void pointer must be used in the argument list. Then the type of variable pointed to is determined by either an additional argument or inspecting the variable pointed to. An example of that type of use of a function would be something like the following. In this case we include an indicator as to the type of the struct in the first member of the various permutations of the struct. As long as all structs that are used with this function have as their first member an int indicating the type of struct, this will work.

struct struct_1 {
    int iClass;  // struct type indicator.  must always be first member of struct
    int iValue;
};

struct struct_2 {
    int   iClass;  // struct type indicator.  must always be first member of struct
    float fValue;
};

void func2 (void *pStruct)
{
    struct struct_1 *pStruct_1 = pStruct;
    struct struct_2 *pStruct_2 = pStruct;

    switch (pStruct_1->iClass)  // this works because a struct is a kind of template or pattern for a memory location
    {
    case 1:
        // do things with pStruct_1
        break;
    case 2:
        // do things with pStruct_2
        break;
    default:
        break;
    }
}

void xfunc (void)
{
    struct struct_1 myStruct_1 = {1, 37};
    struct struct_2 myStruct_2 = {2, 755.37f};

    func2 (&myStruct_1);
    func2 (&myStruct_2);
}

Something like the above has a number of software design problems with the coupling and cohesion so unless you have good reasons for using this approach, it is better to rethink your design. However the C programming language allows you to do this.

There are some cases where the void pointer is necessary. For instance the malloc() function which allocates memory returns a void pointer containing the address of the area that has been allocated (or NULL if the allocation failed). The void pointer in this case allows for a single malloc() function that can return the address of memory for any type of variable. The following shows use of malloc() with various variable types.

void yfunc (void)
{
    int *pIvalue = malloc(sizeof(int));
    char *paszStr = malloc(sizeof(char)*32);

    struct struct_1 *pStruct_1 = malloc (sizeof(*pStruct_1));
    struct struct_2 *pStruct_2Array = malloc (sizeof(*pStruct_2Array)*21);

    pStruct_1->iClass = 1; pStruct_1->iValue = 23;

    func2(pStruct_1);  // pStruct_1 is already a pointer so address of is not used

    {
        int i;
        for (i = 0; i < 21; i++) {
            pStruct_2Array[i].iClass = 2;
            pStruct_2Array[i].fValue = 123.33f;
            func2 (&pStruct_2Array[i]);  // address of particular array element.  could also use func2 (pStruct_2Array + i)
        }
    }
    free(pStruct_1);
    free(pStruct_2Array);   // free the entire array which was allocated with single malloc()
    free(pIvalue);
    free(paszStr);
}
like image 31
Richard Chambers Avatar answered Sep 18 '22 09:09

Richard Chambers