2010-04-24 16 views
20

Załóżmy, że mam następujące struktura i funkcja powrotu wskaźnika:Wracając wskaźnik struct

typedef struct { 
    int num; 
    void *nums; 
    int size; 
} Mystruct; 

Mystruct *mystruct(int num, int size) 
{ 
    //Is the following correct? Is there a more efficient way? 
    Mystruct mystruct; 
    mystruct.num = num; 
    mystruct.size = size; 
    mystruct.nums = malloc(num*sizeof(size)); 
    Mystruct *my; 
    *my = mystruct; 
    return my; 
} 

chcę definiować dowolny wskaźnik Mystruct stosując powyższą funkcję. Czy powinienem zadeklarować zmienną Mystruct, zdefiniować właściwości Mystruct, przypisać do niej wskaźnik i natychmiast zwrócić wskaźnik lub zdefiniować właściwości właściwości mystruct przez wskaźnik?

+1

Być może można zakodować obie opcje. Z opisu nie wynika jasno, co zamierzacie. –

+0

I pamiętaj ... nigdy nie propaguj wskaźnika "ze stosu" w górę. –

Odpowiedz

34

powinienem zadeklarować zmienną Mystruct, określać właściwości Mystruct, przypisać wskaźnik do niego i powrót wskaźnika

Zdecydowanie nie, ponieważ zmienna zdefiniowana w funkcji (w "auto" klasa pamięci) zniknie, gdy funkcja zakończy działanie, a zwrócisz wskaźnik zwisający.

Użytkownik może zaakceptować wskazówkę na Mystruct (należy do tego odpowiedzialność osoby dzwoniącej) i wypełnić ją; lub możesz użyć malloc, aby utworzyć nowy (odpowiedzialność osoby dzwoniącej za uwolnienie jej po zakończeniu). Druga opcja przynajmniej pozwala zachować podpisu funkcji wydają się być chętni na:

Mystruct *mystruct(int num, int size) 
{ 
    Mystruct *p = malloc(sizeof(MyStruct)); 
    .... 
    return p; 
} 

ale jest często gorszy jeden - ponieważ rozmówca musi mieć obowiązki tak, może również iść z pierwszej opcji i potencjalnie zwiększyć wydajność (jeśli wywołujący może użyć instancji automatycznej klasy, ponieważ wie, że zakres zastosowania jest ograniczony).

+0

dlaczego wpisałeś malloc z (Mystruct *)? Czy to konieczne? – idealistikz

+0

@idealistikz: Nie, to nie jest konieczne. – sth

+5

Nie jest to konieczne w C (i często jest uważany za zły styl w tym języku) - ale odwrotność jest prawdą w C++. – caf

2

Przydzielanie nowego Mystruct i powrót wskaźnika do niej zazwyczaj wyglądają mniej więcej tak:

Mystruct *mystruct(int num, int size) 
{ 
    Mystruct *result; 

    result = malloc(sizeof(MyStruct)); 
    if (!result) 
    return NULL; 

    result->num = num; 
    ... 

    return result; 
} 

Później, gdy skończysz z Mystruct przydzielonego tutaj z malloc, należy ponownie zwolniona z free().

Samo zadeklarowanie zmiennej lokalnej i zwrócenie wskaźnika do tej zmiennej lokalnej nie będzie działać. Zmienna lokalna wykracza poza zakres na końcu funkcji, a pamięć, w której była przechowywana, jest najprawdopodobniej ponownie wykorzystywana do innych celów. Zwrócony wskaźnik będzie wskazywał lokalizację pamięci, w której znajdowała się lokalna zmienna, ale ponieważ ta zmienna już nie istnieje, ten wskaźnik nie byłby zbyt użyteczny.

2

Należy pamiętać, że wskaźnik nie jest czymś, co można przypisać do struktury, ale wskaźnik wskazuje położenie w pamięci, które ma być traktowane jako struktura. Na podstawie twojego pytania, naprawdę chcesz przydzielić pamięć do przechowywania struktury danych. Daje to wskaźnik do przydzielonej lokalizacji pamięci. Kiedy już to zrobisz, możesz go zwrócić.


EDIT (po edycji na oryginalne pytanie) Patrząc na edycję w pytaniu, na pewno będzie miał problemy z „moim” wskaźnika. To jest niezainicjowane i może wskazywać na dowolne miejsce w pamięci. Kiedy spróbujesz skopiować do niego strukturę, prawdopodobnie uzyskasz błąd seg.

6

Nie można użyć zmiennej, ponieważ zostanie zwolniona, gdy funkcja zostanie zakończona.Na przykład:

Mystruct *mystruct(int num, int size) 
{ 
    MyStruct x; 
    x.num = 1; 
    ... 
    return &x; 
} 

Daje błąd segmentacji lub naruszenie dostępu, ponieważ pamięć dla x jest zwalniana zaraz po wyjściu. Musisz więc przydzielić pamięć dla struktury (i pamiętaj, aby zwolnić ją później) lub zadeklarować globalną, która pozostanie na zawsze. Przykładem tego ostatniego ...

Mystruct *mystruct(int num, int size) 
{ 
    MyStruct *x; 
    x = (MyStruct*)malloc(sizeof(MyStruct)); 
    x->num = 1; 
    ... 
    return x; 
} 
+2

właściwie 'return * x' nie będzie kompilował się w pierwszej funkcji (domyślam się, że masz na myśli' return & x' ** ** ** da ci subtelniejszy błąd, który myślisz, lub bardziej prawdopodobne błędy jeszcze bardziej subtelne). –

+0

Ups, masz rację - return & x. – staticman

2

Jeszcze jeden sposób to zrobić ..

int mystruct(Mystruct *mystruct, int num, int size){ 
    if(mystruct == NULL) 
     return -1; 

    mystruct->num = num; 
    mystruct->size = size; 
    :: 
    return 0; 
} 

int main(){ 
    Mystruct my; 

    if(mystruct(&my, 3, 4) != 0){ 
     fprintf(stderr, "Cannot init!\n"); 
     exit(0); 
    } 
    :: 
} 
3

Jeśli piszesz kod ogólny i nie wiesz, w jaki sposób może on być używany, dobrze jest zapewniają obie opcje:

int mystructm(Mystruct *storage, int num, int size) 
{ 
    int rv = 0; 

    storage->num = num; 
    storage->size = size; 
    storage->nums = malloc(num*sizeof(size)); 
    if (!storage->nums) 
     return -1; 

    return 0; 
} 

Mystruct *mystruct(int num, int size) 
{ 
    Mystruct *mp = (Mystruct *)malloc(sizeof(Mystruct)); 
    if (mp) 
    { 
     if (mystructm(mp, num, size) == -1) 
     { 
      free(mp); 
      mp = NULL; 
     } 
    } 

    return mp; 
} 

Chodzi o to, że jako pisarz biblioteki, nie powinien dyktować politykę (jak każdy Mystruct musi być dynamicznie przydzielana), ale powinno pozwolić pisarz zdecydować, że aplikacja.