2014-11-15 18 views
8

Czy istnieje sposób (w czystej C), aby odróżnić ciąg ed z malloc ed z literału ciągu, nie wiedząc, który jest który? Ściśle mówiąc, próbuję znaleźć sposób na sprawdzenie zmiennej, czy jest to ciąg przerobiony, czy nie, a jeśli tak, to go uwolnię; jeśli nie, odpuszczę.Jak odróżnić ciąg przeróbek od literału literowego?

Oczywiście, mogę kopać kod do tyłu i upewnić się, czy zmienna jest malloc ed czy nie, ale tylko w przypadku, jeśli istnieje prosty sposób ...

EDIT: linie dodany do kwestii bardziej konkretny.

char *s1 = "1234567890"; // string literal 
char *s2 = strdup("1234567890"); // malloced string 
char *s3; 
... 
if (someVar > someVal) { 
    s3 = s1; 
} else { 
    s3 = s2; 
} 
// ... 
// after many, many lines of code an algorithmic branches... 
// now I lost track of s3: is it assigned to s1 or s2? 
// if it was assigned to s2, it needs to be freed; 
// if not, freeing a string literal will pop an error 
+9

Nie w żaden przenośny sposób, nie. Dlaczego tak trzeba? Wygląda na to, że próbujesz zarządzać pamięcią w niewłaściwy sposób. Istnieją lepsze sposoby na zagwarantowanie, że pamięć jest przydzielana i prawidłowo czyszczona. Pierwszym krokiem jest nie "zapomnieć", jak coś zostało przydzielone. –

+0

Nie jestem programistą C/C++, ale wydaje mi się to oczywiste, więc prawdopodobnie czegoś mi brakuje. W każdym razie, jeśli potrzebujesz zwolnić 's2', czy w ogóle troszczycie się o' s3', jeśli ma zostać przypisana wartość 's1' lub' s2', i nic więcej? Nie uwolnisz dwa razy ciągu zwróconego przez 'strdup', więc jeśli masz zamiar zwolnić' s2', dlaczego interesuje Cię 's3'? –

+2

Kiedy piszesz kod w C, robisz to podczas robienia silnych gwarancji "kto" jest właścicielem pamięci. Czy dzwoniący jest właścicielem? Wtedy ich obowiązkiem jest zwolnić go. Czy właściciel jest właścicielem? Podobna rzecz. Piszesz kod, który jasno mówi o łańcuchu nadzoru i własności, i nie napotykasz problemów takich jak "kto zwalnia to?" I, jak powiedział Lasse, jeśli nazwiesz 'free' na' s2' * i * 's3', skończysz z niezdefiniowanym zachowaniem. –

Odpowiedz

8

Czy istnieje sposób (w czystym C), aby odróżnić malloced ciąg z ciągiem dosłownym,

Nie w każdym przenośnym sposób, no. Nie musisz się jednak martwić; istnieją lepsze alternatywy.

Kiedy piszesz kod w C, robisz to, jednocześnie robiąc silne gwarancje "kto" jest właścicielem pamięci. Czy dzwoniący jest właścicielem? Wtedy ich obowiązkiem jest zwolnić go. Czy właściciel jest właścicielem? Podobna rzecz.

Piszesz kod, który jasno określa łańcuch kontroli i prawo własności i nie napotykasz problemów takich jak "kto zwalnia?" Nie powinieneś czuć potrzeby, aby powiedzieć:

// after many, many lines of code an algorithmic branches... 
// now I forgot about s3: was it assigned to s1 or s2? 

Rozwiązaniem jest; nie zapomnij! Masz kontrolę nad swoim kodem, po prostu spójrz trochę na stronę. Zaprojektuj go tak, aby był odporny na kulki, przed wypuszczeniem pamięci do innych funkcji bez wyraźnego zrozumienia, że ​​"hej, możesz to przeczytać, ale nie ma gwarancji, że będzie on ważny po X lub Y. To nie jest twoja pamięć, traktuj to jako takie . "

A może to twoja pamięć. Przykładem; twój telefon do strdup. strdup załóżmy, że (poprzez dokumentację) Twoim obowiązkiem jest zwolnić ciąg, który ci zwraca. Sposób, w jaki to robisz, zależy od ciebie, ale najlepiej jest ograniczyć jego zakres tak, aby był tak wąski, jak to możliwe, i utrzymywać go tylko przez tak krótki czas, jak to konieczne.

Potrzeba czasu i praktyki, aby stać się drugą naturą. Stworzysz kilka projektów, które poradzą sobie z pamięcią słabo, zanim się poprawisz. W porządku; twoje błędy na początku nauczą cię, co dokładnie zrobić, a unikniesz powtórzenia ich w przyszłości (miejmy nadzieję!).

Również, jak @Lasse nawiązywał do komentarzy, nie masz martwić się o s3, który jest kopią wskaźnika, a nie całą porcją pamięci. Jeśli zadzwonisz za darmo na s2 i s3, otrzymasz nieokreślone zachowanie.

+0

Dodatek do przypadku: Powiedzmy, że 's1' jest literałem, a' s2' jest zmiennikiem (jako kod mówi). Kod rozgałęzia się na 'if' i przypisuje wartość' s1' lub 's2' do' s3', w zależności od pewnych warunków. Następnie wywoływana jest funkcja foo (s3). Teraz, jeśli muszę zwolnić 's3' w func. 'foo', ja - dłużej nie mam pojęcia o' s3'. Aby się upewnić, przekazałem pozostałe dwa znaki ('someVar' &' someVal') do func. 'foo' i ** ponownie ocenia ** warunek. Jedynie przekazując pozostałe zmienne, mogę być pewien wartości wskaźnika "s3", niezależnie od tego, czy został przypisany do 's1' czy' s2'. – ssd

+3

@ merkez3110: Twój problem nie jest tym, który opisujesz. Prawdziwym problemem jest to, że już naruszyłeś zasady, o których mówię. Kiedy zaprojektowałeś swój kod w taki sposób, aby to się zdarzyło, że już przegrałeś. Tworzysz system, który trudno jest utrzymać i uzasadnić. Rozwiązanie; nie rób tego. * "Teraz, jeśli chcę uwolnić s3 w func." Foo' ... "* - Nie, nie masz! Osłabliście, kto jest odpowiedzialny za tę pamięć. jeśli 'foo' może zaakceptować dowolny ciąg *, to nie powinno być odpowiedzialne za dealokację *. –

+2

... Dzwoniący 'foo' jest odpowiedzialny, ponieważ tylko on wie, co należy zrobić. Niepoprawnie przekazałeś odpowiedzialność/własność funkcji, która nie może wnioskować o jej wprowadzeniu. *To jest problem. Jest to kwestia projektowa, a nie techniczna. Spójrz na standardowe funkcje biblioteki; Ile widzisz funkcji, która bierze "char *" i * także * zwalnia ją? –

0

Oto praktyczny sposób:

Chociaż C-język standardowy nie dyktować to, identycznych dla wszystkich wystąpień danego ciągu w dosłownym kodu, kompilator generuje jedną kopię wewnątrz RO- sekcja danych obrazu wykonywalnego.

Innymi słowy, każde wystąpienie ciągu "1234567890" w kodzie jest tłumaczone na ten sam adres pamięci.

więc w miejscu, gdzie chcesz cofnąć przydział ten ciąg, można po prostu porównać swój adres z adresem dosłownym ciąg "1234567890":

if (s3 != "1234567890") // address comparison 
    free(s3); 

Aby to podkreślić jeszcze raz, że nie narzuca standard języka C, ale jest praktycznie zaimplementowany przez każdy przyzwoity kompilator języka.


UPDATE:

Wyżej jest tylko praktyczny trik odnosząc się bezpośrednio do kwestii pod ręką (zamiast motywacji za to pytanie).

Ściśle mówiąc, jeśli osiągnąłeś punkt w swojej implementacji, w którym musisz rozróżnić między statycznie przydzielonym ciągiem a dynamicznie przydzielonym łańcuchem, to zgaduję, że twój początkowy projekt jest wadliwy gdzieś wzdłuż linii.

+0

Inną możliwą "praktyczną techniką" jest wykrycie części przestrzeni adresowej, którą twój system wykorzystuje do sterty i zobaczenie, czy łańcuch jest w tym. –

+0

@MattMcNabb: Myślałem o tym również, ale nie mogłem wymyślić, jak wyrazić to w kodzie. Jedyną dostępną opcją jest sprawdzanie ustawień linkera (lub pliku konfiguracyjnego). –

Powiązane problemy