Moin Forum,
ich stehe gerade aufm Schlauch und brauch mal Hilfe.
Ich möchte ein Array aufbauen, in dem n structs enthalten sind.
1
typedefstructtstConfigParam{
2
uint8_tRegister;
3
uint8_tLength;
4
constchar*Content;
5
}tstConfigParam;
6
7
tstConfigParamMyParamList[]={
8
/*0*/{0x23,4,"abcd"},
9
/*1*/{0x45,6,"efghij"},
10
/*2*/{0x67,2,{0x55,0x66}}/* <<<--- Fehler */
11
};
Bei der Initialisierung der Werte würde ich aber gerne den String aus
Konstanten aufbauen lassen. Also der Compiler soll die 0x55 und 0x66 in
den Flash legen und den Zeiger darauf hier in die Liste eintragen, so
wie es mit den Zeichenfolgen "abcd" auch funktioniert.
Ja, ich könnte auch
1
"\x55\x66"
schreiben, aber das geht leider nicht wenn ich die 0x55 aus einem
1
2
#define Value1 0x55
3
#define Value2 0x66
entnehmen will.
Ich würde aber auch gerne vermeiden noch extra Variablen auzulegen.
Gibts da eine Möglichkeit?
Rangi J. schrieb:> /*2*/ { 0x67, 2, {0x55, 0x66}} /* <<<--- Fehler */
Weil ein const char* erwartet wird, du aber gar kein Array hast, von dem
du einen Zeiger bilden kannst. Das müsstest du schon erst noch bauen:
Ja, das geht natürlich, aber ich wollte ja gerade vermeiden, noch extra
Variablen anzulegen. Ziel ist, das in diesem Array in einigermaßen
übersichtlicher Art eine Liste von Konfigurationsparametern als Tabelle
zusammengestellt werden. Für eine Handvoll Parameter mag das gehen, aber
dann wirds schnell unübersichtlich.
Und bei den Strings "abcd" gehts ja auch. Die müssen auch nicht extra
irgendwo angelegt werden.
Rangi J. schrieb:> /*2*/ { 0x67, 2, {0x55, 0x66}} /* <<<--- Fehler */Jörg W. schrieb:> /*2*/ { 0x67, 2, "\x55" "\x66" },
Früher oder später wird das Programm abstürzen, weil der Content Pointer
an eine Funktion übergeben wird, die auf einen String-Terminator
angewiesen ist. Das sind die typischen Fehler die erst in einem halben
Jahr auftreten.
Also wenn schon denn schon, das eine Byte für den Terminator investieren
:
Oliver S. schrieb:> 0x55 0x66 soll wohl den String "Uf" ergeben, nicht "0x550x66".
sorry, ja. Mit 0x geht es nicht so einfach. Und wenn es formatspezifisch
ist, kann man es eh vergessen.
Johann L. schrieb:> Funktioniert leider nicht in allen Situationen:
Das Compound Literal befindet sich dort im Scope der Funktion. Das ist
ein bisschen wie eine anonyme lokale Variable.
Ab C23 kann man auch im Compound Literal einen Storage Class Specifier
angeben, dann geht das:
Oliver S. schrieb:> Nicht wirklich...>> 0x55 0x66 soll wohl den String "Uf" ergeben, nicht "0x550x66".
Darauf bin ich vorhin auch reingefallen ... aber ich hab's dann noch
gemerkt ("stringify" ...)
Rangi J. schrieb:> Ich würde aber auch gerne vermeiden noch extra Variablen auzulegen.> Gibts da eine Möglichkeit?
Was generell schon mal ungünstig ist.
Zeiger bringen eine gewisse Flexibilität, aber man sollte sich damit
auskennen.
Für die Stack-Navigation z.B. braucht man immer auch eine Schrittbreite.
Ohne geht einfach nicht.
(Verschachtelte Schleifen können auch hilfreich sein.)
Bruno V. schrieb:> Oliver S. schrieb:>> 0x55 0x66 soll wohl den String "Uf" ergeben, nicht "0x550x66".>> sorry, ja. Mit 0x geht es nicht so einfach. Und wenn es formatspezifisch> ist, kann man es eh vergessen.
Wobei sich da schon die nach dem Warum für diese Forderung stellt.
Oliver
Oliver S. schrieb:> Wobei sich da schon die nach dem Warum für diese Forderung stellt.
Was ich meine: Für Dezimal oder Octal geht das define ja mit "\" am
Anfang. Aber wenn jemand 0x55 schreiben will und dafür ein anderes
#define braucht, dann ist meine Lösung immer murx.
Oliver S. schrieb:> Was ich meine: wenn er „Uf“ haben will, das schon zur Compilezeit weiß,> warum definiert er dann nicht „U“ und „f“.
Vielleicht ist die Anwendung die Ausgabe von Sonderzeichen, die nicht im
Quelltextzeichenvorrat enthalten sind oder in diesem eine andere
Codierung haben, als für die Anwendung gebraucht wird.
Ein Beispiel wären Sonderzeichen auf den üblichen HD44780-Displays.
Da kann man im Quelltext nicht "Ä" schreiben, wenn "Ä" auf dem Display
erscheinen soll.
Harald K. schrieb:> Ein Beispiel wären Sonderzeichen auf den üblichen HD44780-Displays.
Da kann man sich aber auch anders helfen, ich habe hier beispielsweise
sowas:
Jörg W. schrieb:> Da kann man sich aber auch anders helfen
Im Prinzip ja, wenn man aber den numerischen Wert auch noch braucht ...
obwohl, den hat man ja:
1
#define BATLEFT_EMPTY "\x01"
2
3
#define BATLEFT_EMPTY_VALUE BATLEFT_EMPTY[0]
Und auf die Weise lässt sich des Threadstarters Problem dann doch lösen:
Bruno V. schrieb:> Wenn es wichtig ist, dass es wirklich ein String ist (also mit> \0-Endung),
Nein, im Gegenteil, in diesem Fall sind in dem "String"
Gerätekonfigurationsdaten enthalten. Da kommen sehr häufig 0x0 vor, ein
Null-terminierter String ist hier nicht sinnvoll.
Oliver S. schrieb:> 0x55 0x66 soll wohl den String "Uf" ergeben
Ja, korrekt, aber es ist halt nur ein Platzhalter für irgendwelche
Daten. "Uf" hat keinen Textbezug.
Daniel A. schrieb:> Ab C23 kann man auch im Compound Literal einen Storage Class Specifier> angeben
Ich verwende die Cube-IDE. Und das nimmt der Compiler so ohne zu
meckern:
Rbx schrieb:>> Ich würde aber auch gerne vermeiden noch extra Variablen auzulegen.>> Gibts da eine Möglichkeit?>> Was generell schon mal ungünstig ist.
Warum? Macht in meinem Fall gar keinen Sinn, aber "generell" ...
Oliver S. schrieb:> Was ich meine: wenn er „Uf“ haben will, das schon zur Compilezeit weiß,> warum definiert er dann nicht „U“ und „f“.
Ja, exakt, da steht z.B. für einen ADXL355-Sensor folgendes:
1
/*! register values */
2
#define ADXL355_DEVID_AD_VALUE (0xAD) //Analog Devices ID
3
#define ADXL355_DEVID_MST_VALUE (0x1D) //Analog Devices MEMS ID
4
#define ADXL355_PARTID_VALUE (0xED) //Device ID 0xED (355 octal)
Für andere Geräte steht da natürlich ganz was anderes. Und in dem
Beispiel muss im Flash zum Schluss eine Zeichenkette "AD 1D ED" stehen.
In der Liste ein Zeiger auf diese 3 Bytes.
Harald K. schrieb:> Und auf die Weise lässt sich des Threadstarters Problem dann doch lösen:
Ja und nein, genau sowas wollte ich ja gerade vermeiden. Damit habe ich
viele Zeilen Code mit Inhalt verteilt über unterschiedliche Sektionen.
Dadurch schleichen sich ganz schnell Copy n Paste Fehler ein. Ja bei
kleinen Listen mag das schleichen, aber wenn da über 50 Zeilen stehen,
blickt da keiner mehr durch. In dem Beispiel von oben mit dem ADXL355
ist das ein Einzeiler:
TLDR:
Mit dem Cast "const uint8_t[]" funktioniert es genau wie gewünscht. Auf
dem Bild sieht man, das die Liste im Flash steht, der Zeiger demzufolge
auch. Und auch die Daten stehen im Flash. Wahlweise kann der Zeiger auch
auf den Ram zeigen, und auch das geht.
Gelöst
Rangi J. schrieb:> a gerade vermeiden. Damit habe ich> viele Zeilen Code mit Inhalt verteilt über unterschiedliche Sektionen.
Nö, Du hättest nur exakt zwei #defines statt einem, und der numerische
Wert stünde auch nur in einem davon.
Das ist der einzige Unterschied.
1
#define ADXL355_DEVID_AD_STR "\xAD" //Analog Devices ID
Das funktioniert dann auch mit älteren C-Dialekten (es gibt immer noch
freilaufende Systeme, die kein C99 verstehen).
Aber gut, der andere Ansatz ist natürlich auch einer, wenn man sich
seines Compilers sicher ist.
Jörg W. schrieb:> Harald K. schrieb:>> Ein Beispiel wären Sonderzeichen auf den üblichen HD44780-Displays.>> Da kann man sich aber auch anders helfen, ich habe hier beispielsweise
[... Zeichen im CG-RAM ...]
Jörg W. schrieb:> oder> #define MY "\xe4"> const char units_lcd[] = " m" MY "npf";> const char units_iso[] = " mµnpf";
Für die Batterie-Icons ist das ja ok. Aber für Text? Muss das sein?
Macht man das so? Wenn man das "ganz unten" im Treiber macht (ja, zur
Laufzeit), bleibt der Quelltext doch ein wenig lesbarer. Soviel Zeit und
Flash muss sein ;)
Rangi J. schrieb:> Für andere Geräte steht da natürlich ganz was anderes. Und in dem> Beispiel muss im Flash zum Schluss eine Zeichenkette "AD 1D ED" stehen.> In der Liste ein Zeiger auf diese 3 Bytes.
Ganz generell: Wenn es immer 3 Byte sind, kann sich das ruhig im
Datentyp widerspiegeln. Also z.B. ein 3-Byte-Array.
Wenn Du 50 verschiedene dieser Structs hast, ggf. auf mehrere Files
verteilt, dann macht es durchaus Sinn, diese Structs in den Files
zusammenzufassen und nur den ptr zu veröffentlichen. Es ist sogar
möglich, diese Liste vom Linker erstellen zu lassen. Das erfordert aber
einige Erfahrung mit Linker-Sections.
Kurz gesagt: Es gibt 2 Möglichkeiten.
A) Du brauchst diese #defines (z.B. ADXL355_DEVID_AD_STR) im ganzen
Code, dann stehen die in einer Header und Du packst sie irgendwo
zusammen. Aber nicht zentral, sondern im jeweiligen Modul.
B) Du greifst auf diese Werte eher über Strukturen zu, dann brauchst Du
die #defines gar nicht bzw. wenn, nur lokal.
Rangi J. schrieb:> Daniel A. schrieb:>> Ab C23 kann man auch im Compound Literal einen Storage Class Specifier>> angeben> Ich verwende die Cube-IDE. Und das nimmt der Compiler so ohne zu> meckern:
Das liegt daran, dass da der Scope und storage duration anders ist, als
im ursprünglichen Beispiel.
Dort hatte man im localen Scope eine statische Variable (also mit static
storage duration), die initialisiert wurde, aber das Compound literal
hatte kein static, war damit weil lokal im Funktionsscope mit automatic
storage duration. Das geht nicht, denn die statische Variable wird beim
Programmstart initialisiert, aber das comound literal existiert es erst
später, wenn die Funktion aufgerufen wird.
Ab C23 kann man auch bei einem Compound literal static dazuschreiben,
also static storage duration geben, und das Problem entfällt.
In deinem Beispiel ist gar kein static. Ausserhalb einer Funktion haben
dann beide, die Variable und das Compound Literal, static storage
duration. Oder wenn es in einer Funktion wäre, hätten beide automatic
storage duration. Das ist auch alles kein Problem.
Rangi J. schrieb:>> Was generell schon mal ungünstig ist.> Warum? Macht in meinem Fall gar keinen Sinn, aber "generell" ...
Denkblockade, Premature Optimization. Sieht man doch hier auch an der
Diskussion. Mir sind die im Hintergrund lauernden Fallstricke
einigermaßen bewusst - dir auch?
Z.B.:
1. Code-Übersetzung
2. Variablensicherheit
3. Speicher bzw. Stacknavigation.
Die im Hintergrund lauernden Fallstricke sind nicht trivial - weswegen
erstmal ein Entwurf, der einigermaßen funktioniert hilfreich wäre.
So könnte man die Verbesserung dann Schrittweise und genauer machen.
Rangi J. schrieb:> const char * Content;
Das sieht wie ein C-String aus!
Ist aber keiner, wie sich später dann zeigt.
Dann ergibt sich, eine paar Postings weiter, dass es Zahlen sind und
keine Charakter.
Da es Implementation Defined ist, ob char unsigned oder signed ist, wird
es später dann evtl Probleme geben beim rechnen (signed overflow wird
UB) oder Überraschungen beim vergleichen.
const unsigned char * Content;
Oder gleich
const uint8_t * Content;
Wenn es denn uint8_t auf dem Kesselchen gibt.
Das gleiche gilt natürlich auch für Arrays.
Mein Rat:
Wenn es kein C-String ist oder sein soll, dann auch bitte die
Verwechselungsgefahr ausschließen.