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.
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. :)
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
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.
> 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.
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_ti;
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 :-)
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.
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...