Forum: Mikrocontroller und Digitale Elektronik C: Kann man mittels #define einen Wert zurück geben?


von Ste N. (steno)


Lesenswert?

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, unsigned size)
4
{
5
  // ... tue was
6
  return p_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.
1
#define OS_ATOMIC_READ(expr)  (__builtin_disable_interrupts(), _OS_Temp = (expr), __builtin_enable_interrupts(), _OS_Temp)

Gibt es da einen Trick?

Ich weiß, da gibt es andere Möglichkeiten, aber malloc() möchte ich für 
ein einfaches Programm gerne vermeiden.

Gruß, Steffen

von Pete K. (pete77)


Lesenswert?

Warum deklariert Du CreateMyArray nicht als Funktion?

von Brxxxxx (Gast)


Lesenswert?

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 = static unsigned myArray[256];
2
myFoo(myArray, 256)

Wenn DU etwas zurück haben willst, brauchst Du schon eine Funktion.

von BLE (Gast)


Lesenswert?

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.

von und dann? (Gast)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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.

von PittyJ (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Klaus W. (mfgkw)


Lesenswert?

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.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

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.

von Ste N. (steno)


Lesenswert?

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.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

: Bearbeitet durch Moderator
von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von EAF (Gast)


Lesenswert?

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?

von Ste N. (steno)


Lesenswert?

Yalu X. schrieb:
> 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

Ist schon Krass! 4 Klammern an der richtigen Position haben das 
"Problem" gelöst.
1
#define CreateMyArray (name, size)       \
2
  ({static unsigned name [size];         \
3
    myFoo (name, size);})

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!

von Nop (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Ste N. (steno)


Lesenswert?

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.
1
#define MERGE_(a,b)   a##b
2
#define STRINGIFY_(a) #a
3
4
#define CreateMyStruct(mystruct,size)                            \
5
  ({const char MERGE_(mystruct, _name)[] = STRINGIFY_(mystruct); \
6
    static myStruct_t MERGE_(mystruct, _struct);                 \
7
    static unsigned MERGE_(mystruct, _array)[size];              \
8
    _CreateStruct ( &MERGE_(mystruct, _struct),                  \
9
                    MERGE_(mystruct, _name),                     \
10
                    MERGE_(mystruct, _array),                    \
11
                    size);})

Also ich finde es genial einfach!

von EAF (Gast)


Lesenswert?

Ste N. schrieb:
> Am Ende sieht mein Makro so aus.
OK....

von Ste N. (steno)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

1
#define CreateMyStruct(name,size)    \
2
  ({static myStruct_t s;             \
3
    static unsigned   a[size];       \
4
    _CreateStruct(&s, #name, a, size})

Das sollte identisch sein.

O.T.: mit einem dritten Parameter und Zuweisung in der letzten Zeile 
wäre es auch ohne Erweiterung nutzbar.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Ste N. (steno)


Lesenswert?

A. S. schrieb:
>
1
> #define CreateMyStruct(name,size)    \
2
>   ({static myStruct_t s;             \
3
>     static unsigned   a[size];       \
4
>     _CreateStruct(&s, #name, a, size})
5
> 
6
>
>
> 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.

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

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.

von Ste N. (steno)


Lesenswert?

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!

von Klaus W. (mfgkw)


Lesenswert?

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?

von Hans-Georg L. (h-g-l)


Angehängte Dateien:

Lesenswert?

Das wäre mal wieder eine Anwendung für den sogenannten X-macro-trick der 
nicht so bekannt ist.
1
// Liste
2
#define MY_STRUCTURES \
3
 MY_STRUCT(s1, 42 )  \
4
 MY_STRUCT(s2, 174 )  \
5
 MY_STRUCT(s3, 22 )  \
6
7
// Expansion 
8
#define MY_STRUCT(name, size)  \
9
typedef struct {  \
10
int var[size]; \
11
} name; 
12
MY_STRUCTURES 
13
#undef MY_STRUCT 
14
15
Daraus macht der Preprozessor dann ::
16
17
18
typedef struct { int var[42]; } s1;
19
typedef struct { int var[174]; } s2;
20
typedef struct { int var[22]; } s3;

von BLE (Gast)


Lesenswert?

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

von Hans-Georg L. (h-g-l)


Lesenswert?

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 ...
1
// Liste
2
#define MY_STRUCTURES \
3
 MY_STRUCT(s1, 42 , int)  \
4
 MY_STRUCT(s2, 174, char )  \
5
 MY_STRUCT(s3, 22, float )  \
6
7
// Expansion 
8
#define MY_STRUCT(name, size, vtype)  \
9
typedef struct {  \
10
vtype var[size]; \
11
} name; 
12
MY_STRUCTURES 
13
#undef MY_STRUCT

ergibt:

typedef struct { int var[42]; } s1;
typedef struct { char var[174]; } s2;
typedef struct { float var[22]; } s3;

von Hans-Georg L. (h-g-l)


Lesenswert?

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

von W.S. (Gast)


Lesenswert?

EAF schrieb:
> Aber dann darfst du die Adresse nicht per Return weiterreichen.

Er darf schon, muß sich dann aber mit den Folgen zufrieden geben..

W.S.

von W.S. (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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:
1
#define STRUCT_SIZE(n) (sizeof(myStruct_t) + sizeof(unsigned)*n)
2
3
static unsigned char buf_A[STRUCT_SIZE(50)];
4
5
    p_myA = CreateMyStruct(buf_A, sizeof(buf_A), "myA" );

Aufruf nur mit Speicher, Größe und Namen. CreateMyStruct erstellt dann 
aus Puffer und Größe per cast die Structur und rechnet size aus. Etwa 
so:
1
my_t * CreateStruct(unsigned char *b, size_t n, char *name)
2
{
3
const int   s = ; // Größe der Struktur in Bytes
4
myStruct_t *p = (myStruct_t *) b;   // Zeiger auf die Struktur
5
unsigned   *a = b + s;              // Zeiger auf das Array
6
size_t   size = n - s;              // Arraygröße in Byte   
7
    
8
     /* und aufruf Deiner Funktion wie bisher */
9
     return _CreateStruct(p, name, a, size/(sizeof unsigned)  );
10
}

von BLE (Gast)


Lesenswert?

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.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

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.