W moich programach C często potrzebuję sposobu, aby utworzyć ciąg reprezentujący moje ADT. Nawet jeśli nie muszę w żaden sposób drukować łańcucha na ekranie, fajnie jest mieć taką metodę debugowania. Tak więc często pojawia się tego rodzaju funkcja.
char * mytype_to_string( const mytype_t *t );
W rzeczywistości zdaję sobie sprawę, że mam (przynajmniej) trzy opcje obsługi pamięci dla ciągu do zwrócenia.
Alternatywa 1: Przechowywanie łańcucha zwrotnego w statycznej tablicy znaków w funkcji. Nie potrzebuję dużo myślenia, poza tym, że ciąg jest nadpisywany przy każdym wywołaniu. Co może być problemem w niektórych przypadkach.
Alternatywa 2: Przydziel ciąg na stercie z malloc wewnątrz funkcji. Naprawdę fajnie, bo wtedy nie będę musiał myśleć o wielkości bufora ani o nadpisywaniu. Jednak muszę pamiętać, aby po zakończeniu wykonać polecenie free (), a następnie muszę także przypisać zmienną tymczasową, którą mogę zwolnić. a następnie przydzielanie sterty jest naprawdę znacznie wolniejsze niż przydzielanie stosu, dlatego bądź wąskim gardłem, jeśli powtarza się to w pętli.
Alternatywa 3: Przekaż wskaźnik do bufora i pozwól programowi wywołującemu przydzielić ten bufor. Lubić:
char * mytype_to_string( const mytype_t *mt, char *buf, size_t buflen );
Daje to więcej wysiłku dzwoniącemu. Zauważam również, że ta alternatywa daje mi inną opcję w kolejności argumentów. Który argument powinienem mieć pierwszy i ostatni? (właściwie sześć możliwości)
Które więc powinienem preferować? Dlaczego? Czy istnieje jakiś niepisany standard wśród programistów C?
źródło
sysctlbyname
w OS X i iOSOdpowiedzi:
Metody, które widziałem najczęściej to 2 i 3.
Bufor dostarczony przez użytkownika jest w rzeczywistości dość prosty w użyciu:
Chociaż większość implementacji zwróci ilość użytego bufora.
Opcja 2 będzie wolniejsza i jest niebezpieczna przy korzystaniu z bibliotek połączonych dynamicznie, w których mogą używać różnych środowisk wykonawczych (i różnych stosów). Nie możesz więc uwolnić tego, co zostało mallocedowane w innej bibliotece. To wymaga
free_string(char*)
funkcji, która sobie z tym poradzi.źródło
printf("MyType: %s\n", mytype_to_string( mt, buf, sizeof(buf));
i dlatego nie chcę zwracać użytej długości, ale wskaźnik do łańcucha. Komentarz biblioteki dynamicznej jest naprawdę ważny.sizeof(buffer) - 1
zadowolić\0
terminatora?snprintf
. Use.Dodatkowy pomysł na projekt # 3
Jeśli to możliwe, podaj również maksymalny wymagany rozmiar
mytype
w tym samym pliku .h comytype_to_string()
.Teraz użytkownik może odpowiednio kodować.
Zamówienie
Rozmiar tablic, gdy jest pierwszy, pozwala na typy VLA.
Nie tak ważne z jednym wymiarem, ale przydatne z 2 lub więcej.
Pamiętam, że czytanie pierwszego rozmiaru jest preferowanym idiomem w następnym C. Muszę znaleźć to odniesienie ...
źródło
Jako dodatek do doskonałej odpowiedzi @ ratchetfreak wskazałbym, że alternatywa # 3 ma podobny paradygmat / wzorzec jak standardowe funkcje biblioteki C.
Na przykład
strncpy
.Przestrzeganie tego samego paradygmatu pomogłoby zmniejszyć obciążenie poznawcze dla nowych programistów (a nawet przyszłego siebie), gdy będą musieli korzystać z Twojej funkcji.
Jedyną różnicą w stosunku do tego, co masz w poście, jest to, że
destination
argument w bibliotekach C zwykle jest wymieniony jako pierwszy na liście argumentów. Więc:źródło
Poza tym, że to, co proponujesz, to nieprzyjemny zapach kodu, alternatywne 3 brzmią dla mnie najlepiej. Myślę też, jak @ gnasher729, że używasz niewłaściwego języka.
źródło
Szczerze mówiąc, możesz przejść na inny język, w którym zwracanie ciągu nie jest skomplikowaną, pracochłonną i podatną na błędy operacją.
Możesz rozważyć C ++ lub Objective-C, w którym możesz pozostawić 99% kodu bez zmian.
źródło