Best Practice beim Schreiben konstanter Parameter für eingebettete Systeme

8

Dies ist ein Fall von "statisch const" vs "#define" in C " für eingebettete Systeme.

Was ist bei großen / mittleren Projekten mit "übergebenem" Code und Modulen die beste Vorgehensweise beim Schreiben konstanter Parameter für Ihre Include-Dateien, Module usw.?

In einem Code "übergibt", wo Sie nicht wissen, ob die Namen, die Sie wählen, in einer anderen eingeschlossenen Datei definiert sind oder mit extern oder als Makros in einer anderen Datei, die Ihre Datei enthalten könnte, aufgerufen werden.

Diese 3 Optionen haben:

  1. static const int char_height = 12;
  2. #define CHAR_HEIGHT 12
  3. enum { char_height = 12 };

welcher wäre besser (auf einem eingebetteten System mit unbekannten Speicherbeschränkungen)?

Der ursprüngliche Code verwendet dafür hauptsächlich #define , aber diese Art von Konstanten wird willkürlich auf verschiedene Arten implementiert (und an verschiedenen Orten sogar in den gleichen Dateien), da es scheint, dass mehrere Leute diese Demo-Software für eine bestimmte entwickelt haben Gerät.

Insbesondere ist dies ein Demo-Code, der alle Hardware- und SDK-Funktionen eines bestimmten Geräts anzeigt.

  

Die meisten Daten, über die ich nachdenke, sind die Art der Konfiguration der Umgebung: Bildschirmdimensionen, Zeichensatzeigenschaften, etwas, um die Lesbarkeit des Codes zu verbessern. Nicht auf die automatische Konfiguration könnte ein Compiler und Pre-Prozessor tun. Da aber viel Code drin ist und ich Angst vor globalen Namenskonflikten habe, zögere ich, # define's

zu verwenden

Gegenwärtig denke ich, dass es besser wäre, das Projekt von Grund auf neu zu schreiben und die meisten der bereits geschriebenen Funktionen neu zu implementieren, um ihre Konstanten aus nur einer c-Datei zu erhalten oder die Implementierung der Konstanten auf nur einen Stil zu reorganisieren.

Aber:

  1. Dies ist ein Ein-Personen-Projekt (es würde also viel Zeit in Anspruch nehmen, alles neu zu implementieren)
  2. Der bereits implementierte Code funktioniert und wurde mehrmals überarbeitet. (Wenn es nicht kaputt ist ...)
Sdlion 07.01.2014, 19:00
quelle

5 Antworten

5

Berücksichtigen Sie immer Lesbarkeit und Speicherbeschränkungen. Außerdem sind Makros einfach Copy / Paste-Operationen, die vor der Kompilierung auftreten. Mit diesen Worten möchte ich Folgendes tun:

  • Ich definiere alle Variablen, die als static const konstant sind, wenn sie in einer c-Datei verwendet werden sollen (z. B. nicht global über mehrere Dateien hinweg zugänglich). Alles, was als const definiert ist, muss im Dateibereich in ROM gespeichert werden. Offensichtlich können Sie diese Variablen nach der Initialisierung nicht ändern.
  • Ich definiere alle konstanten Werte mit #define .
  • Ich verwende enum erations, wo es zur Lesbarkeit beiträgt. An jedem Ort, an dem Sie einen festen Wertebereich haben, bevorzuge ich Aufzählungen, um die Absicht explizit anzugeben.

Versuchen Sie, das Projekt mit einer objektorientierten Perspektive anzugehen (obwohl c nicht OO ist). Verstecken Sie private Funktionen (erstellen Sie keinen Prototyp in der Kopfzeile), verwenden Sie keine Globals, wenn Sie es vermeiden können, markieren Sie Variablen, die nur in einem c-Modul (Datei) liegen sollen als static , etc.

    
bblincoe 07.01.2014, 19:08
quelle
3

Sie sind 3 verschiedene Dinge, die in 3 verschiedenen Situationen verwendet werden sollten.

  • #define sollte für Konstanten verwendet werden, die zur Kompilierzeit ausgewertet werden müssen. Ein typisches Beispiel ist die Größe eines statisch zugewiesenen Arrays, d. H.

    %Vor%

    Es ist auch gut, #define alle Konstanten zu verwenden, bei denen es egal ist, wie oder wo die Konstante zugewiesen ist. Menschen, die behaupten, dass es eine schlechte Übung ist, sprechen nur ihre eigenen, persönlichen, subjektiven Meinungen.

    Aber natürlich können Sie für solche Fälle auch const Variablen verwenden. Es gibt keinen wichtigen Unterschied zwischen #define und const , mit Ausnahme der folgenden Fälle:

  • const sollte verwendet werden, wenn es darauf ankommt, unter welcher Speicheradresse eine Konstante zugewiesen ist. Es sollte auch für Variablen verwendet werden, die der Programmierer wahrscheinlich häufig ändern wird. Wenn Sie const verwendet haben, können Sie die Variable leicht in ein Speichersegment im EEPROM oder Datenblitz verschieben (aber wenn Sie dies tun, müssen Sie sie als flüchtig deklarieren).

    Ein weiterer kleiner Vorteil von const ist, dass Sie eine höhere Typsicherheit erhalten als #define . Damit% code_de% die gleiche Sicherheit erhält, müssen Sie explizite Typumwandlungen in das Makro einfügen, was ein wenig schwieriger zu lesen sein kann.

    Da Constants (und Enums) Variablen sind, können Sie natürlich ihren Umfang mit dem Schlüsselwort #define reduzieren. Dies ist eine gute Vorgehensweise, da solche Variablen den globalen Namespace nicht durcheinander bringen. Obwohl die wahre Quelle von Namenskonflikten in globalen Namespaces in 99% aller Fälle durch schlechte Benennungsrichtlinien oder überhaupt keine Benennungsrichtlinien verursacht wird. Wenn Sie keinem Kodierungsstandard folgen, ist das die wahre Ursache des Problems.

    Im Allgemeinen ist es in Ordnung, Konstanten bei Bedarf global zu machen, es ist jedoch ziemlich harmlos, solange Sie eine vernünftige Benennungsrichtlinie haben (vorzugsweise sollten alle Elemente, die zu einem Codemodul gehören, das gleiche Benennungs-Präfix haben). Dies sollte nicht mit der Praxis verwechselt werden, reguläre Variablen global zu machen, was immer eine sehr schlechte Idee ist.

  • Aufzählungen sollten nur verwendet werden, wenn Sie mehrere konstante Werte haben, die zueinander in Beziehung stehen und Sie einen speziellen Typ erstellen möchten, z. B .:

    %Vor%

    Ein Vorteil der Enumeration ist, dass Sie einen klassischen Trick verwenden können, um die Anzahl der enumerierten Elemente als eine weitere Kompilierzeitkonstante "kostenlos" zu erhalten:

    %Vor%

    Aber es gibt verschiedene Fallen mit Enums, deshalb sollten sie immer mit Vorsicht verwendet werden.

    Der Hauptnachteil von enum ist, dass es nicht typsicher ist, noch ist es "type sane". Zuallererst sind Enumerationskonstanten (wie static im obigen Beispiel) immer vom Typ OK , der signiert ist.

    Der Aufzählungstyp selbst ( int in meinem Beispiel) kann jedoch von jedem Typ sein, der mit char oder int, signiert oder unsigniert kompatibel ist. Raten Sie, es ist implementierungsdefiniert und nicht portabel. Daher sollten Sie enums vermeiden, insbesondere im Rahmen verschiedener Datenbyte-Mappings oder als Teil arithmetischer Operationen.

Lundin 08.01.2014 15:46
quelle
2

Ich stimme mit bblincoe ... + 1

überein

Ich frage mich, ob Sie verstehen, was die Unterschiede in dieser Syntax sind und wie sie sich auf die Implementierung auswirken können. Einige Leute interessieren sich vielleicht nicht für die Implementierung, aber wenn Sie in eingebettet werden, sollten Sie vielleicht.

Wenn bblincoe ROM statt RAM erwähnt.

%Vor%

Das sollte idealerweise .text-Immobilien verbrauchen und diese Immobilien mit dem von Ihnen angegebenen Wert vorinitiieren. Sie werden es nicht ändern, aber es hat einen Platzhalter? Warum brauchen Sie einen Platzhalter für eine Konstante? Denken Sie darüber nach, sicherlich könnten Sie die Binärdatei aus irgendeinem Grund auf die Straße hacken, um etwas ein- oder auszuschalten oder einen Board-spezifischen Tuning-Parameter zu ändern ...

Ohne eine volatile, das bedeutet nicht, dass der Compiler immer diesen .text-Speicherort verwenden muss, kann er diesen Wert optimieren und als Anweisungen direkt oder sogar noch schlechter mathematische Operationen optimieren und etwas Mathe entfernen.

Die Definition und enum konsumieren Speicher nicht, sie sind Konstanten, die der Compiler wählt, wie sie implementiert werden, schließlich sind diese Bits, wenn sie nicht optimiert sind, irgendwo in .text manchmal überall in .text landen, abhängig von der Befehlsmenge seine unmittelbaren arbeiten die spezifische Konstante, etc.

Also define vs enum ist im Grunde wollen Sie alle Werte auswählen oder möchten Sie den Compiler, einige Werte für Sie auswählen, definieren, wenn Sie es Enumeration steuern möchten, wenn Sie möchten, dass der Compiler die Werte auswählen.

Es ist also wirklich keine Best-Practice-Sache überhaupt, es ist ein Fall, zu bestimmen, was Ihr Programm tun muss und die geeignete Programmierlösung für diese Situation zu wählen.

Je nach Compiler und Zielprozessor kann die Wahl von volatile static const int und nicht die ROM-Auslastung beeinflussen. Aber es ist eine sehr spezifische Optimierung und keine allgemeine Antwort (und hat nichts mit Embedded zu tun, sondern mit Compiling im Allgemeinen).

    
old_timer 07.01.2014 20:15
quelle
1

Dan Saks erklärt, warum er die Aufzählungskonstante in diesen Artikeln bevorzugt, Symbolische Konstanten und Enumeration Constants vs Constant Objects . Zusammenfassend vermeiden Sie Makros, da sie die üblichen Bereichsregeln nicht beachten und die symbolischen Namen für symbolische Debugger typischerweise nicht erhalten bleiben. Und bevorzugen Sie Aufzählungskonstanten, da sie nicht für eine Leistungseinbuße anfällig sind, die konstante Objekte beeinflussen können. Es gibt viel mehr Details in den verknüpften Artikeln.

    
kkrambo 07.01.2014 21:14
quelle
1

Eine andere Sache, die berücksichtigt werden muss, ist die Leistung. Auf eine #define-Konstante kann normalerweise schneller zugegriffen werden als auf eine const-Variable (für ganze Zahlen), da const aus ROM (oder RAM) abgerufen werden muss und der #define-Wert normalerweise ein unmittelbares Anweisungsargument ist, so dass er zusammen mit dem abgerufen wird Anweisung (keine zusätzlichen Zyklen).

Wie bei Namenskonflikten verwende ich Präfixe wie MOD_OPT_, wobei MOD der Modulname OPT ist. Das bedeutet, dass das Define eine Kompilierzeit-Option ist, etc. Enthalten Sie nur die #defines in Ihren Header-Dateien, wenn sie Teil sind Verwenden Sie andernfalls eine INC-Datei, wenn sie in mehreren Quelldateien benötigt wird, oder definieren Sie sie in der Quelldatei selbst, wenn sie nur für diese Datei spezifisch sind.

    
fernan 08.01.2014 03:57
quelle

Tags und Links