Могу ли я получить указатель на структуру в качестве параметра из функции в C? - PullRequest
0 голосов
/ 25 ноября 2011

У меня есть тип, определенный следующим образом:

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

У меня есть утилита хранения, которую я могу использовать для хранения и извлечения.Я решил поддерживать int, double и тип указателя.У меня есть функция для получения значений int и double по ключу:

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;
}

, которые работают для int.Но не для указателя, когда я пытаюсь получить Dyntab_t.Это работает, если я использую функцию

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;
}

, когда я вызываю ее с помощью:

int       Key = 1;
DynTab_t  MyTab;

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

Вопрос в том, могу ли я вообще использовать этот MyHashtableGet для получения элемента DynTab_t, или второй параметр долженбыть недействительным ** типа?Если да, можете ли вы предоставить точный синтаксис для вызова и MyHashtableGet в случае MY_HASHTABLE_TYPE_POINTER.

Спасибо & BR -Matti

Ответы [ 2 ]

1 голос
/ 25 ноября 2011

Вопрос в том, могу ли я вообще использовать этот MyHashtableGet для получения элемента DynTab_t или второй параметр должен иметь тип void **?

Единственное отличие (если вы хранитеуказатели, такие как вы храните значения int), будут такими, что при извлечении int вы передадите адрес переменной int;и при получении указателя вы передадите адрес переменной указателя.void * может содержать адрес чего угодно (кроме функций), включая другие указатели.Поэтому последний параметр подходит как void *, если вы правильно его обрабатываете в другом месте.

Хотя я не уверен, что вы делаете в своей функции.Если вы храните указатели так же, как int s в вашей структуре данных, так что на том же уровне косвенности у вас будет int для целочисленного типа и void * для типа указателя, тогдапочему они разыменовываются на разные уровни?

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

В приведенном выше примере кажется, что ((char *)Row + Hashtable->KeySize) возвращает указатель на любое сохраненное вами значение, хотя и с неверным типом указателя.Затем (int *) приводит к указателю типа ваших данных (int в данном случае), который вы затем разыменовываете и присваиваете то, на что указывает Value.

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

Но здесь вы бросаетена int **, разыменование, приведение к void **, затем разыменование снова и присвоение Value вместо того, на что указывает Value?Разве это не слишком много разыменований?Разве вы не должны назначить цели Value, а не Value?И зачем вообще нужно приводить к int **?Я думаю, что это должно быть больше так:

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

Затем, при вызове для получения int:

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

... и при вызове, чтобы получить указатель:

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

Редактировать: Это предполагает одну из двух вещей: что переменная, адрес которой вы передали в Value, равна void * или переменная, которой вы передали адресимеет тип, который внутренне представлен так же, как void * (часто это правда, но не гарантируется).Если вы хотите полагаться на тип указателя, преобразуемый при присваивании (в случае, если их представления различаются), вы можете реализовать свою функцию MyHashtableGetPointer() в качестве оболочки для MyHashtableGet():

void *MyHashtableGetPointer(MyHashtable_t Hashtable, void *Key)
{
  void *res = NULL;
  MyHashtableGet(Hashtable, Key, &res);
  return res;
}
0 голосов
/ 25 ноября 2011

Это действительно зависит от того, как вы хотите это сделать. Вы можете сделать это, используя ссылку (только в C ++) или указатель (C и 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));
}

Вам не нужен указатель на указатель, если вы не хотите возвращать или изменять указатель вместо значения.

Для вызова вышеуказанных образцов:

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

myStruct *p;
allocStruct(&p);
changeTheParamRef(*p);
changeTheParamPtr(p);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...