Forum: Compiler & IDEs C: Zeiger an Funktion übergeben und Funktion über Adresse aufrufen


von Nicky C. (nicky)


Lesenswert?

Hallo Zusammen,

ich bin gerade nur noch am Probieren, da jeder Versuch irgendwie fehl 
schlägt und hoffe mir kann jemand weiter helfen...
wie man eine Adresse einer Funktion an ein Modul (Modul, da C und damit 
kein Objekt) übergibt, damit dieses Modul,
eine Statusmeldung (z.B. fertig) an eine beliebige Funktion zurück gibt, 
die in Form der Adresse der Funktion zuvor mit übergeben wurde.

Die Problemstellung:
Ich möchte einen externen Speicher über SPI ansprechen, sowohl lesend 
als auch schreibend.
Da ich mich nicht weiter um den eigentlichen Prozess kümmern mag (es gib 
so viel anderes zu tun),
fülle ich in einer aufrufenden Funktion ein Array mit Daten,
1
 
2
// Structur des DatenArrays
3
struct  
4
{
5
  uint8_t    Modul;
6
  uint8_t    BytesInArray;
7
  uint16_t  *ResponseFunction;
8
  uint8_t    DataArray[128];
9
} MEMORY;

die SPI aufrufendende Funktion und das Array, in dem sich u.a. eine 
Modulkennung, die Speicheradresse der Antwort-Funktion,
Anzahl der zu sendenden/lesenden Bytes und natürlich die Daten selber 
befinden.
Die Adresse des Arrays übergebe ich an die SPI-Funktion(en).
1
void MemRead(void)
2
{
3
  //...  
4
  MEMORY.Modul        =  Modul_MEM;
5
  MEMORY.BytesInArray  =  14;
6
  MEMORY.ResponseFunction  =  &MEM_READ_RESPONCE_FCT;
7
  
8
  MEMORY.DataArray[0] = 0;  
9
  
10
  //....
11
  
12
  if (SPI_Get_Status() == FREE)
13
  {
14
    SPI_READ(&MEMORY);
15
  }
16
}
17
18
void MEM_READ_RESPONCE_FCT(uint8_t status)
19
{
20
// Do some with status
21
}


Damit kann das SPI-Modul selbstständig arbeiten.

Für einen Schreibbefehl, kann ich mir innerhalb des SPI Moduls ein 
Statusbit setzen, was den Zugriff anderer Funktionen
auf SPI sperrt, solange vom vorherigen Auftrag noch nicht alle Daten 
gesendet wurden.

Für einen Lesebefehl sieht es anders aus, ich kann zwar das SPI Modul 
als Frei/Besetzt melden, aber ich werde nicht erfahren,
wann z.B. 20 Byte gelesen wurden und diese im Array (dessen Adresse beim 
SPI-Modul Aufruf mit übergeben wurde) bereit liegen.
1
  
2
ISR(SPI__STC_vect)
3
{
4
  if (SPI_Ctrl.Direction == READ)
5
  {
6
    if (SPI_Ctrl.NrToSendByte !=0)
7
    {
8
      SPI_SendData();
9
    }
10
    else
11
    {
12
      // Feedback that SPI Transfer is READY
13
      // Aufruf der Funktion über die Adresse für Status Feedback - aber wie??? 
14
      MEM_READ_RESPONCE_FCT(SUCCESS);
15
    }
16
  }
17
}

Genau hierzu wollte ich im Datenarray die Adresse einer Funktion 
(MEM_READ_RESPONCE_FCT) übergeben,
an die eine z.B. Fertig-Meldung zurück gegeben wird.

Spätestens hier bricht es mir das Genick...

1. Übergebe ich die Adresse richtig?
2. Wie rufe ich die Funktion mit der Adresse aus der ISR auf?

Hat jemand eine Idee?

Vorab schon einmal vielen Dank!

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Du hast es schon fast richtig. Du brauchst einen Funktionspointer. Das 
sieht so aus:
1
struct  
2
{
3
  uint8_t    Modul;
4
  uint8_t    BytesInArray;
5
  void (*ResponseFunction) (uint8_t); // Pointer auf Funktion mit Rückgabetyp void, und Parameter uint8_t
6
  uint8_t    DataArray[128];
7
} MEMORY;
Aufruf der Funktion dann über:
1
myMEMORYInstance.ResponseFunction (SUCCESS);

Nicky C. schrieb:
> an ein Modul (Modul, da C und damit
> kein Objekt)
Du kannst auch C++ verwenden, da gibt es Objekte, abstrakte und damit 
Callbacks durch Polymorphie - falls du dieses Muster schon von Java oder 
so kennst...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Anmerkung:

Gewöhn Dir ganz schnell wieder ab, Funktionsnamen in Versalien 
(Großbuchstaben) zu schreiben.

Das ist Macros (#defines) etc. vorbehalten.

: Bearbeitet durch User
von ♪Geist (Gast)


Lesenswert?

>Gewöhn Dir ganz schnell wieder ab, Funktionsnamen in Versalien
>(Großbuchstaben) zu schreiben.
Nanana! Dem muss ich entgegen meckern! Alles Buchstaben groß -> Nein, 
Verwendung von Versalien -> Ja!

von Udo S. (urschmitt)


Lesenswert?

♪Geist schrieb:
>>Gewöhn Dir ganz schnell wieder ab, Funktionsnamen in Versalien
>>(Großbuchstaben) zu schreiben.
> Nanana! Dem muss ich entgegen meckern! Alles Buchstaben groß -> Nein,
> Verwendung von Versalien -> Ja!

Redet ihr nicht eigentlich über Binnenmajuskel?


Oder kann man dazu nicht einfach GROSSBUCHSTABEN sagen?
:-)

von Nicky C. (nicky)


Lesenswert?

Hallo Zusammen,

@Dr. Sommer
vielen Dank für die schnelle Antwort, ich werde es so schnell es geht 
probieren, vllt. schon heute Nacht und gebe dann ein Feedback.

@Rufus Τ. Firefly
Danke für die Bemerkung, hier war zwar alles schnell nur mit dem 
Texteditor extra zurechtgeschrieben und eingekürzt, da es sonst zu viel 
wäre, und so existiert die Funktion bei mir auch nicht, aber dennoch bin 
ich mir ab und an unschlüssig, wann ich welche Schreibweise wähle. Und 
da im Regelfall keiner weiter meinen Code liest, bekomme ich da auch 
kein Feedback zu.
Daher noch einmal vielen Dank für die Anmerkung, ich werde es 
verinnerlichen... und mich auch noch einmal schlau machen.
MACROS komplett groß wusste ich, aber sonst streiten sich die Gelehrten 
in jedem Buch, was, wann, wie richtig ist und jeder gibt andere 
Empfehlungen...

von Karl H. (kbuchegg)


Lesenswert?

Nicky C. schrieb:

> wäre, und so existiert die Funktion bei mir auch nicht, aber dennoch bin
> ich mir ab und an unschlüssig, wann ich welche Schreibweise wähle.

Die Sache ist sehr einfach.

In C ist es manchmal lebenswichtig zu wissen, ob etwas ein Makro oder 
eine Funktion oder Variable ist.

Denn Makros sind potentiell gefährlich.
Daher gibt es seit Anbeginn der Welt eine Übereinkunft an die sich 
Millionen C-Programmierer weltweit halten: Makronamen werden 
ausschliesslich in Grossbuchstaben geschrieben und umgekehrt ist ein 
Name in ausschliesslich Grossbuchstaben auch immer ein Makro.

Denn wie gesagt, ist es an manchen Stellen wichtig zu wissen, ob man es 
mit einem Makro zu tun hat, oder nicht
1
  int a = 5;
2
  int b = 6;
3
  int c;
4
5
  c = MAX( a++, b++ );
da dieses MAX hier groß geschrieben ist, ist das ein Makro und alleine 
aus diesem Wissen heraus kann ich folgern, dass die 'Parameter' a++ bzw. 
b++ keine gute Idee sind. Denn a oder b haben danach nicht die Werte 6 
bzw. 7, wie man das naiv annehmen würde. Je nachdem wie das Makro 
konkret geschrieben ist, sind das ganz andere Werte. Und auch c hat dann 
unter Umständen nicht den erwarteten Wert.

Hingegen ist in
1
  int a = 5;
2
  int b = 6;
3
  int c;
4
5
  c = max( a++, b++ );

alles klar. 'max' ist nicht groß geschrieben, daher ist das eine normale 
Funktion. Sowohl a, b, als auch c haben die naiv zu erwartenden Werte.

> MACROS komplett groß wusste ich, aber sonst streiten sich die Gelehrten
> in jedem Buch, was, wann, wie richtig ist und jeder gibt andere
> Empfehlungen...

Makros komplett groß ist so ziemlichz die einzige Regel, die weltweit 
gültig ist und an die sich alle diskussionslos halten.

von Karl H. (kbuchegg)


Lesenswert?

PS:
Man kann sich mit einem typedef für den Funktionspointer das Leben 
meistens um einiges leichter machen
1
typedef void (*Callback) (uint8_t);

und schon hast du einen neuen Datentyp namens Callback, welcher einen 
Funktionszeiger auf Funktionen der Signatur 'returns void, takes one 
uint8_t' dartstellt.
1
typedef void (*Callback) (uint8_t);
2
3
struct  
4
{
5
  uint8_t    Modul;
6
  uint8_t    BytesInArray;
7
  Callback   ResponseFunction;
8
  uint8_t    DataArray[128];
9
} MEMORY;

speziell wenn dann dieser Datentyp auch noch in ein paar 
Funktionsargumenten vorkommt, vereinfacht es das Leben enorm, wenn man 
dem ganzen durch den typedef einen Namen gibt.

von Stefan W. (dl6dx)


Lesenswert?

Karl Heinz schrieb:
> Denn wie gesagt, ist es an manchen Stellen wichtig zu wissen, ob man es
> mit einem Makro zu tun hat, oder nicht

Hallo Karl-Heinz,

wenn du gestattest, möchte ich das dem TO noch einmal etwas 
ausführlicher erläutern. Er kennt die Gefahr durch Seiteneffekte 
möglicherweise noch nicht.

Also: Die potentielle Gefahr bei der unkritischen Verwendung von Makros 
ist, dass je nach dem Aufbau des Makros beim Textersatz Argumente 
dupliziert werden können. Ein Beispiel:
1
#define MAX(x, y) ((x > y) ? x : y)
2
3
int a, b, c;
4
5
c = MAX(a++, b++);

Der Präprozessor macht daraus:
1
int a, b, c;
2
3
c = ((a++ > b++) ? a++ : b++);

Man sieht, dass durch die Textexpansion plötzlich beide 
Inkrement-Operationen plötzlich zweimal da stehen und je nach den Werten 
von a und b eine Variable plötzlich zweimal inkrementiert wird.

Grüße

Stefan

von Nicky C. (nicky)


Lesenswert?

Hallo Zusammen,

erst einmal vielen Dank für eure Hilfe, man lernt nie aus und
ES FUNKTIONIERT und ich bin fast happy!!!
Ich habe es bisher nur im Atmel Studio simuliert, aber da schaut es gut 
aus.

@Stefan Wagner: Danke für die Erklärung, so ähnlich habe ich es auch mal 
gelesen, nie verwendet und auch prompt wieder verdrängt, weshalb ich 
MACROS bei nicht trivialen Dingen, zumindest zur Zeit lieber meide und 
doch eine Funktion verwende.


Ich habe trotzdem noch Fragen... :-(

ich habe für das SPI-Modul, als auch für das Speicher-Modul eine 
Struktur aufgebaut... und wie vorgeschlagen einen neuen Typ generiert.
1
typedef  void (*Callback) (uint8_t);
2
3
//Struktur für SPI-Modul
4
struct  
5
{  
6
  uint8_t    Status;
7
  Callback   ResponseFct;
8
  uint8_t    Direction;
9
  uint8_t    SlaveModul;
10
  uint8_t    *ptr_DataAddress;
11
  uint8_t    NumberBytesToRead;
12
}spiCtrl;
13
14
// Struktur für ext. EEPROM (SPI)
15
struct
16
{
17
  uint8_t    Modul;
18
  Callback   ResponseFct;
19
  uint8_t    BytesInArray;
20
  uint8_t    Data[5];
21
} Memory;

angelegt.
Dann baue ich mir in einer Funktion meinen Datensatz auf, in dem 
"readMemReady" meine Funktion ist, die die Antwort nach Beendigung der 
Arbeit erhält:
1
void readMem(void)
2
{
3
  Memory.Data[0] = 0;
4
  Memory.Data[1] = 1;
5
  Memory.Data[2] = 2;
6
  Memory.Data[3] = 3;
7
  
8
  Memory.Modul        = MEMORY_MODUL;
9
  Memory.ResponseFct  = &readMemReady; 
10
  Memory.BytesInArray = 4;
11
    
12
  spi_Read(&Memory);
13
}

Nun muss ich im SPI-Modul so etwas machen (doppeltes *address++ bei 
spiCtrl.ResponseFct), damit die Daten auch passen.
Geht das eleganter?
Ich denke, und denke mir wirklich nur, es liegt an den 8Bit Variablen in 
der Struktur und der 16Bit für die Pointer
1
void  spi_Read(uint8_t *address)
2
{
3
  spiCtrl.Direction         = READ;
4
  spiCtrl.Status            = NOT_FREE;
5
  spiCtrl.SlaveModul        = *address++; 
6
  spiCtrl.ResponseFct       = *address++;
7
                              *address++;
8
  spiCtrl.NumberBytesToRead = *address++;
9
  spiCtrl.ptr_DataAddress   = address;
10
  
11
  Set_spi_Slave(spiCtrl.SlaveModul); //setzt die ChipSelect Leitungen der Slaves
12
  
13
  spi_ReadData();
14
}

Vorab DANKE!!!

von Rolf Magnus (Gast)


Lesenswert?

Karl Heinz schrieb:
> Daher gibt es seit Anbeginn der Welt eine Übereinkunft an die sich
> Millionen C-Programmierer weltweit halten: Makronamen werden
> ausschliesslich in Grossbuchstaben geschrieben und umgekehrt ist ein
> Name in ausschliesslich Grossbuchstaben auch immer ein Makro.

Und dann gibt's da noch die Java- und leider auch manche 
C++-Programmierer, die diese Regel falsch verstanden haben und alle 
Konstanten in Großbuchstaben schreiben, weil Makros in C halt beshonders 
häufig als Ersatz für Konstanten verwendet werden.

♪Geist schrieb:
>>Gewöhn Dir ganz schnell wieder ab, Funktionsnamen in Versalien
>>(Großbuchstaben) zu schreiben.
> Nanana! Dem muss ich entgegen meckern! Alles Buchstaben groß -> Nein,
> Verwendung von Versalien -> Ja!

Wieder was gelernt...

Wikipedia:
"Der synonyme Gebrauch der Bezeichnung Versalbuchstabe oder Versal (Pl. 
Versalien) ist gängige Druckersprache, aber etymologisch betrachtet 
ungenau. Versal ist von lateinisch versus (Zeile, Absatz) abgeleitet und 
bezeichnet eigentlich die großen, in alten Handschriften und frühen 
Drucken nicht selten ausgeschmückten Anfangsbuchstaben (Initialen) von 
Absätzen und Verszeilen."

Nun schreibe ich aber selten einen ganzen Absatz in einen Funktionsnamen 
;-)

von Karl H. (kbuchegg)


Lesenswert?

Nicky C. schrieb:

> Nun muss ich im SPI-Modul so etwas machen (doppeltes *address++ bei
> spiCtrl.ResponseFct), damit die Daten auch passen.
> Geht das eleganter?

Wenn spi_Read sowieso ein "struct Memory" Objekt haben will, warum sagst 
du das dann nicht in der Argumentliste?

Ah, ich seh schon. Deswegen
1
struct
2
{
3
  uint8_t    Modul;
4
  Callback   ResponseFct;
5
  uint8_t    BytesInArray;
6
  uint8_t    Data[5];
7
} Memory;
du hast eine anonyme Struktur gebaut. Keine gute Idee. Gewöhn dir das 
wieder ab. Eine Struktur ist ein Datentyp wie jeder andere. Ab und zu 
kommt man mal mit einer anonymen Struktur durch, aber meistens ist die 
Idee nicht so schlau. Gib dem Ding einen Namen und du kannst dich darauf 
beziehen
1
struct memory_t
2
{
3
  uint8_t    Modul;
4
  Callback   ResponseFct;
5
  uint8_t    BytesInArray;
6
  uint8_t    Data[5];
7
} Memory;
1
void  spi_Read(struct memory_t* mem)
2
{
3
  spiCtrl.Direction         = READ;
4
  spiCtrl.Status            = NOT_FREE;
5
  spiCtrl.SlaveModul        = mem->Modul;
6
  spiCtrl.ResponseFct       = mem->ResponseFct;
7
  spiCtrl.NumberBytesToRead = mem->BytesInArray;
8
  spiCtrl.ptr_DataAddress   = mem->Data;
9
10
  Set_spi_Slave(spiCtrl.SlaveModul); //setzt die ChipSelect Leitungen der Slaves
11
 
12
  spi_ReadData();
13
}

Je weniger du mit 'typenlosen' Pointern hantierst, egal ob das jetzt 
void* oder uint8_t* sind, desto besser. Denn dann klinkt sich der 
Compiler ein, und überprüft für dich, ob du auch jeweils die richtigen 
Pointer übergibst.
Ganz abgesehen davon, dass du dich in deiner Version mit deinem Code auf 
eine bestimmte Member-Reihenfolge festgenagelt hast - ändere sie und 
dein Programm bricht auseinander.

: Bearbeitet durch User
von Udo S. (urschmitt)


Lesenswert?

Rolf Magnus schrieb:
> Und dann gibt's da noch die Java-

Falsch. Java hat seine eigenen Regeln Code zu strukrutieren und 
Packages, Klassen, Methoden, Variablen und Konstanten zu schreiben.
und diese Regeln werden im Java Umfeld meines Erachtens deutlich 
einheitlichert eingehalten wie in C / C++.
Und eine der Regeln ist daß Konstanten groß geschrieben werden.
Das schliesst sogar enum Typen mit ein.

von Karl H. (kbuchegg)


Lesenswert?

PS:

Wenn ich mir das so überlege, dann wäre spiMsg_t ein besserer Name für 
die Datenstruktur. Denn genau das beschreibt ja so ein Objekt: Eine 
'Nachricht' die vom SPI Modul behandelt werden soll. Dass du im Moment 
nur 'Nachrichten' hast, die im EEPROM landen, ist zwar schön, aber 
eigentlich ist das ja nicht der Normalfall. D.h. abstrahiert von der 
konkreten Verwendung in deinem Programm, ist das einfach nur eine 
'Nachricht', die vom SPI Modul von/zu einem bestimmten Modul 
transportiert werden soll
1
struct spiMsg_t_
2
{
3
  uint8_t    Modul;
4
  Callback   ResponseFct;
5
  uint8_t    BytesInArray;
6
  uint8_t    Data[5];
7
};
8
9
typedef struct spiMsg_t_ spiMsg_t;
10
11
void void  spi_Read( spiMsg_t* msg)
12
{
13
  spiCtrl.Direction         = READ;
14
  spiCtrl.Status            = NOT_FREE;
15
  spiCtrl.SlaveModul        = msg->Modul;
16
  spiCtrl.ResponseFct       = msg->ResponseFct;
17
  spiCtrl.NumberBytesToRead = msg->BytesInArray;
18
  spiCtrl.ptr_DataAddress   = msg->Data;
19
20
  Set_spi_Slave(spiCtrl.SlaveModul); //setzt die ChipSelect Leitungen der Slaves
21
 
22
  spi_ReadData();
23
}

und darauf aufbauend hast du dann den Fall, dass du eben mit einem 
EEPROM kommunizieren willst.
1
spiMsg_t eepromMsg;
2
3
void readMem(void)
4
{
5
  eepromMsg.Data[0] = 0;
6
  eepromMsg.Data[1] = 1;
7
  eepromMsg.Data[2] = 2;
8
  eepromMsg.Data[3] = 3;
9
  
10
  eepromMsg.Modul        = MEMORY_MODUL;
11
  eepromMsg.ResponseFct  = &readMemReady; 
12
  eepromMsg.BytesInArray = 4;
13
    
14
  spi_Read( &eepromMsg );
15
}

du könntest ja auch mit einem ADC kommunizieren wollen
1
spiMsg_t adcMsg;
2
3
void readADC( uint8_t channel )
4
{
5
  eepromMsg.Data[0] = channel;
6
  
7
  eepromMsg.Modul        = ADC_MODUL;
8
  eepromMsg.ResponseFct  = &readADCReady; 
9
  eepromMsg.BytesInArray = 1;
10
    
11
  spi_Read( &adcMsg );
12
}

das alles tangiert aber das SPI Modul nicht. Das SPI Modul bekommt eine 
allgemeine SPI Message Datenstruktur, in der die Details stehen, was es 
zu tun hat.

von Karl H. (kbuchegg)


Lesenswert?

Da ein SPI Control Objekt sowieso einen Pointer zu den Daten hält (die 
damit beim Aufrufer nicht out of Scope gehen dürfen), würde ich auch 
gleich etwas Speicher sparen, und in dieses Control Objekt einfach nur 
einen Pointer auf ein Message Objekt einbauen
1
struct spiMsg_t_
2
{
3
  uint8_t  Modul;
4
  Callback ResponseFct;
5
  uint8_t  BytesInArray;
6
  uint8_t  Data[5];
7
};
8
typedef struct spiMsg_t_ spiMsg_t;
9
10
struct spiCtrl_t_
11
{  
12
  uint8_t   Status;
13
  uint8_t   Direction;
14
  spiMsg_t* pMsg;
15
};
16
typedef struct spiCrtl_t_ spiCtrl_t;
17
18
static spiCtr_t spiCtrl;
19
20
void void  spi_Read( spiMsg_t* msg )
21
{
22
  spiCtrl.Status    = NOT_FREE;
23
  spiCtrl.Direction = READ;
24
  spiCtrl.pMsg      = msg;
25
26
  Set_spi_Slave(spiCtrl.pMsg->Modul); //setzt die ChipSelect Leitungen der Slaves
27
 
28
  spi_ReadData();
29
}

Entweder hält dieses Control Objekt einen Poiner auf die 
Original-Nachricht vom Aufrufer, oder aber es macht sich eine Kopie 
davon (damit befreist du den Aufrufer davon, dass er das Original-Objekt 
vorrätig halten muss, bis der komplette Vorgang abgeschlossen ist. Das 
wird dann wichtig, wenn die Operationen mal asynchron laufen sollen. 
D.h. spi_ReadData stösst den Vorgang nur an und während das Programm 
weiterläuft findet die Übertragung statt, wird auch irgendwann fertig, 
aber das Programm weiß nicht genau wann. Dann könnte man in spiCtrl eine 
Kopie der Nachricht einbauen
1
struct spiMsg_t_
2
{
3
  uint8_t    Modul;
4
  Callback   ResponseFct;
5
  uint8_t    BytesInArray;
6
  uint8_t    Data[5];
7
};
8
9
struct spiCtrl_t_
10
{  
11
  uint8_t           Status;
12
  uint8_t           Direction;
13
  struct spiMsg_t_  Msg;
14
};
15
16
typedef struct spiMsg_t_ spiMsg_t;
17
typedef struct spiCtrl_t_ spiCtrl_t;
18
19
static spiCtrl_t spiCtrl;
20
21
void void  spi_Read( spiMsg_t* msg )
22
{
23
  spiCtrl.Status    = NOT_FREE;
24
  spiCtrl.Direction = READ;
25
  spiCtrl.Msg       = *msg;
26
27
  Set_spi_Slave( spiCtrl.Msg.Modul ); //setzt die ChipSelect Leitungen der Slaves
28
 
29
  spi_ReadData();
30
}

(
worauf ich eigentlich hinaus will, ist die Zeile
1
  spiCtrl.Msg       = *msg;
da der Compiler weiß, dass es sich hier um spiMsg_t Objekte handelt, 
kann er die Zuweisung ganz einfach so machen. Du musst nicht Member für 
Member umkopieren. Egal wie groß so ein Strukturobjekt ist, mit so einer 
Zuweisung wird es als ganzes umkopiert.
)

: Bearbeitet durch User
von Steffen R. (steffen_rose)


Lesenswert?

Karl Heinz schrieb:
>> MACROS komplett groß wusste ich, aber sonst streiten sich die Gelehrten
>> in jedem Buch, was, wann, wie richtig ist und jeder gibt andere
>> Empfehlungen...
>
> Makros komplett groß ist so ziemlichz die einzige Regel, die weltweit
> gültig ist und an die sich alle diskussionslos halten.

Dem möchte ich keineswegs widersprechen. Ich kenne es genauso und 
definiere es überall, wo es geht, in dieser Weise. Aber:

Ich habe immer ein Entscheidungsproblem, wenn ich aus den 
"function-like" Macros Inlinefunktionen mache. Ändere ich nun die 
Schreibweise im gesamten Programm (von gross auf klein) oder lasse ich 
es so, wie es ist.

Und selbst der Misra-C 2012 Standard nutzt kleingeschriebene Macros 
(z.B. Rule 5.5 Example).

Insofern kann die Schreibweise immer nur ein Anhaltspunkt sein. Gerade 
wenn man fremden Code in Händen hat, der dem eigenen Firmenstandard 
nicht entspricht.

von cybmorg (Gast)


Lesenswert?

Das hier gezeigt MAX()-Macro ist ein gutes Beispiel fuer etwas, wofuer 
Macros gar nicht verwendet werden sollten. Mal wieder so eine "so haben 
wir es in den 80ern schon gemacht"-Optimierung. Mit einer statischen 
Methode im Header (oder fuer die, die sich krampfhaft an C klammern 
kann's auch eine includebare Funktion sein) haette der Compiler alle 
Moeglichkeit selbst zu entscheiden, ob er je nach Optimierungsziel 
inlined oder nicht. Und ueber unerwuenschtes Verhalten wie oben muss man 
sich gar keine Gedanken machen.

von Nicky C. (nicky)


Lesenswert?

Hallo und ein dickes Danke an Alle die sich mit eingeklinkt haben!

@Karl Heiz:
Du hast genau die Idee dahinter verstanden, die ich eigentlich umsetzen 
möchte. Ich denke, es geht in "Richtung" Objektorientierung, verwende 
aber nicht C++, denn wie man sieht habe ich in C erst einmal noch einige 
Hausaufgaben zu machen und krampfe bei C++ noch mehr mit dem Syntax und 
den Klassen herum.

In der Tat sollen mehrere Baugruppen über SPI kommunizieren. In meinem 
Fall gerade ein externer EEPROM, ein Display und ganz zum Schluss 
eventuell noch eine SD-Karte.
Dem SPI-Modul kann und soll es egal sein, von wem es die Daten bekommt 
und soll nach Aufruf und erhalt der Daten über den Pointer eigenständig 
arbeiten, damit der µC Zeit für andere Dinge hat (wie I2C-Steuerung, 
UART, Taster-Abfragen u.a.) und nach Fertigstellung des Auftrages eine 
Message zurück geben, um ggf. weitere Dinge durchführen zu können oder 
einfach nur zu wissen, ob der Auftrag erledigt wurde oder nicht (warum 
auch immer) ...

Bisher habe ich hart und strikt alle Parameter "global" gemacht, um auf 
alles von überall zugreifen zu können, was aber nicht Sinn und Zweck 
sein kann. Deshalb, denn man möchte ja besser werden, setzt man sich 
selbst Ziele, auch wenn man sich damit gerade selbst die Beine stellt.
Aktuell, mit Deinen Code-Vorschlägen, wenn ich auch das Grundprinzip 
etwas verstehe..., trotzdem schwere Kost, die es zu verdauen und zu 
verarbeiten gilt.

Ich habe also zu tun... ;-) Auf geht's, Bucher wälzen, und verstehen 
versuchen...

Nochmal BESTEN DANK für die Unterstützung!

von Axel S. (a-za-z0-9)


Lesenswert?

cybmorg schrieb:
> Das hier gezeigt MAX()-Macro ist ein gutes Beispiel fuer etwas, wofuer
> Macros gar nicht verwendet werden sollten. Mal wieder so eine "so haben
> wir es in den 80ern schon gemacht"-Optimierung.

Mit Optimierung hat das nix zu tun. Es war/ist einfach die bequemste 
Art, das in C zu machen.

> Mit einer statischen
> Methode im Header (oder fuer die, die sich krampfhaft an C klammern
> kann's auch eine includebare Funktion sein)

Nein, kann es nicht.

Ein Makro MAX() paßt für alle Datentypen, sogar beliebig gemischt. 
Eine C-Funktion hingegen nur für genau die spezifizierten Argument- 
Typen. Und da C keine polymorphen Funktionen hat, kann man auch nicht
1
int max(int a, int b)
2
{
3
  ...
4
}
und
1
double max(double a, double b)
2
{
3
  ...
4
}
gleichzeitig haben, sondern muß den Funktionen dann verschiedene Namen 
geben. Erst mit C++ läßt sich das vernünftig schreiben.

Typische freistehende Funktionen (Paradebeispiel: main()) als statische 
Methoden in eine Pseudo-Klasse zu stecken ist Java-Bullshit und IMNSHO 
einer der hassenswertesten Aspekte an Java.


XL

von Karl H. (kbuchegg)


Lesenswert?

> ... trotzdem schwere Kost, die es zu verdauen und zu verarbeiten gilt.

findest du?
Ich hab im Grunde nicht viel gemacht. Ich hab nur dein System etwas 
anders strukturiert und der statischen Prüfung durch den Compiler 
zugeführt. Aber das Prinzip ist nach wie vor dasselbe.

von Udo S. (urschmitt)


Lesenswert?

Axel Schwenke schrieb:
> Typische freistehende Funktionen (Paradebeispiel: main()) als statische
> Methoden in eine Pseudo-Klasse zu stecken ist Java-Bullshit und IMNSHO
> einer der hassenswertesten Aspekte an Java.

Sag mal Axel, was ist denn mit dir los, ich hasse keine Sprache, ausser 
ich würde gezwungen sie zu benutzen und sie nervt mich dann damit, daß 
ich Dinge nicht oder nur sehr umständlich damit lösen kann.

Aber um was zu lernen, was ist denn an der main() so schlimm, und wie 
machen das andere Sprachen besser? In c++ wird doch auch eine main() 
verwendet?

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> Aber um was zu lernen, was ist denn an der main() so schlimm,

Nichts. Was Axel meint, ist, daß in Java so etwas in eine Klasse 
verpackt wird, obwohl es eine freistehende, d.h. klassenlose Funktion 
ist.

von Udo S. (urschmitt)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Was Axel meint, ist, daß in Java so etwas in eine Klasse
> verpackt wird, obwohl es eine freistehende, d.h. klassenlose Funktion
> ist.

Ja und? Was ist daran so schlimm? Ich sehe sogar den Vorteil: Ich kann 
in einem Projekt (jar) mehrere unabhängige Programme haben die ich je 
nach Aufruf anspringe.

Na ja, egal.

von Karl H. (kbuchegg)


Lesenswert?

Udo Schmitt schrieb:

> Na ja, egal.

Ja, bitte.
Das alles hat nichts mit dem eigentlichen Thema zu tun.

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.