简体   繁体   中英

Can I get a pointer to a struct as a parameter from a function in C?

I have a type defined like this:

struct DynTab_s {
    int     object_count;
    //other fields
    void    *data;
};

typedef struct DynTab_s *Dyntab_t; //note that type I use is pointer to struct

I have a storage utility that I can user to store and retrieve them. I've chosen to support int, double and pointer type. I have a function to retrieve the int and double values by key:

int MyHashtableGet(MyHashtable_t Hashtable, void *Key, void *Value)
{   
    void    *Row = NULL;
    int     RetValue = -1;

    MakeRow(Hashtable, Key, NULL, &Row);
    if (!MyStoreSelect(Hashtable->TableId, Row))
    {
        switch (Hashtable->ValueType)
        {
        case MY_HASHTABLE_TYPE_INT: 
            *(int *)Value = *(int *)((char *)Row + Hashtable->KeySize);
            break;
        case MY_HASHTABLE_TYPE_POINTER:
            //after row below I can see the DynTab_t in the item when I cast it
            Value = *(void **)*(int **)((char *)Row + Hashtable->KeySize);
        break;
        }
    }
    MyFree(Row);

    return RetValue;
}

that work for int. But not for pointer when I try to get Dyntab_t. This works if I use function

void *MyHashtableGetPointer(MyHashtable_t Hashtable, void *Key)
{
    void        *Row = NULL;
    void        *RetValue = NULL;

    MakeRow(Hashtable, Key, NULL, &Row);
    if (!MyStoreSelect(Hashtable->TableId, Row))
        RetValue = *(void **)*(int **)((char *)Row + Hashtable->KeySize);

    MyFree(Row);

    return RetValue;
}

when I call it with:

int       Key = 1;
DynTab_t  MyTab;

MyTab = (DynTab_t)MyHashtableGetPointer(MyHashtable, &Key);

The question is can I at all use this MyHashtableGet to get DynTab_t item or does second parameter have to be void ** type? If yes, can you please provide the exact syntax to call and to MyHashtableGet in case of MY_HASHTABLE_TYPE_POINTER.

Thanks & BR -Matti

The question is can I at all use this MyHashtableGet to get DynTab_t item or does second parameter have to be void ** type?

The only difference (if you're storing the pointers like you store the int values) would be that when retrieving an int , you'd pass the address of an int variable; and when retrieving a pointer, you'd pass the address of a pointer variable. A void * can hold the address of anything (except functions sometimes) -- including other pointers. So the last parameter is fine as void * , as long as you handle it appropriately elsewhere.

I'm not sure what you're doing in your function, though. If you store the pointers the same way as the int s in your data structure, so that at the same level of indirection you'd have an int for the integer type and a void * for the pointer type, then why are they dereferenced to different levels?

    case MY_HASHTABLE_TYPE_INT: 
        *(int *)Value = *(int *)((char *)Row + Hashtable->KeySize);
        break;

In the above, it seems that ((char *)Row + Hashtable->KeySize) gets you the pointer to whatever value you've stored, though of the wrong pointer type. Then the (int *) casts to a pointer of your data's type ( int in this case), which you then dereference and assign to what Value points to.

    case MY_HASHTABLE_TYPE_POINTER:
        Value = *(void **)*(int **)((char *)Row + Hashtable->KeySize);
    break;

But here, you cast to int ** , dereference, cast to void ** , then dereference again, and assign to Value instead of what Value points at? Isn't that one too many dereferences? Shouldn't you assign to the target of Value rather than Value ? And why do you need to cast to int ** at all? I think it should be more like this:

    case MY_HASHTABLE_TYPE_POINTER:
        *(void **)Value = *(void **)((char *)Row + Hashtable->KeySize);
        break;

Then, when calling to get an int:

...
int       Val;
MyHashtableGet(Table, Key, &Val);

...and when calling to get a pointer:

...
void      *Val;
MyHashtableGet(Table, Key, &Val);

Edit: This assumes one of two things, though: That the the variable you passed the address of in Value is void * , or that the variable you passed the address of is of a type that is internally represented the same way as void * (often true, but not guaranteed). If you want to rely on the pointer type being converted on assignment (in case their representations differ), you could implement your MyHashtableGetPointer() function as a wrapper for MyHashtableGet() :

void *MyHashtableGetPointer(MyHashtable_t Hashtable, void *Key)
{
  void *res = NULL;
  MyHashtableGet(Hashtable, Key, &res);
  return res;
}

It really depends on how you want to do it. You can do it using a reference (only in C++) or a pointer (C and C++):

void changeTheParamRef(struct myStruct &s)
{
    myStruct other;
    s = other; // or
    s.something = other.something;
}

void changeTheParamPtr(struct myStruct *s)
{
    myStruct other;
    *s = other; // or
    s->something = other.something;
}

void allocStruct(struct myStruct **s)
{
    *s = malloc(sizeof(myStruct));
}

You don't need a pointer to a pointer unless you'd like to return or change a pointer instead of a value.

To call above samples:

myStruct s;
changeTheParamRef(s);
changeTheParamPtr(&s);

myStruct *p;
allocStruct(&p);
changeTheParamRef(*p);
changeTheParamPtr(p);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM