Gibt es eigentlich eine Möglichkeit von einer Makrodefinition einen Wert
zurück zu geben? Hier mal ein Beispiel, was ich meine.
1
myfoo.c
2
3
unsigned*myFoo(unsigned*p_array,unsignedsize)
4
{
5
// ... tue was
6
returnp_array;
7
}
8
9
myfoo.h
10
11
#define CreateMyArray (name, size) \
12
static unsigned name [size]; \
13
myFoo (name, size)
14
15
main.c
16
17
#include"myfoo.h"
18
unsigned*p_myarray=NULL;
19
20
main(){
21
p_myarray=CreateMyArray(myArray,256);
22
}
D.h. den Zeiger den myFoo zurück gibt, möchte ich gern an p_myarray
zuweisen.
Ich habe so etwas schon mal gesehen, wo es funktioniert. Das ist aber
nur ein sehr einfaches Makro.
Ste N. schrieb:> Gibt es eigentlich eine Möglichkeit von einer Makrodefinition einen Wert> zurück zu geben?
Nein, denn ein #define ist nur eine Textersetzung vor der Übersetzung.
1
#define CreateMyArray (name, size) \
2
static unsigned name [size]; \
3
myFoo (name, size)
4
5
p_myarray=CreateMyArray(myArray,256);
Das würde hier also folgendes ergeben:
1
p_myarray=staticunsignedmyArray[256];
2
myFoo(myArray,256)
Wenn DU etwas zurück haben willst, brauchst Du schon eine Funktion.
wer sagt, das ein macro nicht auch eine funktion definieren darf?
Der funktionsnaume darf dann halt nicht der macro name sein, ... das ist
halt zu beachten. Und du must an einer stelle im code dein macro einmal
"Aufrufen" damit die notwendige funktion auch im code auftaucht.
und wenn das ganze dann noch parameterisierte macros sind fängt der
spass erst richtig an, ... Wobei genau dann macht man das eigentlich
damit man sich copy and past spart.
BLE schrieb:> wer sagt, das ein macro nicht auch eine funktion definieren darf?
das geht, aber wie soll man dann einen Array ohne malloc-Funktion
erzeugen
Pete K. schrieb:> Warum deklariert Du CreateMyArray nicht als Funktion?
Weil das erzeugte statische Array nur so lange existiert, bis die
Funktion zurückkehrt, aber offenbar auch danach noch benötigt wird.
Was für ein Geschwurbel. Da blickt dann doch gar keiner mehr durch.
Spätestens nach 3 Monaten fliegt einem das um die Ohren.
Anfänger sollten keine #define verwenden dürfen.
Vergiss #define. Mach ein richtige Funktion. Und lass den Compiler das
optimieren.
Rolf M. schrieb:> Weil das erzeugte statische Array nur so lange existiert, bis die> Funktion zurückkehrt, aber offenbar auch danach noch benötigt wird.
Also, wenn so ein Array nicht auf dem Heap erzeugt wird und auch nicht
als lokale Variable auf dem Stack, dann bleibt nur noch der gewöhnliche
RAM übrig, also eine globale Variable, die ihren Platz auf alle
Ewigkeiten belegt. Wenn also dynamisch, dann ist eine Heapverwaltung
fällig.
OK, man könnte das Array vorher und/oder hinterher auch für etwas
anderes benutzen, aber man setzt sich damit selber in die Nesseln.
W.S.
PittyJ schrieb:> Anfänger sollten keine #define verwenden dürfen.
Wieder mal so eine kategorische Aussage, die eigentlich unhaltbar ist.
Wer beurteilt denn, wer irgendwann einmal den Titel eines Nichtanfängers
verliehen bekommt und ab da besagtes #define verwenden darf?
Abgesehen davon ist die Programmiersprache C quasi kastriert, wenn man
sowas wie #define nicht verwenden darf. In C hat man keine solchen Dinge
wie z.B. den 'const' Abschnitt in Pascal, wo man Konstanten definieren
kann. Der Preprozessor ist ein wesentlicher Bestandteil des Ganzen in C.
Aber eines stimmt: Man kann sich mit dem Definieren von Macros auch
zurückhalten. Auch als Anfänger.
W.S.
Rolf M. schrieb:> Weil das erzeugte statische Array nur so lange existiert, bis die> Funktion zurückkehrt, aber offenbar auch danach noch benötigt wird.
Nein!
Der Witz an statischen Variablen ist doch, daß sie ab Programmstart
durchgehend existieren.
Weil das Feld in diesem Fall lokal definiert ist (wenn auch statisch),
ist es über den Variablennamen nur innerhalb der Funktion erreichbar.
Aber über Zeiger oder sonstwie auch außerhalb der Funktion.
Nur automatische Variablen liegen auf dem Stack und werden beim
Verlassen eines Blocks bzw. der Funktion aufgelöst.
W.S. schrieb:> In C hat man keine solchen Dinge> wie z.B. den 'const' Abschnitt in Pascal, wo man Konstanten definieren> kann.
Aha. Bring' dich mal auf Stand. Seit 21 Jahren kann man Konstanten
definieren.
Rolf M. schrieb:> Weil das erzeugte statische Array nur so lange existiert, bis die> Funktion zurückkehrt, aber offenbar auch danach noch benötigt wird.
Wenn man denn unbedingt will, kann man auch das. Arrays können zwar kein
Funktionsergebnis sein, structs können das aber durchaus. Und arrays
können auch Bestandteil von structs sein.
Ob das allerdings empfehlenswert ist, steht auf einem anderen Blatt.
Erst mal Danke für den Input. Das mit dem Array ist ja nur ein Beispiel.
Was ich eigentlich will, das ein Struct mit einem variablen Datenfeld
erstellt werden kann, aber eben ohne malloc() und ein Teil der Daten,
wie z.B. der Name, im ROM liegt. Der Name wird im Makro automatisch aus
den Parametern generiert. Und genau das geht eben mit einer Funktion
nicht, oder doch?
Alle Structs und dessen Größe stehen schon bei Programmerstellung fest,
sollen aber trotzdem variabel bleiben. Das hat für mich den Vorteil, das
eben schon der Compiler meckert wenn der Speicher verbraucht ist. Das
funktioniert ja soweit auch hervorragend, bis auf das Zurückgeben des
Zeigers. Bis jetzt mache ich das über einen Funktionsparameter, aber das
ist eben nicht ganz so elegant. Wenn ich den Zeiger nicht unbedingt
benötige, kann ich ihn nicht einfach weglassen.
Wie man an dem obigen Beispiel mit dem ATOMIC_READ sieht, scheint es ja
auch irgendwie mit Makros zu gehen.
Ste N. schrieb:> Wie man an dem obigen Beispiel mit dem ATOMIC_READ sieht, scheint es ja> auch irgendwie mit Makros zu gehen.
Die benutzen auch den Komma-Operator.
Bei Variablendefinitionen hat das Komma aber eine andere Bedeutung.
Ste N. schrieb:> Gibt es eigentlich eine Möglichkeit von einer Makrodefinition einen Wert> zurück zu geben? Hier mal ein Beispiel, was ich meine.
Beim GCC sind solche Dinge mit Statement Expressions möglich. Das ist
aber eine GCC-spezifische Spracherweiterung:
https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
Ich persönlich bin kein Freund solcher Spracherweiterungen. Ordentlicher
C-Code sollte IMHO mit
1
gcc -std=c18 -pedantic-errors
fehlerfrei kompiliert werden.
> Ich habe so etwas schon mal gesehen, wo es funktioniert. Das ist aber> nur ein sehr einfaches Makro.>> #define OS_ATOMIC_READ(expr) (__builtin_disable_interrupts(), _OS_Temp> = (expr), __builtin_enable_interrupts(), _OS_Temp)
In diesem Fall besteht der Makrorumpf nur aus einem einzelnen Ausdruck,
der natürlich – wie jeder Ausdruck, der nicht vom Typ void ist – ein
Ergebnis hat, das von einem aufrufenden Programmteil bspw. einer
Variable zugewiesen werden kann.
In deinem Beispiel enthält der Makrorumpf aber zusätzlich noch eine
Deklaration, weswegen dein Vorhaben nur mit der o.g. Spracherweiterung
realisierbar ist.
Markus F. schrieb:> W.S. schrieb:>> In C hat man keine solchen Dinge>> wie z.B. den 'const' Abschnitt in Pascal, wo man Konstanten definieren>> kann.>> Aha. Bring' dich mal auf Stand. Seit 21 Jahren kann man Konstanten> definieren.
Benamste Kompilezeitkonstanten sind in C – anders als in C++ –
nur mit #define möglich. Mit dem Schlüsselwort const werden
Read-Only-Variablen deklariert, was aber nicht dasselbe ist.
Klaus W. schrieb:> Rolf M. schrieb:>> Weil das erzeugte statische Array nur so lange existiert, bis die>> Funktion zurückkehrt, aber offenbar auch danach noch benötigt wird.>> Nein!>> Der Witz an statischen Variablen ist doch, daß sie ab Programmstart> durchgehend existieren.
Da hast du natürlich recht. Das gilt selbstverständlich nur für
nicht-statische lokale Variablen. Mea culpa.
Ste N. schrieb:> Alle Structs und dessen Größe stehen schon bei Programmerstellung fest,> sollen aber trotzdem variabel bleiben.
Das verstehe ich nicht ganz. Nicht nur die Größe, sondern alle Elemente
mit deren jeweiligen Typ und Position innerhalb der Struct sind zur
Compilezeit bekannt. Man spricht sie über den bei der Definition des
Typs bereits vergebenen Namen an. Was willst du da variabel machen?
> Das hat für mich den Vorteil, das eben schon der Compiler meckert wenn> der Speicher verbraucht ist. Das funktioniert ja soweit auch> hervorragend, bis auf das Zurückgeben des Zeigers. Bis jetzt mache ich> das über einen Funktionsparameter, aber das ist eben nicht ganz so> elegant. Wenn ich den Zeiger nicht unbedingt benötige, kann ich ihn nicht >
einfach weglassen.
Aber ohne den Zeiger kannst du doch gar nicht drauf zugreifen.
Ste N. schrieb:> Was ich eigentlich will, das ein Struct mit einem variablen Datenfeld> erstellt werden kann,
Das nennt man "dynamische Speicherverwaltung" oder Heap, was du da
nutzen möchtest.
Ste N. schrieb:> aber eben ohne malloc()
Ach...
Wie möchte es der Herr denn: Statisch oder dynamisch?
Ste N. schrieb:> Und genau das geht eben mit einer Funktion> nicht, oder doch?
Funktionen legen ihre lokalen Variablen auf dem Stack an.
Aber dann darfst du die Adresse nicht per Return weiterreichen.
Ste N. schrieb:> Alle Structs und dessen Größe stehen schon bei Programmerstellung fest,> sollen aber trotzdem variabel bleiben.
Also eine konstante Größe, welche variabel ist?
Ich sehe da einen Widerspruch!
Du nicht?
So funktioniert es und wird Fehlerfrei und ohne Warnungen übersetzt!
Selbst mit der Option strict-ansi wird es erfolgreich übersetzt,
allerdings mit reichlich Warnungen, darunter auch Kommentare mit //.
Aber damit kann ich gut leben, strict-ansi nutze ich nicht und es ist ja
nur für mich.
Also Danke für den Input Yalu, wieder was gelernt!
Markus F. schrieb:> Aha. Bring' dich mal auf Stand. Seit 21 Jahren kann man Konstanten> definieren.
Nein, weil "const" in C nicht "Konstante" bedeutet. Es bedeutet, daß
programmatisch nicht schreibend zugegriffen wird, das ist alles.
Deswegen gibt es ja auch die Kombo "volatile const".
PittyJ schrieb:> Anfänger sollten keine #define verwenden dürfen.
Bisschen krass ausgedrückt. Aber ich kann durchaus empfehlen,
funktionsartige Makros durch Inline-Funktionen zu ersetzen, sofern das
möglich ist.
EAF schrieb:> Ste N. schrieb:>> Was ich eigentlich will, das ein Struct mit einem variablen Datenfeld>> erstellt werden kann,> Das nennt man "dynamische Speicherverwaltung" oder Heap, was du da> nutzen möchtest.
ich glaube du hast mein Anliegen nicht verstanden...
> Ste N. schrieb:>> aber eben ohne malloc()> Ach...> Wie möchte es der Herr denn: Statisch oder dynamisch?
statisch
> Ste N. schrieb:>> Und genau das geht eben mit einer Funktion>> nicht, oder doch?> Funktionen legen ihre lokalen Variablen auf dem Stack an.> Aber dann darfst du die Adresse nicht per Return weiterreichen.
deswegen ja static
> Ste N. schrieb:>> Alle Structs und dessen Größe stehen schon bei Programmerstellung fest,>> sollen aber trotzdem variabel bleiben.> Also eine konstante Größe, welche variabel ist?> Ich sehe da einen Widerspruch!> Du nicht?
Nö! Variabel sollen sie ja zur Compilezeit sein, nicht zur Laufzeit.
Am Ende sieht mein Makro so aus.
Dazu muß ich noch sagen, das in _CreateStruct() noch viel mehr passiert.
Unter anderem wird aus den Structs eine verkette Liste erstellt, welche
eben Arrays unterschiedlicher Länge beinhalten kann.
Wenn ich jetzt jeden Eintrag von Hand erstellen sollte, fände ich das
ziemlich umständlich.
Ste N. schrieb:> Also ich finde es genial einfach!
Für persönlichen Gebrauch sicherlich cool. In einem professionellen
Code-Review? Keine Chance für den ganzen Ansatz.
Kannst du die vielen erzeugten Namen nutzen? Im Editor oder Debugger?
Fall nicht, lass sie weg. Nach außen ist ja nur der string-inhalt
sichtbar.
Das makro wurde deutlich klarer und kleiner.
Ste N. schrieb:>> Ste N. schrieb:>>> Alle Structs und dessen Größe stehen schon bei Programmerstellung fest,>>> sollen aber trotzdem variabel bleiben.>> Also eine konstante Größe, welche variabel ist?>> Ich sehe da einen Widerspruch!>> Du nicht?>> Nö! Variabel sollen sie ja zur Compilezeit sein, nicht zur Laufzeit.
Das sind sie doch einfach durch Änderung des struct-Typs. Ich sehe aber
nicht, wie dir das Makro dabei hilft.
>> Das sollte identisch sein.>> O.T.: mit einem dritten Parameter und Zuweisung in der letzten Zeile> wäre es auch ohne Erweiterung nutzbar.
Kannst Du das bitte noch mal näher erläutern?
Das mit dem String ist wirklich ne sehr gute Idee, manchmal sieht man
den Wald vor lauten Bäumen nicht... aber der Rest geht so leider nicht.
Das Makro kann ja dann nur 1x aufrufen werden und dafür bräuchte ich die
ganzen Klimmzüge tatsächlich nicht. Beim nächsten Aufruf gibt es s und a
schon und der Compiler meckert, und zwar zu recht.
So kann ich in meiner main() einfach nur
p_myA = CreateMyStruct (a, 256);
p_myB = CreateMyStruct (b, 128);
p_myC = CreateMyStruct (c, 64);
p_myD = CreateMyStruct (d, 256);
aufrufen und alles weitere wird automatisch abgeleitet.
A. S. schrieb:> Kannst du die vielen erzeugten Namen nutzen? Im Editor oder Debugger?> Fall nicht, lass sie weg. Nach außen ist ja nur der string-inhalt> sichtbar.
Den Namen benötige ich wirklich nur zum debuggen. Könnte man mit einem
weiteren #define bei Bedarf auch ausblenden ;) Aber dann wirds am Ende
wirklich unübersichtlich...
Nop schrieb:> Für persönlichen Gebrauch sicherlich cool. In einem professionellen> Code-Review? Keine Chance für den ganzen Ansatz.
Da gebe ich dir natürlich vollkommen recht! Wie gesagt es ist eine
private Spielerei. Das Makro wird wirklich nur im Initialisierungsteil
der main() aufgerufen. Das ich das nicht in eine Schleife oder if/then
Abfrage setzen kann, ist klar.
Rolf M. schrieb:>> Nö! Variabel sollen sie ja zur Compilezeit sein, nicht zur Laufzeit.>> Das sind sie doch einfach durch Änderung des struct-Typs. Ich sehe aber> nicht, wie dir das Makro dabei hilft.
Das hilft mir insofern, das es die Deklaration extrem unkompliziert
macht. Zumindest für mich, siehe oben.
Ste N. schrieb:> Beim nächsten Aufruf gibt es s und a schon und der Compiler meckert, und> zwar zu recht.
Aber doch nur statc in einem Block in { }.
Da kannst Du beliebig viele Blöcke von haben.
A. S. schrieb:> Ste N. schrieb:>> Beim nächsten Aufruf gibt es s und a schon und der Compiler meckert, und>> zwar zu recht.>> Aber doch nur statc in einem Block in { }.>> Da kannst Du beliebig viele Blöcke von haben.
Funktioniert tatsächlich! Das wird ja immer besser und macht die Sache
extrem übersichtlich. Das die Klammern einen Block abschotten, hätte ich
jetzt nicht etwartet und deswegen auch nie probiert.
Danke!
Ich ahne langsam, was du vorhast.
Ich glaube dir auch, daß es in C mit dem Praeprozessor machbar ist, und
sicher eine nette Spielerei.
Aber nüchtern betrachtet, geht sowas in C++ mit templates sicher
wesentlich eleganter und möglicherweise besser.
Nachdem der gcc ja auch C++ kann, wäre es zumindest eine Überlegung
wert?
he he he, ...
Hat sich einer mal mit Zephyr angeschaut?
Da werden OS Strukturen PIN Configuration ... genau mit solchen
macros angelegt und teilweise wird dan im macro wider auf andere macros
zugegriffen,
Auch im Linuz kernel sind mir solche macros schon begegnet, war glaube
ich der part power rails / Frequanzy scalling
BLE schrieb:> he he he, ...> Hat sich einer mal mit Zephyr angeschaut?>> Da werden OS Strukturen PIN Configuration ... genau mit solchen> macros angelegt und teilweise wird dan im macro wider auf andere macros> zugegriffen,>> Auch im Linuz kernel sind mir solche macros schon begegnet, war glaube> ich der part power rails / Frequanzy scalling
Das war schon zu Zeit der pdp11 bekannt ;-)
@ Ste N.
Du kannst auch damit den Datentyp deiner variable ändern ...
Klaus W. schrieb:> Ich ahne langsam, was du vorhast.> Ich glaube dir auch, daß es in C mit dem Praeprozessor machbar ist, und> sicher eine nette Spielerei.>> Aber nüchtern betrachtet, geht sowas in C++ mit templates sicher> wesentlich eleganter und möglicherweise besser.> Nachdem der gcc ja auch C++ kann, wäre es zumindest eine Überlegung> wert?
In C++ gibt es dafür std::array
Wozu das Ganze?
Ich bin da eher für klare Verhältnisse, wozu auch der Verzicht auf
umfangreichere Macros gehört. System KISS sozusagen. Sowas ist bei
gründlicher Denk-Vorarbeit sowohl einfacher zu schreiben als auch
leichter zu warten. Man muß nicht jede Pirouette drehen, die in der
Sprachdefinition nicht ausdrücklich verboten ist.
W.S.
Ste N. schrieb:> Das wird ja immer besser und macht die Sache> extrem übersichtlich.
Deine Aufgabe ist üblich: Eine bekannte Struktur mit variabler Größe
einiger Felder durch Create erzeugen.
Es ist jedoch nicht notwendig, die Struktur wirklich anzulegen. Man
berechnet einmal den benötigten Platz und reserviert nur einen
"byte-Puffer". Dann ist kein Makro für Create:
Beispiel:
W.S. schrieb:> Wozu das Ganze?> Ich bin da eher für klare Verhältnisse, wozu auch der Verzicht auf> umfangreichere Macros gehört. ... Sowas ist bei> gründlicher Denk-Vorarbeit sowohl einfacher zu schreiben als auch> leichter zu warten. ...
Das könnte bei Macros wie sie im Linux Kernel oder Zephyr verwendet
werden genau das gegentail bewirken.
Die macros dort ersparen den entwicklern jede menge fleis arbeit.
Ja das macro ist sehr komplex, teils nicht leicht zu versthehen.
Erspart dir aber jede menge copy past. Beim anlgen und gerade beim
anpassen.
Es ist halt z.B. abzuwägen was besser ist, ein complexes Macro oder jede
menge copy past code.
BLE schrieb:> Es ist halt z.B. abzuwägen was besser ist, ein complexes Macro oder jede> menge copy past code.
Bei W.S. ist die Antwort klar, er mag Kopierpastencode.
Auf Cortex-M mit mehreren uarts hat er für jeden Uart ne eigene Funktion
mit verdoppeltem Code:
uart1_CharOut()
uart2_CharOut()
uart3_CharOut()
etc.
Bei Programmierfragen darf man nicht W.S. fragen.
Er kann ja nichtmal C.
A. S. schrieb:> static unsigned char buf_A[STRUCT_SIZE(50)];
Das kann je nach Prozessor abstürzen oder auch nur unerwartete
Ergebnisse liefern. Der zugrundeliegende Buffer braucht das maximale
Alignment, das die zugrundeliegende Hardware kann (z.B. 4 Byte auf
32bit-HW), und das muß man manuell als attribute zufügen.
Markus F. schrieb:> Aha. Bring' dich mal auf Stand. Seit 21 Jahren kann man Konstanten> definieren.
Nur liegen Konstanten beim AVR blöderweise im RAM. Sogar die avr_libc
drängt zur Verwendung von #define für Konstante und konfigurierbare
Parameter. Beispiele:
#define F_CPU 8000000
#define BAUD 9600
Wobei man das alternativ (als gcc Parameter -D) ins Makefile schreiben
kann. Gerade bei F_CPU ist das der üblichere Weg.
Ich habe in drei Projekten umfangreiche Makros ausprobiert, die ganze
Funktionen deklarieren. Und ich bin seit dem der Meinung, dass dies eine
schlechte Idee war. Sie verbergen unnötig Details und führen im
Fehlerfall zu hässlichen (oft unverständlichen) Fehlermeldungen des
Compilers. Mehrfach verschachtelte Makros sind zudem extrem schwer
verständlich.
BLE schrieb:> Es ist halt z.B. abzuwägen was besser ist, ein complexes Macro oder jede> menge copy past code.
Bleibe mal auf dem Teppich. Die Allermeisten, die hier fragen oder
antworten, schreiben nicht an einem Linux-Kernel, sondern bauen eine
Firmware für ihren Controller und das ist eine andere Liga.
Und wer in seiner Firmware für seinen kleinen Controller solche Dinge
wie komplexe Macros benötigt, oder zu benötigen glaubt, der muß dann
auch die Folgen davon tragen. Ich halte es da anders, nämlich die
verschiedenen Ebenen in der Firmware voneinander getrennt zu halten, so
daß man bei Plattformwechsel (z.B. von 78K5 auf FR30 und dann auf
ARM7TDMI und von dort irgend etwas anders) nicht die eigentliche Logik
anpassen muß, sondern lediglich zur jeweiligen Hardware passende
Lowlevel-Treiber schreiben muß.
Das bleibt einem auch bei komplexen Macros nicht erspart, denn kein
Macro kann die Spezifika einer Plattform vor-ahnen, worauf es noch nie
portiert worden ist (weil diese Plattform erst nächstes Jahr erfunden
werden wird)
W.S.
Nop schrieb:> Das kann je nach Prozessor abstürzen oder auch nur unerwartete> Ergebnisse liefern. Der zugrundeliegende Buffer braucht das maximale> Alignment, das die zugrundeliegende Hardware kann (z.B. 4 Byte auf> 32bit-HW), und das muß man manuell als attribute zufügen.
Ja. Stimmt. Gehört dazu.