Forum: Compiler & IDEs Tutorial für _sauberen_ C-Code


von Code-Schweinchen (Gast)


Lesenswert?

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.

von Philip (Gast)


Lesenswert?

für den anfang und als googlefutter:
http://de.wikipedia.org/wiki/Programmierstil

von avion23 (Gast)


Lesenswert?

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.

von Tommy (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tommy wrote:
> Der Wikipedia-Link ist wirklich gut. Und bitte auch
> "ungarische Notation" beachten!

"beachten" im Sinne von "vermeiden", oder? :-)

von Christian R. (supachris)


Lesenswert?

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.

von Matthias N. (vbchaos)


Lesenswert?

> 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)

von P. S. (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

"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

von Thomas P. (tpircher) Benutzerseite


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Heinze (Gast)


Lesenswert?

>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.

von Simon K. (simon) Benutzerseite


Lesenswert?

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 ;)

von Günter R. (galileo14)


Lesenswert?

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.

von Code-Schweinchen (Gast)


Lesenswert?

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?

von Thomas P. (tpircher) Benutzerseite


Lesenswert?

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

von Code-Schweinchen (Gast)


Lesenswert?

Dann frag ich mal so: Wo gibts eine komplette Beschreibung eines guten 
Stils?
Weil irgendwas aus verschiedene zusammenpuzzeln ist auch Mist.

von Thomas P. (tpircher) Benutzerseite


Lesenswert?

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

von Urs (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Code-Schweinchen (Gast)


Lesenswert?

>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?

von der peterle (Gast)


Lesenswert?

> 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.

von Micha54 (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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

von Thomas P. (tpircher) Benutzerseite


Lesenswert?

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

von Michael A. (micha54)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Michael A. (micha54)


Lesenswert?

D'accord, hatte ich so nicht gelesen...

BTW guter Tip, ich schau mir nachher mal die twi.h an.

Gruß,
Michael

von Karl H. (kbuchegg)


Lesenswert?

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.

von C-Fan (Gast)


Lesenswert?

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).

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Michael A. (micha54)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Michael A. (micha54)


Lesenswert?

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

von Dirk D. (dirkd)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Sven P. (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

> - 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.

von Sven P. (Gast)


Lesenswert?

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 */

von Karl H. (kbuchegg)


Lesenswert?

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.

von Sven P. (Gast)


Lesenswert?

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 :-)

von Simon K. (simon) Benutzerseite


Lesenswert?

Brauchbare Zusammenfassung von haku!

von Sven P. (Gast)


Lesenswert?

Was ich grad noch lese: Lint hätte man doch nich kaufen müssen... da 
gibts doch etliche freie Alternativen :-)

von Gast (Gast)


Lesenswert?

@Sven: ich kenne (für C) nur splint - was gibt es denn noch?

von Sven P. (Gast)


Lesenswert?

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.

von Berti (Gast)


Lesenswert?

MISRA ist ein toller Ansatzpunkt für sauberes Programmieren!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

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

von Jeanne (Gast)


Lesenswert?

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...

von wurstwasser (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.