Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

What is the use of void***?? 5

Status
Not open for further replies.

jyotsna24

Programmer
Jun 24, 2001
15
0
0
IN
Hi,
What is the use of void*** in C?
Thanx in advance.
 
int x()/*int here means function returns an integer*/
{
/*core here*/
return 0;/*returning an integer*/
}
void x()/*void here means function returns nothing*/
{
/*core here*/
}
void x(int c)/*int here means function take an integer parameter*/
{
/*code here*/
}
int x(void)/*void here means function take nothing as a parameter(parameters)*/
{
/*code here*/
}

int a;
char b;
int* x;
char* y;
void* z;
x=&a;/*good*/
y=&b;/*good*/
y=&a;/*error*/
x=&b;/*error*/
z=&a;/*correct but you could get a warning*/
z=&b;/*correct but you could get a warning*/
z=y;/*correct but you could get a warning*/
z=x;/*correct but you could get a warning*/
z=(void*)&a;/*correct without warnings*/
z=(void*)&b;/*correct without warnings*/
z=(void*)y;/*correct without warnings*/
z=(void*)x;/*correct without warnings*/
John Fill
1c.bmp


ivfmd@mail.md
 
you've confused even me!!

what can you possibly do with a void*??
 
void* is some thing what know nothing about types. With void* you can woork directly in binary mode. John Fill
1c.bmp


ivfmd@mail.md
 
I have used void* in various places. The best example I can think of is in a struct with pointers to different types. This is a basic example:

enum TYPES{INTEGER,CHARACTER,FLOAT,STRING};

struct myStruct{
void* ptr;
TYPES type;
}

main()
{
...
int x = 3;
char c = 'A';
float f = 3.14;
char s[] = "Hello";

myStruct ms[] = [
{&x,INTEGER},
{&c,CHARACTER},
(&f,FLOAT},
{&s,STRING}
}

...

switch(ms.type)
{
case INTEGER:
cout<<&quot;Integer = &quot;<<(int)*ptr;
case CHARACTER:
cout<<&quot;Character = &quot;<<(char)*ptr
case FLOAT:
cout<<&quot;Float = &quot;<<(float)*ptr
case STRING:
cout<<&quot;String = &quot;<<(char*)*ptr;
}


that is your basics and please remember breaks as i did not hehe.

Matt
 
oops... to expand on the above. All a void*** would be is a void pointer to a 3 dimentional array. Tough to visualize. First try to understand char*** to see the application.

int array[2][2][2] = {1,2,3,4,5,6};

// 0,0,0 = 1
// 0,0,1 = 2
// 0,1,0 = 3
etc... (verify this)

This could be useful in graphics with 3d programming i would assume.

matt
 
A pointer to void is a way of expressing a generic pointer type. In C, a pointer to any object of any type may be converted back and forth between a pointer to void implicitly without any loss of information.

So, you can do:

void *ptr;
int i=5;
float f=2.0;
double d=5.5;
char *s=&quot;string&quot;;

ptr=&i;
i=*ptr;
ptr=&f;
f=*ptr;
ptr=&d;
d=*ptr;
ptr=s;
s=ptr;

The value of i, f, d and s are guaranteed to be unaffected by the conversion to a void pointer and back again.

This capability makes a pointer to void a good candidate for the storage of generic objects as Matt describes in his post above.

Consider a simple stack implementation that makes no assumptions about the objects that will eventually be placed on the stack. This is a very powerful mechanism that can be applied to other ADTs such as linked lists, binary search trees, queues, etc.

/*** All code untested ***/

You might have a struct like this to represent the stack:

struct stack {
void **items; /* items in the stack */
int count; /* # of items in the stack */
int capacity; /* size of stack */
};

You might initialize the stack like this:

struct stack *
init(int capacity)
{
struct stack *obj=malloc(sizeof *obj);
if (obj) {
obj->items=malloc(sizeof *obj->items * capacity);
if (!obj->items) {
free(obj); obj=NULL;
} else {
obj->capacity=capacity;
obj->count=0;
}
}
return obj;
}

Then the obligatory &quot;push&quot; and &quot;pop&quot; functions:

int
push(struct stack *obj,void *item,size_t size)
{
if (obj->count==obj->capacity) {
/* grow stack here (see below) */
}
obj->items[obj->count]=malloc(size);
if (obj->items[obj->count]) {
memcpy(obj->items[obj->count],item,size);
++obj->count;
} else {
return -1; /* error */
}
return 0;
}

void *
pop(struct stack *obj)
{
void *item;

if (!obj->count)
return NULL;
/* Get the item at the top of the stack */
item=obj->items[obj->count-1];
/* Set the pointer that pointed to this item
* to NULL to &quot;erase&quot; the item from the stack */
obj->items[--obj->count]=NULL;
/* User must eventually free memory
* for item */
return item;
}

Now, a use for void ***. Above we omitted the code for growing a stack. Here's a function which might perform this task:

int
grow_stack(void ***items,int new_size)
{
char **tmp;

tmp=realloc(*items,new_size);
if (tmp) {
*items=tmp;
return 0;
} else {
return -1;
}
}

To call grow_stack() we need to pass a pointer to our stack items variable so that the realloc() call gets assigned to the *actual* items pointer rather than the local pointer in grow_stack(). We would call grow_stack() from our push() function like this (doubling the size of the stack):

if (grow_stack(&obj->items,obj->capacity*2)) {
/* success */

Also, look into the standard library function qsort(). Its final argument is a pointer to a function that accepts 2 const pointers to void(). This is because qsort() is a generic routine that can sort an array of objects of any type. This is made possible through void pointers.
Russ
bobbitts@hotmail.com
 
Well essentially, the void is used for declaring a prototype, on its own it makes no sense.

so in forward declarations of functions, (ie., declaration comes before definition) while declaring functions that return nothing they are preceded by a void to explain to the compiler that the function returns NOTHING.

Now, in the beautiful example Zyrenthian has provided, he has nicely explained that if u have such a case where u don't know what type of variable u are going to use, but u need to declare a variable anyway, what do u do ? u declare it as a void, and later in the code u typecast it to the data type to suit u'r requirement. so u can use it as void, void*, void**, whatever, but declaring it as such means u are declaring a Prototype, u have to typecast it later.
 
In C, no typecast is required between pointers to void and pointers to other object types. This is why typecasting the return value of malloc() isn't necessary, provided that a proper prototype is in scope.

In C++, however, this typecast is required.
Russ
bobbitts@hotmail.com
 
A nice programming style require correct tipecasting always when it is tipe reassigning. John Fill
1c.bmp


ivfmd@mail.md
 
In Zyrenthian example there is no need in void*** to pass 3 dimension array, since you work with specific type you have to cast your void argument to this type, and in this case void* will do the same job as void***, even less confusing.

rbobbit example is much more better.
void*** and so on (void**** ....) is very useful when you want to store some date with no type (or different type in the same array, stack ...), when all what you know is the size.

Some silly example:
In 10 level building on each flour there are 50 rooms in each room there are 100 safety boxes.
void *Boxes[10][50][100];
Somebody on 5 flour from 45 room ask to put in box 23 his 10 byte information.
Boxes[10][50][23] = malloc (10); allocate room;
memcpy(Boxes[10][50][23], HisData,10); put his data
.....
We don't want to know what it is, we will return it to him when hi ask.
Regards.
 
I'd have to disagree with John w.r.t to typecasting being a good programming style. It's certainly a good style when the cast is needed AND the cast is reasonable (e.g. the cast isn't placed there just to silence the compiler when it's warning you about an illegal conversion that's liable to cause a bus error). But it seems pointless when the cast isn't required. It is part of C that you're allowed to convert pointers to void to any object type and back again. It's rather like doing:

void foo(int bar)
{
/* ... */
}

/* ... */

char a=10;

foo((int)a);

This is silly, the default argument promotions in C will automatically convert a to an int before it is passed to foo(), so the cast doesn't gain you anything.

Just as in:

char *a=(char *)malloc(10);

The pointer to void that malloc returns can be *implicitly* assigned to a pointer of any object type. The cast doesn't gain you anything here either.

The only benefit I can see is if you intend to use your C code in a C++ program. However, in these cases I prefer to change the code later when I actually need to use it in a C++ program. YMMV
Russ
bobbitts@hotmail.com
 
rbobbit,
I agree that is not a good practice to hide real arguments type, but some time there is a sence in it.
For example I have 3 modules each of them has a public header and each C file includes two of other headers. Now, my first module call function from second and pass pointer to his structure, my second module pass this pointer to third module. Since my second module do nothing with this structure I don't want him depends from first module and I can skip including of first public header in second module and hide argument that is realy a pointer to structure to pointer to void.
In this case I don't have to recompile module N2 if public header (with this structure definition) of first module has changed.
It has no sence in small project, but if your ptoject includes 100 or more modules it can safe a lot of time on re-compilation.
!!! But don't forget to put real type in comments to this function.

About C and C++. I went through C to C++ transmition and I would prefered that C code had this casting, but it did not take long to fixed it.

 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top