2013-08-04 22 views
12

Nie jestem programistą C, więc nie jestem zaznajomiony z ciągiem C, ale nowy muszę korzystać z biblioteki C, więc tutaj jest skrócony wersja mojego kodu do wykazania mój problem:Używanie C-string: "Adres pamięci stosu skojarzonej z zwracaną zmienną lokalną"

char** ReadLineImpl::my_completion() { 

    char* matches[1]; 


    matches[0] = "add"; 

    return matches; 

} 

otrzymuję ostrzeżenie:

Ostrzeżenie - adres pamięci stosu wiąże się ze zmiennej lokalnej „pasuje” zwrócony

Moja aplikacja nie działa poprawnie (może być spowodowana tym ostrzeżeniem).

Co to jest ostrzeżenie i czy spowoduje jakiekolwiek problemy?

+3

Powracasz do pierwszego wskaźnika znaków, który jest przydzielany na stosie i który przestaje istnieć po wyjściu z funkcji. –

Odpowiedz

19

Zmienna char* matches[1]; jest zadeklarowana na stosie i zostanie automatycznie zwolniona, gdy bieżący blok zniknie z zakresu.

Oznacza to, że po powrocie na numer matches pamięć zarezerwowana dla matches zostanie zwolniona, a wskaźnik wskaże coś, czego nie chcesz.

Problem ten można rozwiązać na wiele sposobów, a niektóre z nich to:

  1. Zadeklaruj matches[1] jak static: static char* matches[1]; - to przydzieli przestrzeń dla matches na stercie (może cię ugryźć, jeśli was użytkowania to niepoprawnie, ponieważ wszystkie wystąpienia funkcji my_completion będą miały tę samą zmienną).

  2. Przydzielenie miejsca w funkcji dzwoniącego i przekazać go do my_completion funkcja: my_completion(matches):

    char* matches[1]; 
    matches = my_completion(matches); 
    
    // ... 
    
    char** ReadLineImpl::my_completion (char** matches) { 
        matches[0] = "add"; 
    
        return matches; 
    } 
    
  3. Przydzielanie miejsca w nazwie funkcji na stercie (używając malloc, calloc i przyjaciół) i przechodzą na własność do funkcji wywołującej, która będzie musiała zwolnić tę przestrzeń, gdy nie jest już potrzebna (przy użyciu free).

+0

Dzięki, mam to. Najgorsze jest to, że po raz drugi dostaję tego rodzaju problem :) w każdym razie, dziękuję bardzo – khajvah

+0

Zakładając, że jest to 'GNU readline', spowoduje to awarię, ponieważ' readline' zwalnia pamięć zwróconą przez funkcja zakończenia. –

+0

@MatsPetersson Wydaje się, że to wygląda na readline - w tym przypadku masz rację. Przekażę twoją odpowiedź! –

7

Gdy zwrócisz tablicę matches, zwracasz adres pierwszego elementu. Jest to przechowywane na stosie wewnątrz my_completion. Gdy powrócisz z my_completion, pamięć zostanie odzyskana i (najprawdopodobniej) zostanie ponownie użyta do czegoś innego, zastępując wartości zapisane w matches - i tak, to może być powód, dla którego Twoja aplikacja nie działa - jeśli nie jest odpowiednia teraz prawdopodobnie nastąpi to po rozwiązaniu kilku innych problemów lub zmianie trochę, lub czegoś innego, ponieważ nie jest to jedno z tych małych ostrzeżeń, które można bezpiecznie zignorować.

Możesz to naprawić na kilka różnych sposobów.Najbardziej oczywistym jest po prostu użyć std::vector<char *> [albo jeszcze lepiej std::vector<std::string>] zamiast:

std::vector<std::string> ReadLineImpl::my_completion() 
{ 
    std::vector<std::string> strings; 
    strings.push_back("add"); 
    return strings; 
} 

Edycja: Tak, jeśli biblioteka wymaga char ** jak na interfejsie readline, a następnie użyć tego:

char** ReadLineImpl::my_completion() 
{ 
    char **matches = static_cast<char **>malloc(1 * sizeof(char *)); 
    matches[1] = "add"; 
    return matches; 
} 

Problem rozwiązany!

+0

Użyłbym wektora jako programisty C++, ale biblioteka wymaga char **, więc muszę konwertować na char **. – khajvah

+0

Zakładając, że używasz interfejsu 'readline', MUSISZ przydzielić pamięć za pomocą malloc, ponieważ' readline' zwalnia ją później. Będę edytować. –

+0

Dobrze, tylko jedno, musiałem użyć (char **) malloc (1 * sizeof (char *)), ale dzięki, twoja odpowiedź jest lepsza, chociaż nie zamierzam zmienić – khajvah

0

zmiana

char* matches[1]; 

do

char *matches = new matches[1]; 
+0

Dlaczego? Być może, powinieneś opisać powód, by to zrobić. –

0

Korzystanie sterty zamiast stosu

Lepiej przydzielić pamięci na stercie dla tym przypadku za pomocą:

int* someDataForParams(void *_params) { 

    ... 
    int* charCounts = calloc(96, sizeof(char*)); 
    ... 

    return charCounts; 
} 

96 to tylko długość łańcucha znaków (tylko liczba magiczna)