Hallo, ich programmiere meine AVRs seit der Schule in C. Bisher hat niemand diese Codes gelesen und deshalb habe ich mir mit dem Aufbau keine Mühe gegeben. Teilweise habe ich selber Probleme einen alten Code richtig zu verstehen. Deshalb such ich nach einer Art Sammlung wo drinsteht, wie C-Code formatiert sein sollte damit andere ihn lesen können. Also zum Beipiel Sachen wie das "#define bla 42" nicht in die *.c sondern in *.h reingehört oder wie man switch/case-Blöcke formatiert. Und auch Tricks wie man Namen für Funktionen und Variablen passend wählt. Der Gockel spuckt nur allgemeine Tuts zu C aus, da mir das passende Schlagwort nicht einfallen will.
Mir würde das auch sehr weiter helfen. Tipps von mir. - STRG + SHIFT + F in eclipse formatiert den Code selbstständig. - Hinter die schließenden geschweiften Klammern "}" schreiben, wozu dieser Block gehört. - Möglichst viel in (inline) Funktionen auslagern und möglichst wenig in der while(true) Schleife haben. Sozusagen Makro und Mikroebene trennen.
Der Wikipedia-Link ist wirklich gut. Und bitte auch "ungarische Notation" beachten! Ich selbst war anfangs recht faul beim Programmieren, doch wenn man eigene Programme nach einiger Zeit modifizieren muss, dann freut man sich an üppigen Kommentaren. Will heissen: Mittlerweile schreibe ich fast hinter jeder(!) Zeile einen Kommentar. Klar, das ist lästig, aber es hilft. Eine Variable "z" zu nennen ist ok. Sie "Zaehler" zu nennen ist besser. Sie "uc_zaehler" zu nennen bewirkt, dass man jedesmal erinnert wird, dass sie uc = unsigned char ist. Noch ein Tip: Irgendwann habe ich mal PC-Lint gekauft. Wenn man damit seinen Code checkt, dann merkt man schnell, wie man "richtig" in C programmiert.
Tommy wrote: > Der Wikipedia-Link ist wirklich gut. Und bitte auch > "ungarische Notation" beachten! "beachten" im Sinne von "vermeiden", oder? :-)
Jörg Wunsch wrote: > Tommy wrote: >> Der Wikipedia-Link ist wirklich gut. Und bitte auch >> "ungarische Notation" beachten! > > "beachten" im Sinne von "vermeiden", oder? :-) Hoffentlich. Das kommt aus einer grauen Vorzeit, als es noch keine gescheite IDE gab, die einem den Datentyp per Mouse-Over oder beim Autovervollständigen anzeigt. Ich find´s einfach nur abscheulich solchen "ungarischen" Code lesen zu müssen.
> Tipps von mir. > - STRG + SHIFT + F in eclipse formatiert den Code selbstständig. In dein Einstellungen von Eclipse kann man die Formatierung sehr detailliert festlegen, inklusive einrücken, festgelegtes Zeilenende etc etc. Diese Vorgaben werden mit STRG SHIFT F dann umgesetzt, entweder auf das gesamte Dokument oder auf eine gewünschte Passage (zb nur ein switch case markieren und dann formatieren lassen). Die Vorgaben kann man in eine XML datei exportieren und in anderen Eclipse umgebungen wieder importieren, was im professionellen Umfeld ueberaus praktisch ist, da so dann alle Kollegen mit der selben Formatierung arbeiten koennen (vorrausgesetzt, sie benutzen den FORMAT shortcut)
Code-Schweinchen wrote: > Bisher hat niemand diese Codes gelesen und deshalb habe ich mir mit dem > Aufbau keine Mühe gegeben. > Teilweise habe ich selber Probleme einen alten Code richtig zu > verstehen. Dann hast du ja schon den idealen Ansatz - im Gegensatz zu vielen anderen Entwicklern hast du das Problem erkannt! Also gehst du am besten in dich, oder noch besser in deinen Code und versuchst die Detailprobleme zu identifizieren und abzustellen. Ein paar Hinweise: - Leerzeichen und Leerzeilen kosten kein Geld! - Kommentare schreibt man fuer sich selbst, fuer spaeter. - Um so genialer die Idee, um so noetiger das Kommentar. - Vergiss nicht, Zusammenhaenge zu dokumentieren. Die erschliessen sich nicht aus den paar Zeilen Code, auf die du gerade guckst! - Die ueblichen Regeln der Muttersprache (oder englisch, wenn's wirklich auch fuer andere sein soll) sollten in deinen Stil einfliessen. Du bist darauf trainiert solche Texte zu lesen.
Tommy wrote: > Ich selbst war anfangs recht faul beim Programmieren, doch > wenn man eigene Programme nach einiger Zeit modifizieren > muss, dann freut man sich an üppigen Kommentaren. jep. > Will heissen: Mittlerweile schreibe ich fast hinter jeder(!) > Zeile einen Kommentar. Klar, das ist lästig, aber es hilft. Das ist jetzt nicht unbedingt zielführend. Kommt drauf an, was in deinem Kommentar drinnen steht. Es gibt durchaus Sequenzen, in denen es vernünftig ist, jede Zeile zu kommentieren (wenns besonders trickreich zugeht), aber im Regelfall reicht ein Blockkommentar vor einem Abschnitt völlig aus. Die meisten 'Jede-Zeile-Kommentierer' die ich bis jetzt gesehen habe, schreiben im Kommentar genau das hin, was sowieso schon im Programmtext steht. Solche Kommentare sind sinnlos. Beschreibe nicht wie etwas passiert, sondern warum es passiert. Beschreibe in einem Kommentar die Idee die im Code steckt und nicht die Details der Umsetzung (ausser sie sind trickreich und nicht auf den ersten Blick zu erkennen) In dieselbe Kategorie fallen die Kommentare // *********** globale Variablen ************* sinnlos. Wenn ein Programmierer nicht nach 0.5 Sekunden sieht, dass hier die globalen Variablen zusammengefasst sind, soll er sich sein Lehrgeld zurückgeben lassen. Ditto // // Konstruktor // Object::Object( int Arg1, char Arg2 ) { ... } Der Kommentar ist für die Katz. Jeder C++ Programmierer, der sein Geld wert ist, sieht dass das ein Konstruktor ist. Dafür brauchts keinen Kommentar. Was die Argumente darstellen hätte er kommentieren sollen, hat es aber nicht getan! Noch besser wäre es allerdings gewesen, er hätte den Argumenten vernünftige Namen gegeben. Grundsätzlich gilt: Versuch immer deinen Code so zu gestalten, dass er sein eigener Kommentar ist. Wenn das nicht ausreicht, überlege wie man den Code ändern müsste um in diesen Zustand zu kommen. Und erst dann, wenn danach der Code immer noch nicht aussgaekräftig genug ist, verpass ihm einen Kommentar. Man kann schreiben: cb = 0.2 * x; // Berechne Mehrwertssteuer Man kann aber auch schreiben MwSteuer = MWST_SATZ * NettoPreis; Den Unterschied in der Aussage (was mir der Code selber erzählt) brauch ich wohl nicht aufzeigen. > Sie "uc_zaehler" zu nennen bewirkt, dass man jedesmal > erinnert wird, dass sie uc = unsigned char ist. Es soll schon vorgekommen sein, dass Variablen ihren Datentyp gewechselt haben. Nennst du sie dann auch wirklich um. Und was machst du mit Strukturen, Unions, Arrays, Arrays von Strukturem, etc? In all diesen Dingen bricht die ungarische Notation zusammen -> war mal eine nette Idee, ist aber in der Praxis fast nicht zu gebrauchen.
"Sauber" kann man auf mehrere Arten sehen. Einmal als Formatierungsstil (coding standard) so wie im Wikilink oben. Ich merke bei mir, dass ich bestimmte Regeln daraus bereits automatisch anwende. Man kann auch Programme wie GNU Indent über den Sourcecode laufen lassen, die eine saubere Formatierung machen. Oder in Sinn von "Beautiful Code". Da ist die Formatierung zunächst aussen vor und es geht um die Methoden/Ideen wie man etwas sauber, schön, elegant in einer (irgendeiner) Programmiersprache formuliert oder umsetzt. Es eignen sich manche Sprachen besser oder schlechter für bestimmte Lösungen, aber grundsätzlich ist dieser Aspekt nicht sprachabhängig. Es gibt ein vielgelobtes und vielgehasstes Buch "Beautiful Code". Unabhängig davon drückt der folgende Vortrag von Laura Thomson ein paar Ideen ganz gut aus: http://laurat.blogs.com/talks/write_beautiful_code.pdf
Ich kommentiere Code an sich recht spaerlich. Ich versuche wie oben beschrieben, aussagekraeftige Variablennamen zu finden und wenn moeglich verwende ich den einfachsten Algorithmus um ein Problem zu loesen. Worauf ich aber sehr grossen Wert lege ist die Dokumentation der Schnittstellen (APIs und interne Interfaces). Wenn die Dokumentation mir sagt was eine bestimte Funktion macht, ist es relativ einfach herauszufinden wie sie es macht, durch Lesen des Quellcodes. Kritische oder "trickreiche" Zeilen, sollten vermieden oder zumindest gut dokumentiert werden. Wenn die Schnittstelle gut dokumentiert ist (es ist der "Vertrag" zwischen Entwickler und Aufrufer den Funktion) dann ist Refactoring ziemlich enifach moeglich, ohne reverse engineering anwenden zu muessen. Dokumentations-Hilfen wie Doxygen sind hier sehr nuetzlich und warnen, wenn Funktionen oder Parameter einer Funktion nicht dokumentiert sind. Teil der "Dokumentation" ist die Verwendung von Schluesselwoertern wie const und static in C so oft als moeglich. Diese helfen sowohl dem Compiler besseren Code zu generiereun, liefern aber auch dem Programmierer ein paar Informationen wie die Variablen intern verwendet werden. Andere Stichworte: - aussagekraeftige Namen von Variablen und Funktionen - Vermeidung langer Funktionen/Aufspalten in kleinere Funktionen und Bibliotheken - einheitlicher Stil bei Formattierung und Namensgebung - Vermeidung voreiliger Optimierungen Thomas
Thomas Pircher wrote: > Andere Stichworte: > - aussagekraeftige Namen von Variablen und Funktionen Bläst ins selbe Horn, sollte eigentlich selbstverständlich sein und wird trotzdem immer wieder mal ignoriert: Eine Funktion sollte das, und nur das, machen was 'aussen drauf steht'. Bzw. auch die Umkehrung davon: Den Funktionsnamen so wählen, dass klar ist was genau die Funktion macht. Gut ist es auch, sich ein Nomenklaturschema zurechtzulegen, dass dann auch durchgezogen wird. zb. Funktionen die eine Aktion auslösen, haben ein Verb im Funktionsnamen, das wie ein Befehl einzusetzen ist. zb. Funktionen, die einen Status abfragen beginnen mit 'is' Beispiel: UartConnect // Aufforderung an die UART die Verbindung // herzustellen. 'Uart, connecte dich!' isUartConnected // Abfrage, ob die Uart connected ist Ich bin bei mir zb. dazu übergegangen, strend zwischen Remove und Kill zu unterscheiden. Die Unterscheidung ist zb bei Programmen wichtig, die einen Undo Mechanismus benutzen. Remove entfernt ein Objekt aus einer Datenstruktur. Kill löscht ein Objekt. Habe ich zb eine Liste, dann entfernt RemoveObject ein bestimmtes Objekt aus der Liste. Das Objekt existiert aber nach dem Aufruf noch. Wohingegen bei einem KillObjekt das Objekt entfernt und gelöscht wird. (Remove habe ich deswegen gewählt, weil der Begriff delete zumindest in C++ schon mit einer Bedeutung belegt ist). Ein anderes Negativbeispiel kennen wohl alle MFC-Programmierer: Wie heisst den der vermaledeite Aufruf, um in einem Control alle Items zu löschen? Mal heisst er ResetControl, dann DeleteItems, DeleteAll, DeleteAllItems, etc.. Also: unterschiedliche Funktionsnamen für identische Funktionalität. Zumindest mich treibt das regelmässig in den Wahnsinn, wenn ich wieder mal die Intellisense Liste durchforsten muss, um zu wissen wie die Funktion bei einer CComboBox heisst. Man kann es gar nicht oft genug betonen: Aber solche Nomenklaturregeln helfen einem, Ordnung ins Chaos zu bringen. Das sollte man nie unterschätzen.
>Also zum Beipiel Sachen wie das "#define bla 42" nicht in die *.c >sondern in *.h reingehört Das ist in der Allgemeinheit natürlich falsch. Auch hier gilt: So lokal wie möglich (wobei für Macros der Scope natürlich immer (mindestens) das File ist. h.
Heinze wrote: >>Also zum Beipiel Sachen wie das "#define bla 42" nicht in die *.c >>sondern in *.h reingehört > > Das ist in der Allgemeinheit natürlich falsch. > Auch hier gilt: So lokal wie möglich (wobei für Macros der > Scope natürlich immer (mindestens) das File ist. > > h. Richtig! So sehe ich das auch. Wenn ein Makro zum Beispiel nur als "kleine Funktion" missbraucht wird um ein Chipselect zu setzen/zu löschen, dann gehört es an den Anfang des C Files. Denn was soll der Rest des Projektes damit anfangen? Erst, wenn etwas für andere Codemodule auch interessant wird an Makros, ODER, wenn der Programmierer über Defines an einem Code Module von Außen Einstellungen vornehmen kann/darf/soll, DANN gehört es in eine/die .h Datei. Und zwar mit Prefix, damit es keine Namensprobleme mit anderen Codemodulen gibt. Wohingegen das Prefix innerhalb der .c Datei egal ist. Da kann das Makro ruhig SELECT_CHIP() oder wie auch immer heißen. Übrigens gehört zu sauberem Code auch const und static Qualifier zu benutzen. (Google gibt da interessante Sachen). Wenn eine Funktion NUR von der gleichen .c Datei aus benutzt wird (zum Beispiel eine SPI Lowlevel Funktion), dann sollte man sie static machen. Denn so liegt sie nicht mehr im globalen Namensraum beim kompilieren und es gibt weniger Namenskonflikte. Einen Funktionsparameter-Zeiger sollte man auf const zeigen lassen, wenn die Funktion den Zeigerparameter NUR zum LESEN benutzt und nicht zum schreiben (was dann eh nicht mehr funktionieren würde). Das gibt dem Programmierer/Benutzer-des-Codes eine gewisse "Sicherheit". Achja eine Sache noch: "Sauberer Code" ist subjektiv und es gibt viele Ansichten, wie sauberer Code auszusehen hat. Wichtig ist, dass du dir auch ein eigenes Empfinden zulegen solltest, wie du meinst, dass ein Code sauber und sinnvoll geschrieben ist. Dabei ist aber wiederum wichtig, nicht von "allgemeinen" Merkmalen von sauberem Code abzuweichen, ansonsten kannst nur du den Code lesen, sonst aber keiner ;)
Was man bei dieser Diskussion nicht vergessen darf: Wenn sich etwas ändert, muß man an jeder Stelle die Doku anpassen, sodaß alles jederzeit konsistent ist. Also: Wenn man irgend eine Methode ändert und sie dokumentiert hat und diese Dokus evt. an unterschiedlichen Stellen auftauchen, dann muß man sich schon die Mühe machen, an allen Stellen anzupassen, damit die Doku immer überall stimmig ist. Das kann viel Arbeit bedeuten, hängt aber auch mit "gutem Programmierstil" zusammen.
Man da hab ich was losgetretten... >>>Also zum Beipiel Sachen wie das "#define bla 42" nicht in die *.c >>>sondern in *.h reingehört >> >> Das ist in der Allgemeinheit natürlich falsch. >> Auch hier gilt: So lokal wie möglich (wobei für Macros der >> Scope natürlich immer (mindestens) das File ist. >> >> h. >Richtig! So sehe ich das auch. Wenn ein Makro zum Beispiel nur als >"kleine Funktion" missbraucht wird um ein Chipselect zu setzen/zu >löschen, dann gehört es an den Anfang des C Files. >Denn was soll der Rest des Projektes damit anfangen? So war das nicht gemeint. Ich habe im Code eine Liste mit ca. 40 Einträgen wie "#define XXX_STATUS_OK 0x01". Die hab ich dann in die "XXX.h" satt in die "XXX.c" gehauen. > Auch hier gilt: So lokal wie möglich Ist schon klar, daß sowas in der main.c nix zu suchen hat. Ich will die XXX.h und XXX.c woanders nochmal verwenden. Danke für das Schlagwort Programmierstil und Laura's Blog! Sowas wie eine komplette Beschreibung eines aktuellen und allgemein akzeptierten Stils gibts nicht, oder?
Code-Schweinchen wrote: > Sowas wie eine komplette Beschreibung eines aktuellen und allgemein > akzeptierten Stils gibts nicht, oder? Nein, weil es keinen allgemein akzeptierten Stil gibt. :)
Dann frag ich mal so: Wo gibts eine komplette Beschreibung eines guten Stils? Weil irgendwas aus verschiedene zusammenpuzzeln ist auch Mist.
Code-Schweinchen wrote: > Dann frag ich mal so: Wo gibts eine komplette Beschreibung eines guten > Stils? Giebt es leider auch nicht. Style gudes sind nun einmal eine Geschmacksfrage. http://de.wikipedia.org/wiki/Programmierstil wurde schon genannt. Der Artikel enthaelt eine Liste von weiteren Style guides. Dann gibt es noch Industrie-Standards (siehe http://de.wikipedia.org/wiki/Programmierstil#Regelwerke), die kosten allerdings. Du wirst wohl nicht darum herumkommen dir ein paar Beschreibungen durchzulesen und dir das herauszupicken, was dir zusagt. Mein Tip: Regeln, die nicht begruendet sondern nur postuliert werden sind einen feuchten Kehricht wert. Thomas
Da die Frage nach "einem" guten Stil und nicht "dem" guten Stil ist: z.B. die Source Code Style Guide von NetBSD http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/share/misc/style?rev=1.45&content-type=text/plain Die geht auf die alte KNF von BSD zurück, d.h. da sind mittlerweile >25 Jahre an Erfahrung drin. http://en.wikipedia.org/wiki/KNF
Karl heinz Buchegger wrote: > In dieselbe Kategorie fallen die Kommentare > > // *********** globale Variablen ************* > > sinnlos. In großen Projekten kann sowas ein wenig Sinn haben, aber dann müssen die entsprechenden Kommentarzeilen auch wirklich bis wenigstens Spalte 70 gehen. Dadurch fallen sie beim schnellen drüber hinweg Scrollen sofort ins Auge, und man kann die einzelnen Teile etwas schneller finden. Im Prinzip würden es natürlich auch reine Trennzeilen à la /* ================================================================*/ ohne jeden Text drin genauso tun.
>http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src...
Das passt echt gut zudem was ich gesucht hab!
Thanks!
Eines steht nicht drin: Sollen die emums, macros, typedefs und structs
in *.h oder in *.c?
> Eines steht nicht drin: Sollen die emums, macros, typedefs und structs > in *.h oder in *.c? Das steht hier doch schon im Thread! Meist in die Header-Datei (*.h), es gibt aber Ausnahmen, siehe die obigen Beiträge.
Hallo, ich bin nicht damit einverstanden, alles in den Header zu knallen, was meist dorthin gehört. Der Header soll die Schnittstelle enthalten, also alles, was vom Modul öffentlich zur Verfügung gestellt wird. Er kann enthalten, was öffentlich bekannt sein muss, also z.B. Verwendung der hardware (Ports, Timer, etc.) Es gehört z.B. nicht hinein, daß eine lokale Variable im Modul XYZ die Werte GEPLANT=1, IN_ARBEIT=2 und FERTIG=3 speichern soll. das geht ausserhal niemanden was an. Es ist ausserdem guter Programmierstil, gerade solche Details nach aussen hin zu minimieren. Das wird dann auch Abstraktion genannt. Ich arbeite gerade mit dem ARV an der TWI-Schnittstelle basierend auf den Beispielen von Atmel. Das erste, was ich gemacht hatte war, die { nach dem GNU-Standard auszurichten und die #define der Statuswerte des TWI in das Modul zu verlagern. Was haltet Ihr von xyz.c included xyz.h sowie xyz_provate.h für die lokalen #define ? Gruß, Michael
der peterle wrote: > Das steht hier doch schon im Thread! Meist in die Header-Datei (*.h), es > gibt aber Ausnahmen, siehe die obigen Beiträge. Headerdateien beschreiben Interfaces einer Implementierung (C-Datei) zur Außenwelt. Insofern gehören Typen, Variablen (mit "extern"!) und #defines dort hinein, wenn sie auch außerhalb der eigentlichen Implementierung benötigt werden. Alles, was nur in einer einzigen C-Datei benötigt wird, muss auch nur dort stehen. Einen richtigen Sinn für xxx_private.h, die dann nur von xxx.c benutzt wird, kann ich nicht erkennen (wenngleich ich auch meine Finger in Projekten drin habe, die sowas benutzen ;-).
Micha54 wrote: > Ich arbeite gerade mit dem ARV an der TWI-Schnittstelle basierend auf > den Beispielen von Atmel. Das erste, was ich gemacht hatte war, die { > nach dem GNU-Standard auszurichten und die #define der Statuswerte des > TWI in das Modul zu verlagern. Die sind schon in <util/twi.h> definiert, mit sinnvollen Namen. > xyz.c included xyz.h sowie xyz_provate.h für die lokalen #define ? Das halte ich fuer zu kompliziert; das Verzeichnis wird dann unuebersichtlich mit den vielen Dateien und die Symbole sind nicht dort definiert (.c) wo sie verwendet werden. Ist aber Geschmackssache. > Es ist ausserdem guter Programmierstil, gerade solche Details nach > aussen hin zu minimieren. Das wird dann auch Abstraktion genannt. Full Ack. Thomas
Jörg Wunsch wrote: > Einen richtigen Sinn für xxx_private.h, die dann nur von xxx.c > benutzt wird, kann ich nicht erkennen (wenngleich ich auch meine > Finger in Projekten drin habe, die sowas benutzen ;-). Hallo, wenn Du libraries schreibst mit vielen Funktionen, die nur sparsam in den Code gelinkt werden sollen, dann musst Du wohl oder übel viele Module schreiben, die gemeinsame Deklarationen nutzen, also sogar auch shared variables. TWI: master und slave haben teilweise gemeinsamen Code, aber es soll immer nur der Master oder der Slave gelinkt werden. TWI-STATUS ist shared und local. twi.h enthält die Schnittstellen twi.c enthält gemeinsamen Code twi_queueRequest() twi_master.c enthält den Mastercode Interrupthander und 1 Timer gegen deadlocks twi_slave.c enthält den Slavecode Interrupthandler twi_private.h enthält alles was twi gemeinsam hat, nach aussen aber nicht sichtbar sein soll Das ergibt ein kleines Projekt für twi.lib Benutze ich twi, so benötige ich nur twi.lib und twi.h Ok so ? stdio machts doch nicht anders.... Gruß, Michael
Michael Appelt wrote: > wenn Du libraries schreibst mit vielen Funktionen, die nur sparsam in > den Code gelinkt werden sollen, dann musst Du wohl oder übel viele > Module schreiben, die gemeinsame Deklarationen nutzen, also sogar auch > shared variables. Richtig. Du hast mich auch flashc interpretiert. Ich schrieb, dass ich kein richtigen Sinn in xxx_private.h sehe, wenn diese Datei dann nur noch von xxx.c (und sonst niemandem) reingezogen wird.
D'accord, hatte ich so nicht gelesen... BTW guter Tip, ich schau mir nachher mal die twi.h an. Gruß, Michael
Michael Appelt wrote: > Jörg Wunsch wrote: >> Einen richtigen Sinn für xxx_private.h, die dann nur von xxx.c >> benutzt wird, kann ich nicht erkennen (wenngleich ich auch meine >> Finger in Projekten drin habe, die sowas benutzen ;-). > > Hallo, > > wenn Du libraries schreibst mit vielen Funktionen, die nur sparsam in > den Code gelinkt werden sollen, dann musst Du wohl oder übel viele > Module schreiben, die gemeinsame Deklarationen nutzen, also sogar auch > shared variables. Das hängt dann aber auch vom Linker ab. Leider ist das beim gcc so. Andere Linker holen sich aus Libraries/Object-Files tatsächlich nur die Funktionen, die tatsächlich benötigt werden. In dem Sinne kann einem so ein Linker den Tag versüssen, weil er mich nicht dazu zwingt, Klimmzüge zu machen.
Weiter oben habe ich gelesen, dass man 'ungarische Notation' auf jeden Fall vermeiden sollte. Soweit ich das verstanden habe, ist das ja nichts anderes, als vor jeden Variablennamen ein Präfix zu setzen. Ich mache das oft so: tByte bVar1; tDWord dwVar2; tWord wVar3; static tWord fTestFunction(void); Warum soll sowas nicht gut sein? Funktionen haben immer das Präfix f. Typen haben t, DWords dw, Words W, ... Ich finde das hilfreich beim Lesen des Codes. Wenn irgendwo ein Befehl auftaucht (als Beispiel) wTest = 0x12345; dann sehe ich gleich: aha, Test ist ein Word, der Befehl kann so gar nicht funtionieren. Nachteil ist halt, wenn man den Typ einer Variablen ändert, muss auch der Name geändert werden (was bisschen umständlich ist).
C-Fan wrote:
> Warum soll sowas nicht gut sein?
Weil es überflüssig ist. Ordentliche Variablennamen sind so gewählt,
dass sie sich möglichst selbst erklären, statt den Datentyp zu
erklären, den sie einmal besitzt haben.
Dein konstruiertes Beispiel sollte sowieso eine Compilerwarnung
generieren, die ist zuverlässiger als der Blick des Programmierers,
der die Anzahl der Ziffern bei
1 | uint32_t i; |
2 | ...
|
3 | i = 0x123456789; |
nämlich ohnehin nicht mehr auf einen Blick erfassen kann. Weil es den normalen Lesefluss stört. Immer, wenn ich "lpParam" lese, frage ich mich, was der Drucker ("line printer", gängige Abkürzung "lp") hier eigentlich im Spiel soll. Außerdem benutzt die ungarische Notation der Marke Microsoft völlig veraltete Begriffe: "word" ist normalerweise die Maschinenwortbreite. Die Zeiten, da Windows auf 16-bit-Maschinen lief, sind aber auch schon lange vorbei. Ein "word" wäre also bei vielen heutigen Maschinen am ehesten ein 32-bit-Datentyp, der 16-bit-Datentyp müssten dann korrekterweise "half word" heißen. Ein "double word" ist auf diesen Maschinen dann 64 bits groß. Wenn schon, dann müsste man also die ungarische Notation mit Präfixen wie s8, u16 oder s32 benutzen. Aber schöner zu lesen wird sie davon natürlich auch nicht.
C-Fan wrote: > Weiter oben habe ich gelesen, dass man 'ungarische Notation' auf jeden > Fall vermeiden sollte. Soweit ich das verstanden habe, ist das ja nichts > anderes, als vor jeden Variablennamen ein Präfix zu setzen. > Ich mache das oft so: > > tByte bVar1; > tDWord dwVar2; > tWord wVar3; > > static tWord fTestFunction(void); > > Warum soll sowas nicht gut sein? > Funktionen haben immer das Präfix f. Typen haben t, DWords dw, Words W, > ... > Ich finde das hilfreich beim Lesen des Codes. Wenn irgendwo ein Befehl > auftaucht (als Beispiel) wTest = 0x12345; dann sehe ich gleich: aha, > Test ist ein Word, der Befehl kann so gar nicht funtionieren. > Nachteil ist halt, wenn man den Typ einer Variablen ändert, muss auch > der Name geändert werden (was bisschen umständlich ist). Hallo, wenn ich in einer Entwicklungsumgebung keine Klassen oder echte Module oder ähnliches nicht zur Verfügung stehen, dann gruppiere ich die Namen über einen gemeinsamen Prefix, quasi den Namen der gedachten Klasse. Beispiel Fifo.c: fifo_Init() initialisiert die Klasse fifo_Put() fügt ein Element an fifo_Get() entnimmt das erste, fifo_Max ist die maximal mögliche Länge usw. Durch diese Notation sehe ich in der Linkmap unmittelbar, das es eine Konstante fifo_Max gibt und ich sie nuzen kann. Eine Gruppierung nach Art der Deklaration dagegen macht für mich keine Sinn. Gruß, Michael
C-Fan wrote:
> Warum soll sowas nicht gut sein?
Weil es bis auf triviale Fälle (die du hier verwendet hast) ganz, ganz
schnell zusmmanenbricht.
Welchen Präfix verwendest du für ein Array aus Words?
Angnommen du hast eine Struktur
struct irgendwas
{
...
};
welchen Präfix verwendest du für eine Variable diesen Types. Was machst
du mit einem Array aus solchen Strukturen? Wie bezeichnest du einen
Pointer auf ein Array von derartigen Strukturen? etc, etc.
Irgendwann hast du dann mehr Buchstaben im Präfix als im Variablenname
und dann geht das ganze am Sinn der ungarischen vorbei.
Zudem (meine persönliche Meinung): ungarische Notation vermittelt hier
ein Gefühl der Sicherheit, das in der Realität nicht da ist. Ob dSteuer
tatsächlich eine double Variable ist oder nicht, kannst du nur klären,
indem du dir die Definition ansiehst. Heutige gute IDEs unterstützen
dich, halt einfach die Maus auf den Variablennamen und die IDE sucht dir
zugehörige Definition.
Um das noch weiter zu unterstüzen: überleg mal, wie Du 200 Quellfiles kontrollieren kannst, nicht nur so ein Miniprojekt mit 10 C-files und ein paar Headerdateien. Gruß, Michael
Vielleicht mal noch einen Hinweis zur "ungarischen Notation" siehe http://de.wikipedia.org/wiki/Ungarische_Notation Von einigen Leuten (z. B. JoelOnSoftware) hört man daß die übliche Verwendung dieser Notation (also ucVariable für eine unsigned char Variable) nur ein Mißverständnis sei, das sich leider durchgesetzt hat. Der ursprüngliche "Erfinder" der Notation wollte mit dem Typen-Präfix nicht den Typ im Sinne der Syntax (also long, short unsigned usw. beschreiben) sondern den Kontext (also Zähler, Zeileindex, Spaltenindex [er war im Excel Team von Microsoft]) beschreiben. Obs stimmt weiß ich nicht. Auch ganz nett: http://mindprod.com/jgloss/unmainnaming.html Punkt 30
Dirk Dörr wrote: > Der ursprüngliche "Erfinder" der Notation wollte mit dem Typen-Präfix > nicht den Typ im Sinne der Syntax (also long, short unsigned usw. > beschreiben) sondern den Kontext (also Zähler, Zeileindex, Spaltenindex > [er war im Excel Team von Microsoft]) beschreiben. Wusste ich bisher gar nicht. Ich kenn die ung. Notation eigentlich nur in der missverstandenen Version. In der ursprünglichen Version ist das allerdins sinnvoll! Das unterstütze ich voll. Wir hatten selbst immer wieder das Problem, dass wir in einer CAD Anwendung immer wieder Bezugssysteme durcheinander gewürfelt haben. Erst ein Präfixen der Variablen mit dem Bezugssystem brachte dann Ordnung ins Chaos. Dadurch wurde klar, dass in diversen Offsetrechnereien ganz einfach diverse Koordinatensysteme durcheinandergewürfelt wurden.
Also ein paar Erfahrungen aus meiner Praxis, bezogen auf C (nicht C++!): - #include nur mit Headern (logisch) - in die Header immer sowas wie '#ifndef BLA_H #define BLA_H ... #endif' - Bezeichner alle klein schreiben und mit Unterstrichen aufteilen - char für Zeichen und Zeichenketten (char *), sonst für garnix - für Zahlen zum Rechnen die Typen aus <stdint.h> - für Längen (Stringlänge, Arraylänge, Speichergröße) gibts size_t aus <stddef.h> - int ist ok wenns um Fehlercodes oder 'ja/nein' geht; in letzterem Fall ist bool als <stdbool.h> vorzuziehen, wenns verfügbar ist (C99) - bei Zeigerarithmetik: nicht (zeiger1 < zeiger2) sondern ((zeiger2 - zeiger1) > 0), sowas sollte nur vorkommen, wenn z.B. beim Puffer errechnet werden soll, wie viel Platz noch ist. (zeiger1 == zeiger2) ist ok - für Aufzählungen gibts enum{}, für Konstanten #define - nicht im Header exportierte Funktionen 'static' machen, damit die nicht versehentlich in den Linker kommen - um Codeblöcke bei if und Schleifen usw. immer geschweifte Klammern setzen - Kommentare mit /* */ und nicht mit // (s.u... Geschmackssache) - Strukturen und Aufzählungen (struct + enum) nicht mit 'typedef' freistellen. Auf diese Weise muss man bei Vereinbarungen immer 'struct' oder 'enum' dazuschreiben und sieht, was man hat. - Quelltext sinnvoll einrücken - Typenpräfixe weglassen, bringen nichts außer Verwirrung, lieber sinnvollere Bezeichner wählen, die sagen, was drinsteht - goto kann sinnvoll sein, aber nicht auf Biegen und Brechen - malloc() und Co. immer mit sizeof() im Argument verwenden, nicht malloc(5), sondern malloc(5*sizeof(uint8_t)) oder was auch immer - Zeiger sind keine Integer! Ein Zeiger darf in einen Integer und danach wieder zurück in einen Zeiger umgewandelt werden, ohne das was schiefgeht, allerdings ist nicht festgelegt, wie der Integer dazwischen aussieht. - Wenn zehn Zeilen Kommentar nötig sind, um ein Stück Code zu erklären, steckt meistens ein Konzeptfehler drin :-D - vararg und Co. sind tot, es heißt nun <stdarg.h> - kurze Bezeichner wie 'i' sind ok, wenn sie in eingängigen Konstruktionen (--> for) benutzt werden Das fällt mir jetzt spontan ein. Zum Teil ist viel persönlicher Geschmack dabei, klar.
> - bei Zeigerarithmetik: nicht (zeiger1 < zeiger2) > sondern ((zeiger2 - zeiger1) > 0), Womit begründest du das? > - Zeiger sind keine Integer! Ein Zeiger darf in einen Integer > und danach wieder zurück in einen Zeiger umgewandelt werden, > ohne das was schiefgeht, Dafür würde ich meine Hand nicht ins Feuer halten. In C gibt es keine Regel, die einen Zusammenhang zwischen sizeof(int) und sizeof(void*) herstellt.
Karl heinz Buchegger wrote: >> - bei Zeigerarithmetik: nicht (zeiger1 < zeiger2) >> sondern ((zeiger2 - zeiger1) > 0), > > Womit begründest du das? Mit persönlichem Geschmack :-) Zeiger minus Zeiger ergibt die Anzahl der dazwischenliegenden Objekte, Zeiger > Zeiger find ich ganz einfach 'unlogisch' oder 'unästhetisch'. >> - Zeiger sind keine Integer! Ein Zeiger darf in einen Integer >> und danach wieder zurück in einen Zeiger umgewandelt werden, >> ohne das was schiefgeht, > > Dafür würde ich meine Hand nicht ins Feuer halten. > In C gibt es keine Regel, die einen Zusammenhang zwischen sizeof(int) > und sizeof(void*) herstellt. In C darf ein Zeiger in einen ausreichend großen Integer umgewandelt werden (K&R, Seite 191), die Umwandlungsfunktion ist aber nicht näher bestimmt. So war das gemeint. Insbesondere so Dinger sehe ich nicht gerne:
1 | char *Zeiger; |
2 | |
3 | Zeiger = (char *) 123 /* Nur weil der Zeiger grad da ist */ |
Sven P. wrote: > Karl heinz Buchegger wrote: >>> - bei Zeigerarithmetik: nicht (zeiger1 < zeiger2) >>> sondern ((zeiger2 - zeiger1) > 0), >> >> Womit begründest du das? OK. Damit kann ich leben :-) > Mit persönlichem Geschmack :-) Zeiger minus Zeiger ergibt die Anzahl der > dazwischenliegenden Objekte, Zeiger > Zeiger find ich ganz einfach > 'unlogisch' oder 'unästhetisch'. Wieso: Die Position auf die Zeiger 1 zeigt ist vor der Position auf die Zeiger 2 zeigt. Zeiger hat man ja oft als Positionen in einem Array. Die Aussage ist dann: Zeiger 1 befindet sich noch auf einem Arrayelement vor dem von Zeiger 2 (Zeigerarithmetik ist sowieso nur dann definiert, wenn beide Zeiger in dasselbe Objekt zeigen) > So war das gemeint. Ah. ok. Mit Integer war also kein int im Speziellen gemeint.
Karl heinz Buchegger wrote: >> Mit persönlichem Geschmack :-) Zeiger minus Zeiger ergibt die Anzahl der >> dazwischenliegenden Objekte, Zeiger > Zeiger find ich ganz einfach >> 'unlogisch' oder 'unästhetisch'. > > Wieso: Die Position auf die Zeiger 1 zeigt ist vor der Position auf die > Zeiger 2 zeigt. Zeiger hat man ja oft als Positionen in einem Array. Die > Aussage ist dann: Zeiger 1 befindet sich noch auf einem Arrayelement vor > dem von Zeiger 2 Ich bin da immer verleitet, zu denken, Zeiger wären Zahlen, die man vergleicht. Bei mir hat sich eben '(Zeiger - Zeiger) gibt Zahl und die kann man vergleichen' eingeprägt :-)
Was ich grad noch lese: Lint hätte man doch nich kaufen müssen... da gibts doch etliche freie Alternativen :-)
Naja, split halt :-) Reicht doch und ist scheinbar sogar leistungsfähiger als das Original, wenn man einschlägigen Foren Glauben schenken darf. Habs gestern nur mal kurz ausprobiert, mir bringts (bisher) nicht viel, aber naja.
Berti wrote:
> MISRA ist ein toller Ansatzpunkt für sauberes Programmieren!
Naja, zuweilen auch ein sehr streitbarer. Ich habe schon ziemlich
sinnlose Dinge da gesehen. Zweiter Nachteil: man diskutiert wie
der Blinde übers Licht, da man den Regelsatz nur gegen viel Geld
erwerben darf. Damit gibt es auch keine Opensource-Tools für die
Kontrolle.
Bug-killing standards for firmware coding By Michael Barr Embedded.com (03/24/09, 03:09:00 PM EDT) http://www.embedded.com/design/opensource/216200567
Da das noch nicht vorkam: Die NASA und das Pentagon sollen auch einen sehr guten Styleguide haben, da die Sicherheitsanforderungen entsprechend hoch sind. Ich weiss aber nicht, wo man den herbekommen kann und ob der nicht etwas "zu streng"/overkill fuer den normalen Einsatz ist...
Auch wenn der Fred schon ewig alt ist und dem Code-Schweinchen schon geholfen sein sollte, hier nur für Dich Jeanne: http://homepages.inf.ed.ac.uk/dts/pm/Papers/nasa-c-style.pdf
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.