Click here to Skip to main content
15,906,329 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
In C, I need a function to return a void * via one of its arguments. Assume that there's a database which holds pointers to any type of data. Once the user stores the pointers and in other parts of the program the pointers are required to be retrieved. The problem is that the pointers are of different types. Let's begin with an example:
C
#include <stdio.h>

#define _countof(ar) (sizeof(ar) / sizeof(ar[0]))

int count;
void *data[10];

int retrieve(int i, void **p2) {
    if (i < 0 || i > count) return -1;
    *p2 = data[i];
    return 0;
}

int save(void *p) {
    if (count == _countof(data)) return -1;
    data[count++] = p;
    return 0;
}

int main(void) {
    int n = 10;
    float f = 10.5;
    save(&n);
    save(&f);
    int *pn;
    retrieve(0, &pn);
    printf("%d\n", *pn);
    float *pf;
    retrieve(1, &pf);
    printf("%g\n", *pf);
    return 0;
}


In the code, the compiler issues the following warnings:

C
2.c: In function ‘main’:
2.c:26:15: warning: passing argument 2 of ‘retrieve’ from incompatible pointer type [-Wincompatible-pointer-types]
   26 |   retrieve(0, &pn);
      |               ^~~
      |               |
      |               int **
2.c:8:28: note: expected ‘void **’ but argument is of type ‘int **’
    8 | int retrieve(int i, void **p2) {
      |                     ~~~~~~~^~
2.c:29:15: warning: passing argument 2 of ‘retrieve’ from incompatible pointer type [-Wincompatible-pointer-types]
   29 |   retrieve(1, &pf);
      |               ^~~
      |               |
      |               float **
2.c:8:28: note: expected ‘void **’ but argument is of type ‘float **’
    8 | int retrieve(int i, void **p2) {
      |                     ~~~~~~~^~


Note that in calling save function there's no problem. Indeed we use void pointers to be treated as pointers to any type of data. But this is not true for **. How can I avoid getting this warning? Please don't suggest casting or any other workarounds. I need a solution, please. Why can't void ** be replaced with type ** while void * can be replaced with type *?!

What I have tried:

I tried casting the this the warning to be issued any more. But, this is not a solution. Instead it's a workaround.
Posted
Comments
jeron1 8-Apr-24 15:44pm    
Maybe this link may shed some light.
FAQ C Language Question 4.9[^]
ilostmyid2 8-Apr-24 16:22pm    
I didn't know about c-faq.com, thank you, but my problem is that how can I inform the callers of the function how to use it? How can I inform them that the pointer they're passing to the function should by of type "type **" not of type "type *"? Is there a way?
jeron1 8-Apr-24 16:58pm    
Perhaps have them use the 'intermediate' void* variable technique mentioned in that link.
ilostmyid2 8-Apr-24 17:45pm    
Assume that I'm writing a library which may be used with many ones around the world. So the code should describe itself. If I prototype the argument of the function in header as void *, the caller (user) can't know that the parameter should be passed as type ** and they may send it as type *, unless they read the documents I provide. I want this not to happen and the function describes itself.

I dont like your code at all. The mismatch of types is problematic. Also the use of global data leeds to bugs when your code may evolve.

tips:
- store data in some byte array and give the save function also the size of data, so it can memcpy the data.
C++
void save(void* data, int size);
which got called as:
C++
save((void*) &n, sizeof(int));
   save((void*)&f, sizeof(float));
And use your retrieve similar. Better is to name it load or restore.

At last: when you will save and restore more complex data than use structs (without inner pointers). Than this code is already the pattern for that.
 
Share this answer
 
Comments
merano99 9-Apr-24 10:33am    
+5, I also see the problem with insecure data types that are also accessed globally.
Especially with the announcement that it could possibly also be used by other developers with multithreading, I would save the length, copy the data and possibly also make the original data type checkable.
C++
int main()
{
   int n = 10;
   float f = 10.5;
   save((void*) & n);
   save((void*)&f);
   int* pn;
   retrieve(0, (void**)&pn);
   printf("%d\n", *pn);
   float* pf;
   retrieve(1, (void**)&pf);
   printf("%g\n", *pf);
   return 0;
}
 
Share this answer
 
Karsten has already addressed the problem of unsafe data types that are also accessed globally. Global variables and the limitation of a pointer field should be avoided. Especially if the code is to be used flexibly by other developers with as little adaptation effort as possible. In the following example, this is all solved dynamically and the data is copied so that the caller can also delete or overwrite it. Some effort would still be required to delete the list and to secure access for multithreading. In addition, the original data type and length could also be remembered to make the access verifiable later.
C
typedef struct nodetyp {
    void* dataptr;
    struct nodetype* next;
} node;

int save(node** head, void* p, size_t len)
{
    if (len == 0)
        return -1;

    node* newnode = (node*)malloc(sizeof(node));
    newnode->next = NULL;
    newnode->dataptr = (void*)malloc(len);
    memcpy(newnode->dataptr, p, len);

    if (*head == NULL) {
        *head = newnode;
        return 0;
    }

    node* tmp = *head;
    for ( ; tmp->next != NULL; tmp = tmp->next);
    tmp->next = newnode;
    return 0;
}

This approach could also be used with fields or structures without global variables.
C
int main(void) 
{
    node* head = NULL;
    int rtn = 0;
    int data1[5] = {1, 3, 6, 2, 4};
    double data2[5] = { 2.1, 3.4, 1.6, 7.2, 8.3 };

    rtn = save(&head, (void*)&data1[0], sizeof(data1));
    ...
 
Share this answer
 
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900