static Variable, Good Practice oder NoGo?
-
Das waren zwei eindeutige Aussagen. An die Nutzung über mehrere Threads hatte ich noch nicht gedacht, aber die Verwendung für das durchlaufen eines Verzeichnisbaums hatte mir mit diesem Setup Kopfzerbrechen bereitet.
Euren Antworten nach zu Urteilen kann man also sagen, dass nahezu keine brauchbaren Anwengungsfälle gibt. Ich wüsste beispielsweise auch nicht, wozu ich beim genannten Fall eines Funktionscounters diesen wirklich benötige.
Ich finde es nur ein wenig erschreckend, dass über diese Zustände in viel Lehrmaterial gar nicht eingegangen wird. Klar, spätestens wenn man mit dem obigen Code in mehreren Threads hantiert und er mir nur Mist auswirft, leuchtet einem das ein.
SeppJ: Du hast Teile der Standardbibliothek erwähnt, die in diesem Fall auch nicht unbedingt als sicher betrachtet werden können. Wie soll ein Neuling denn so etwas erfahren? Ich hab jetzt mal ein wenig die Suchmaschine bemüht, aber eine Auflistung von unsicheren Standard Funktionen scheint es auf den ersten Blick nicht zu geben.
Würde ich hier nicht gelegentlich ins Board schauen oder wie jetzt eine Frage stellen, dann käme ich ja gar nicht auf den Trichter. Ich muss mich dann ja auch das Lehrmaterial oder den Lehrer verlassen, was aber den Erfahrungen in diesem Forum nach auch ein handwerklicher Selbstmord sein kann.
Wie habt Ihr das gemacht? Notwendige Grundtechniken erlernt und irgendwann beim testen/lesen diese Mangel festgestellt (also durch Erfahrung) oder gibt es dort eine andere Möglichkeit?
Ich bedanke mich auf jeden Fall schonmal für die bisherigen Antworten.
-
fairiestoy schrieb:
SeppJ: Du hast Teile der Standardbibliothek erwähnt, die in diesem Fall auch nicht unbedingt als sicher betrachtet werden können. Wie soll ein Neuling denn so etwas erfahren? Ich hab jetzt mal ein wenig die Suchmaschine bemüht, aber eine Auflistung von unsicheren Standard Funktionen scheint es auf den ersten Blick nicht zu geben.
Das ist in Standards festgelegt. Beispielsweise gibt es eine Übersicht zum POSIX-Standard, welche Funktionen nicht threadsicher zu sein brauchen:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_01
Da POSIX auch die Definitionen des C-Standards umfasst, gilt die Liste auch für C, sie enthält bloß noch wesentlich mehr Funktionen als die C-Standardbibliothek. Alles was von POSIX (oder C) nicht auf der Liste steht, darfst du als threadsicher annehmen.Die Betonung liegt hier auf "brauchen": Diese Definitionen sind in vielen Fällen einfach der Stand der Technik zu der Zeit als der Standard entwickelt wurde. Die Programmierer der Bibliotheken sind aber durchaus bestrebt, möglichst viel threadsicher zu machen, es kann daher gut sein, dass sie in der Praxis threadsicher sind. Ob das so ist, sofern man sich denn wirklich darauf verlassen will, muss man dann in der Dokumentation der jeweiligen Umsetzung gucken.
Manche der Funktionen sind aufgrund der vom Standard definierten Schnittstelle unmöglich threadsicher zu machen. In vielen Fällen gibt es dann threadsichere Varianten mit leicht anderem Namen (zum Beispiel mit "_r" hinten dran) und Schnittstelle, so dass die Funktionalität auch threadsicher zur Verfügung gestellt werden kann. Solche Details muss man dann wieder in den Dokumentationen der konkreten Implementierungen nachgucken.
PS: Aber ja, dass man static-Variablen in Funktionen selten braucht, ist richtig. Ein Lehrbuch mag dennoch auf static rumreiten, weil
a) sie alles zeigen wollen, was es in C gibt
und/oder
b) die Alternativmethoden ein bisschen mehr Erklärung bedürfen und sie diese Erklärung aus Platzgründen oder mangelnder Kompetenz nicht aufzeigen können.
-
Vll. zur Ergänzung zu C++ und weshalb man C++ gegenüber C vorziehen sollte.
Ich kenne mind. einen gerechtfertigten Anwendungsfall, der in C++ 11 sogar thread-safe ist:static Singleton& get(){ static Singleton instance; return instance; }
-
Das wirft wieder die Frage nach der Berechtigung von Singletons auf und wie oft dieses Pattern falsch eingesetzt wird, weshalb ich es absichtlich nicht erwähnt habe
-
Bei MVC ist es sinnvoll, wenn der Controller ein Singleton ist.
-
fairiestoy schrieb:
Ich finde es nur ein wenig erschreckend, dass über diese Zustände in viel Lehrmaterial gar nicht eingegangen wird.
Da liegt der Hund begraben.
C ist erschaffen worden von einem Praktiker-Profi. Der wusste, was er wollte und brauchte. Und konnte mit den "Schwachpunkten" umgehen, weil er sie kannte.
Ritchie hat C für sich und seine Aufgaben erschaffen. Er hat dabei niemals auf mögliche leichte Erlernbarkeit und Vermeidung von Fallstricken geachtet.
Und später bei der Standardisierung, als viele (ausschließlich aus dem professionellen Umfeld) diese Sprache auch gerne einsetzen wollten, konnte und wollte man nicht mehr am Sprachdesign rumändern.
C ist eine Programmiersprache von Profis für Profis.
C ist keine Lernsprache.
Irgendwann sind dann aber (leider!) auch Lehrer darauf gekommen, C als Einstieg in die Programmierung zu verwenden, weil sie ja so "schön einfach" aussieht.
Und da bist du jetzt der Leidtragende.fairiestoy schrieb:
Wie habt Ihr das gemacht? Notwendige Grundtechniken erlernt und irgendwann beim testen/lesen diese Mangel festgestellt (also durch Erfahrung) oder gibt es dort eine andere Möglichkeit?
Um dir mal die Illusionen zu nehmen:
egal was dein Lehrer dir sagt: du wirst an einer Schule keine C-Programmierung erlernen, die du später professionell anwenden kannst.
Dasselbe gilt für die heute (leider!) übliche und auch dir sicher bekannte Variante: Lernen mit Google.
Gehe nicht mit der Aussage "ich kann C programmieren" in irgendwelche Vorstellungsgespräche. Denke dir dafür ein paar neutrale Aussagen aus.In C ist Erfahrung alles. Damit meine ich jahrelange tägliche Erfahrung.
Tatsächlich habe auch ich ein paar Vorlesungen über C (und anderes) gehört.
Auf mein oben Gesagtes hat mich damals aber niemand hingewiesen, ich war gutgläubig gegenüber den Dozenten (was ganz natürlich ist), wurde dann aber in der Praxis "abgeholt".Hier noch eine professionelle Quelle zum Thema (und mit anderen sinnvollen und professionellen Hinweisen):
https://www.securecoding.cert.org/confluence/display/c/CON33-C.+Avoid+race+conditions+when+using+library+functionsSolche hübsche, komprimierte und professionelle Wissensvermittlung gab es zu meiner Anfangszeit leider noch nicht, ich habe mir alles selbst erarbeiten müssen und vieles davon findet man hier (und an sehr wenigen anderen professionellen Stellen im Internet) wieder.
-
@Wutz: Das liest sich wie Werbung. Was kannst du mit C (besser) machen, was du mit C++ nicht machen kannst?!
-
ShadowClone schrieb:
@Wutz: Das liest sich wie Werbung. Was kannst du mit C (besser) machen, was du mit C++ nicht machen kannst?!
Wo hat er denn was von C++ gesagt? Er hat etwas über die Eigenschaften von C gesagt. Bitte keine Flamewars beginnen.
-
@fairiestory:
Ich glaube dein Programm ist sogar buggy.Nehmen wir mal an, du rufst erfolgreich deine Funktion auf. In diesem Fall gibst du einen Zeiger auf ein dirent Struct (ep Variable) zurück. Ruft man nun die Funktion mit einer anderem Pfad auf, so wird trotzdem im ersten Pfad gesucht. Die dp Variable ist ja static und mit dem Pfad des ersten Funktionsaufrufs initalisiert.
Ich habe auch einen Verdacht wo der Hund begraben ist. Die ep Variable ist ein Zeiger auf ein dirent Struct. Vermutlich zeigt das Struct auf die dp Variable. Deswegen ist die dp Variable static. Das kannst du auch einfach überprüfen in den du ein closedir(dir) vor dem return ep einfügst.
PS:
Variablen der Form int FileType sind für mich ein Graus. Was ist hier erlaubt? Und wo ist definiert?
-
Bitte ein Bit schrieb:
@fairiestory:
Ich glaube dein Programm ist sogar buggy.Nehmen wir mal an, du rufst erfolgreich deine Funktion auf. In diesem Fall gibst du einen Zeiger auf ein dirent S...
Moin Bitte ein Bit,
danke für deine Meldung. Im Bezug auf den Code haben bereits Wutz und SeppJ ausgeleuchtet, dass dies ein Fehlverhalten ist. Die Funktion weiss in diesem Zustand nicht, wann bzw ob ein anderer Pfad angegeben wurde im Laufe beispielsweise eines wiederholten Aufrufs durch eine while-Schleife.
Aus dem Grund wurde ja auch die Nutzung von übergegebenen Objekten für die Speicherung des Programmzustands für einzelne Teile/Module/wie auch immer vorgeschlagen.
Zumindest ist es das, was ich bisher aus diesem Thread verstanden habe.
Was die Angabe des Filetypes angeht muss ich sagen, dass ich hier ein wenig mehr gemurkst habe als eh schon. Das von mir gepostete Beispiel habe ich aus diesem Code-Abschnitt zusammen geschustert:
http://stackoverflow.com/questions/12489/how-do-you-get-a-directory-listing-in-c
Danach habe ich versucht, einige Infos zu <dirent.h> zu finden, allerdings konnte ich im betreffenden Header keine Filetype-Definitionen ausfindig machen (oder ich habe den Wald vor lauter Bäumen nicht gesehen). Ich vermute jedoch, dass dies betriebssystemspezifisch ist, da auch der Eintrag der Opengroup hierzu keine Info bereit hält.
Nach ein bissel printf-Magie mit ep->d_type habe ich bisher nur gesehen, dass es zwar verschiedene positive Zahlen für die jeweiligen Typen gibt, konnte aber keine Begrenzung oder den Typ feststellen. Beispielsweise finde ich in meiner <dirent.h> folgende Definition:
struct dirent { long d_ino; /* Always zero. */ unsigned short d_reclen; /* Always sizeof struct dirent. */ unsigned short d_namlen; /* Length of name in d_name. */ unsigned d_type; /* File attributes */ char d_name[FILENAME_MAX]; /* File name. */ };
Ich kenne so ein Konstrukt gar nicht. Soll d_type jetzt ein unsigned short oder unsigned char sein? Oder sogar ein unsigned der nativen kleinsten Größe für einen Pointer? Da ich hierzu noch ein bisschen nach der Antwort suchen muss, habe ich erstmal für FileType ein int gewählt (gut, es hätte mindestens ein unsigned int sein sollen, dass sehe ich ein).
-
unsigned
bedeutet wenn kein anderer Typ dabei steht immerunsigned int
.
-
Ich hab auch schon static in rekursiven Funktionen benutzt und wurde dafür von Martin Richter geschumpfen - Problem war ein ähnliches wie deines, aber ich hab es nur als Zählvariable benutzt.