Also gut, ich verwende eigentlich automatisch schon snprintf() (auch wenn ich praktisch nie C programmiere), aber teilweise würde ich sehr wohl auch sprintf() für gerechtfertigt halten. Z.B. wenn man nur Integers in einen String schreibt – länger als 11/20 Stellen (je nach Größe) können die ja nicht werden. Genauso wenn man bei allen Wildcards auch die Maximallänge angibt (bei Strings halt – floats sind wohl echt problematisch).
"Verdammen" wäre da vielleicht auch falsch, selbst wenn es allgemein auch kaum schaden wird, einfach immer die sichereren Varianten zu benutzen.
--
A human is a system for converting dust billions of years ago into dust billions of years from now via a roundabout process which involves checking email a lot.
Interessant wird's dann aber, wenn man in weiser Voraussicht den String auf 11 Stellen dimensioniert hat, weil der Code ja eh nur auf 32bittigen Plattformen zum Einsatz kommt. Und dann kommt plötzlich eine source-kompatible 64bittige Version der Plattform raus oder jemand portiert den Code.
Das ist (sicherheitstechnisch) das Blöde bei C: Die einzige Sicherheit, die du hast, ist die, die du einbaust. Das ist ein Grund, lieber defensiv zu programmieren als die letzten 0,1% Leistung rauszukitzeln.
Wie gesagt, am Ende kommt es immer auf die Anforderungen der Software an, die man programmiert. Es ist genauso falsch an den falschen Stellen zu optimieren, wie auch zu optimistisch, oder zu pessimistisch zu programmieren. Hundertprozentige Sicherheit gibt es sowieso nie.
Doch, gibt es. Sie nennt sich "EAL7-Zertifizierung". Gut, dafür mußt du sämtliche Software und IIRC auch Teile der Hardware formell verifizieren und testen. Aber dann kannst du dir absolut sicher sein, daß jegliche Fehler Hardwaredefekte und nicht Implementierungsfehler sind.
Okay, als Normalsterblicher wirst du nicht mit EAL7-Kram arbeiten. Aber du solltest schon wissen, wann du wo welche Risiken eingehen kannst. Ich würde mir wohl nicht die Mühe machen, bei jedem Befüllen eines Strings abzuwägen, ob es theoretisch möglich ist, den String mit zu vielen Daten zu befüllen – nicht, wenn es Funktionen gibt, die diese Mühe überflüssig machen.
Man kann's natürlich übertreiben, aber solange der Performanceverlust durch Verwendung von sprintf()-Alternativen nicht zu groß ist, würde ich sie grundsätzlich verwenden. Und das dürfte außerhalb von synthetischen Benchmarks immer der Fall sein.
Die CC ist auch keine Silberkugel und unangreifbar. Du kannst dich an absolute Sicherheit annähern, sie aber nie erreichen. Mit jedem erreichten EAL kannst du deine Software als "sicherer", oder als "zur Zeit sicher gegen dies und das" bezeichnen, aber nie als "sicher". Die Hardware spielt IMO auch eine entscheidende Rolle bei der Frage nach der Sicherheit eines Systems. Übrigens habe ich mal irgendwo gelesen, dass EAL7 für Low Level-Implementation nur semi-formelle Spezifikation fordert, so dass es keinen mathematischen Beweis gibt, dass die Low Level-Spezifikation der Implementation entspricht. Dass EAL7 nur für sehr spezielle Systeme mit sehr speziellen Sicherheitsfunktionen überhaupt praktikabel ist, da der Aufwand und die daraus resultierenden Kosten enorm sind, ist bekannt. Naja, ich bleibe dabei, dass absolute Sicherheit - und ich meine absolut - nicht möglich ist. Ich meine mich zudem zu erinnern gelesen zu haben, dass selbst bei der medizinischen Ausrüstung (und anderer Technik mit ähnlichen Sicherheitsstandards) nicht mit absoluter Fehlerfreiheit gerechnet wird und Todesfälle verursacht durch Fehler in diesen Geräten sowohl in Kauf genommen werden (wenn auch minimal), als auch schon vorgekommen sind.
Was sprintf angeht, überrascht es mich ein wenig, dass du nur die Performance, oder gerade die Performance in Betracht ziehst, da diese kaum bei der sprintf-Familie relevant sein kann. Ich kann mir jedenfalls nicht vorstellen aus welchem sinnvollen Grund sprintf und co. etwas in performancekritischen Bereichen zu suchen hätten. Vielmehr würde ich speziell bei snprintf/asprintf argumentieren, dass ihre Verwendung zusätzlichen Aufwand und damit Komplexität erzeugt in dem Fall, wenn man sich nicht auf einen Compiler beschränken möchte. MSVC ist zum Beispiel nicht C99-konform und hat keine snprintf-Implementation. Nun bietet MSVC zwar sein eigenes snprintf, nämlich _snprintf (was hätte man anderes erwartet), dieses ist aber semantisch von C99-snprintf verschieden -> Aufwand ist notwendig um hier Plattformunabhängigkeit zu erreichen. Man könnte argumentieren, dass dieser Aufwand relativ gering ist, es bleibt trotzdem Aufwand, der betrieben werden muss. IIRC ist das Trimmen von _snprintf auf C99-snprintf aus dem Grund der semantischen Unterschiede sogar nicht so effizient wie man es erwarten würde und würde im Worst Case viele Aufrufe erfordern, was vielleicht nicht wünschenswert ist. Wenn die Software klein genug ist und das Risiko, dass sprintf einen Überlauf verursacht gering ist und nur in Grenzfällen eintreten kann, wie etwa wie in deinem Beispiel mit der Portierung auf 64 Bit, kann man dieses durchaus in Kauf nehmen. Man kann auch mit größerem Buffer arbeiten, was absolut nicht unüblich ist. Ein Wechsel von sprintf zu snprintf/asprintf/_snprintf/etc. ist zu jedem Zeitpunkt immernoch möglich. Ich habe gelernt, dass vorzeitige Paranoia genauso schädlich ist, wie vorzeitige Optimierung und ähnliches Übel und Schritte in diese Richtungen erst gemacht werden sollten, wenn ein Anlass besteht. Es gibt natürlich Ausnahmen.
Im Übrigen bin ich alles andere als der Meinung, Performance geht über Stabilität und Sicherheit, falls (aus welchem Grund auch immer) bei irgendwem dieser Eindruck entstanden sein sollte. Um ehrlich zu sein, habe ich doch bis jetzt immer wieder auf eine oder andere Art betont, dass immer abgewogen werden sollte, je nach Situation und Anforderungen. Ich bin aber definitiv der Meinung, dass nicht vorschnell Dinge als impraktikabel erklärt werden sollten, allein aufgrund dessen, dass sie unter bestimmten Voraussetzungen unsicher sind. "sicher" erweckt meiner Meinung nach sowieso ein falsches Gefühl der Sicherheit, denn um überhaupt nur entfernt von Sicherheit zu reden ist weit mehr nötig, als nur calloc oder snprintf und co. zu verwenden. Davon abgesehen, dass calloc kaum als "sichere Variante von malloc" gedacht war, sondern vermutlich nur als "die bequeme Variante", im Fall, dass der erhaltene Speicher genullt sein muss. Im Fall von Gleitpunktzahlen und Zeigern ist calloc schonmal keine Silberkugel, denn alle Bits auf Null gesetzt, bedeutet nicht eine Gleitpunktzahl gleich Null, oder einen Nullzeiger, man hat also das gleiche Problem wie in deinem Beispiel mit sprintf und der Portierung auf 64 Bit.
Ein kurzer Codeschnipsel, den ich mit euch teilen möchte:
Was macht das Ganze? Es ist sowas wie ein möglichst simpler Ersatz für eine Mailing List. Mit folgender Procmail Regel werden eingehende Mails an list@example.com an alle Adressen, die unter TO eingetragen sind, weitergeleitet und das Reply-To Feld so gesetzt, dass man bequem auf Antworten klicken kann.
Die Zeile mit dem Ausrufezeichen ist da, damit das Ganze keine Endlosschleife gibt. Wenn ihr nicht wollt, dass die Mails archiviert werden, fügt einfach noch eine weitere Regel hinzu, die alles, was Reply-to: list@example.com enthält, nach /dev/null schiebt.
Noch einfacher wäre das Ganze, wenn man Sendmail beibringen könnte, das Bcc: Feld zu beachten, das To: Feld aber nicht. Leider gehen nur beide oder keins und für BCC scheint es keinen Kommandozeilenparameter zu geben.
Du kannst das ganze doch auch einfacher machen, und die Empfänger einfach als Argument zu sendmail übergeben, oder? o_O
Dann rewritest du vielleicht noch den Reply-To Header, damit die Menschen, die zu blöd sind, auf "Reply to all" zu klicken, trotzdem zu ihrem Glück kommen. Beim sendmail-Aufruf entfernst du das -t und machst hinten aus $SENDMAIL ein $SENDMAIL $TO (ich glaube, dann musst du die Kommata in $TO aber weglassen)
Der Punkt ist, die Leute sollen ja nicht allen einzeln antworten, sondern der Verteileradresse. Deshalb steht die im Reply-to Feld. Die einzelnen Empfänger stehen schon absichtlich in Bcc, nicht in To.
Der Punkt ist, die Leute sollen ja nicht allen einzeln antworten, sondern der Verteileradresse. Deshalb steht die im Reply-to Feld. Die einzelnen Empfänger stehen schon absichtlich in Bcc, nicht in To.
...
Mir ist die Funktionsweise einer Mailadresse durchaus klar. Wenn du sendmail die Empfänger auf der cmdline übergibst, schreibt dieses sie aber auch nicht ins To. Wenn aber die ursprüngliche Mail an list@domain.tld und außerdem noch person@example.com ging, obwohl person@example.com nicht in der Liste ist, sollte eine Antwort auf die Mail auch an person@example.com (und natürlich die Liste) gehen, oder? Daher sollte das ursprüngliche To nicht modifiziert werden.
Mich würde mal interessieren, ob jemand außer mir auch mit dem Problem zu kämpfen hat, gezwungen zu sein immer wieder den Code umzuschreiben, wenn einem ein treffenderer Name für eine Routine, eine bessere Namenskonvention, oder eine bessere Formulierung für einen Kommentar einfällt (von besseren Problemlösungsansätzen ganz zu schweigen...)? Oft ist es sogar so, dass ich immer wieder von einem Ansatz zum anderen wechsle, da zwar beide ausgewogene Vor- und Nachteile haben, die Vorteile des aktuell anderen Ansatzes aber immer zu überwiegen scheinen. Irgendwann schaffe ich es zwar, mich zusammenzureißen und einen Weg zu gehen, aber der Weg dahin ist mühsam. Das ist zum Kotzen, denn damit verbringe ich etwa 50% (wenn nicht mehr) der gesamten Zeit beim Programmieren. Das hat natürlich auch damit etwas zu tun, dass ich mir mein eigener Chef bin und aus dem Grund keine klaren Anweisungen habe, mich an etwas zu halten, aber ich denke, es hat auch einen nicht unwesentlichen Teil mit fehlender Erfahrung und Routine zu tun.
Oh Mann... ich bin so froh, wenn die Klausurenphase rum ist. Ich muss für morgen die Abschlusspräsentation von unserem Routenplanerprojekt fertig machen und bis Montag für die Betriebssysteme Klausur lernen. Naja, immerhin hat der Prof bei letzterem Humor:
Zitat
Operating System Definition
OS is a resource allocator
Manages all resources
Decides between conflicting requests for efficient and fair resource use
OS is a control program
Controls execution of programs to prevent errors and improper use of the computer
Everything a vendor ships when you order an operating system
=> No universally accepted definition
...
Ich hab das Gefühl, dass der von mir hervorgehobene Punkt ein kleiner Seitenhieb auf Microsoft sein soll.
Mich würde mal interessieren, ob jemand außer mir auch mit dem Problem zu kämpfen hat, gezwungen zu sein immer wieder den Code umzuschreiben, wenn einem ein treffenderer Name für eine Routine, eine bessere Namenskonvention, oder eine bessere Formulierung für einen Kommentar einfällt (von besseren Problemlösungsansätzen ganz zu schweigen...)? Oft ist es sogar so, dass ich immer wieder von einem Ansatz zum anderen wechsle, da zwar beide ausgewogene Vor- und Nachteile haben, die Vorteile des aktuell anderen Ansatzes aber immer zu überwiegen scheinen. Irgendwann schaffe ich es zwar, mich zusammenzureißen und einen Weg zu gehen, aber der Weg dahin ist mühsam. Das ist zum Kotzen, denn damit verbringe ich etwa 50% (wenn nicht mehr) der gesamten Zeit beim Programmieren. Das hat natürlich auch damit etwas zu tun, dass ich mir mein eigener Chef bin und aus dem Grund keine klaren Anweisungen habe, mich an etwas zu halten, aber ich denke, es hat auch einen nicht unwesentlichen Teil mit fehlender Erfahrung und Routine zu tun.
...
Das ist bei mir normal Zustand.
Ich habe mein CMS schon tausend mal umgeschrieben ... vor kurzem habe ich mich z.B. mal einfach so dazu entschlossen es auf PHP-5.3 inkl. Namespaces umzustellen ... .
Und sogar auf der Arbeit ist es häufig so. "Das Modul ist scheiße das müssen wa mal neu machen" .
Ich weiß nicht ob das wirklich so toll ist wie ich das denke, aber:
Ist das ein toller langer Befehl?
Was hier passiert ist dabei ziemlich einfach, für unseren vertikal Shooter haben wir die Gegner die in den einzelnen Level auftauchen in einen Array eines Arrays gepackt, der "obere" Array steht für das Level der "untere" für die Waves in denen die Gegner auftauchen.
Sprich ,die äußere Schleife kontrolliert die Nummer der Wave und die innere die Anzahl der Gegner der Wave.
Innen wird also nur der Gegner gezeichnet und für mich ist das schon ein ziemlich großer Schritt x_X
Ich weiß nicht ob das wirklich so toll ist wie ich das denke, aber:
Ist das ein toller langer Befehl?
Was hier passiert ist dabei ziemlich einfach, für unseren vertikal Shooter haben wir die Gegner die in den einzelnen Level auftauchen in einen Array eines Arrays gepackt, der "obere" Array steht für das Level der "untere" für die Waves in denen die Gegner auftauchen.
Sprich ,die äußere Schleife kontrolliert die Nummer der Wave und die innere die Anzahl der Gegner der Wave.
Innen wird also nur der Gegner gezeichnet und für mich ist das schon ein ziemlich großer Schritt x_X
...
Wofür ist die Variable "distance" da? Wie weit die Gegner"welle" von uns, also dem Shooter, entfernt ist?
Ich weiß nicht ob das wirklich so toll ist wie ich das denke, aber:
Ist das ein toller langer Befehl?
Was hier passiert ist dabei ziemlich einfach, für unseren vertikal Shooter haben wir die Gegner die in den einzelnen Level auftauchen in einen Array eines Arrays gepackt, der "obere" Array steht für das Level der "untere" für die Waves in denen die Gegner auftauchen.
Sprich ,die äußere Schleife kontrolliert die Nummer der Wave und die innere die Anzahl der Gegner der Wave.
Innen wird also nur der Gegner gezeichnet und für mich ist das schon ein ziemlich großer Schritt x_X
...
Du könntest den Code grandios vereinfachen, wenn du enemy.get(i).get(j) in einer neuen Variable speicherst. Hat nebenbei noch den Vorteil, dass die Methoden nur einmal ausgeführt werden und das Programm dadurch wahrscheinlich schneller wird. Und lesbarer...
Muss er unbedingt die kompletten For-Schleifen durchlaufen?
Die Innere Schleife wird ja nur dann ausgeführt, wenn die Bedingung waveStart.get(i) < distance && waveStop.get(i) > distance erfüllt ist.
Wenn die Bedingung einmal nicht erfüllt wurde, kann sie dann nochmal erfüllt werden?
Wenn nein, würde ich eine While-Schleife nehmen oder sogar den Teil zu einer Rekursion umbauen.