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_tModul;
6
uint8_tBytesInArray;
7
uint16_t*ResponseFunction;
8
uint8_tDataArray[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
voidMemRead(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
voidMEM_READ_RESPONCE_FCT(uint8_tstatus)
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!
Du hast es schon fast richtig. Du brauchst einen Funktionspointer. Das
sieht so aus:
1
struct
2
{
3
uint8_tModul;
4
uint8_tBytesInArray;
5
void(*ResponseFunction)(uint8_t);// Pointer auf Funktion mit Rückgabetyp void, und Parameter uint8_t
6
uint8_tDataArray[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...
>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!
♪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?
:-)
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...
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
inta=5;
2
intb=6;
3
intc;
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
inta=5;
2
intb=6;
3
intc;
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.
PS:
Man kann sich mit einem typedef für den Funktionspointer das Leben
meistens um einiges leichter machen
1
typedefvoid(*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
typedefvoid(*Callback)(uint8_t);
2
3
struct
4
{
5
uint8_tModul;
6
uint8_tBytesInArray;
7
CallbackResponseFunction;
8
uint8_tDataArray[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.
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
inta,b,c;
4
5
c=MAX(a++,b++);
Der Präprozessor macht daraus:
1
inta,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
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
typedefvoid(*Callback)(uint8_t);
2
3
//Struktur für SPI-Modul
4
struct
5
{
6
uint8_tStatus;
7
CallbackResponseFct;
8
uint8_tDirection;
9
uint8_tSlaveModul;
10
uint8_t*ptr_DataAddress;
11
uint8_tNumberBytesToRead;
12
}spiCtrl;
13
14
// Struktur für ext. EEPROM (SPI)
15
struct
16
{
17
uint8_tModul;
18
CallbackResponseFct;
19
uint8_tBytesInArray;
20
uint8_tData[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
voidreadMem(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
voidspi_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
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
;-)
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_tModul;
4
CallbackResponseFct;
5
uint8_tBytesInArray;
6
uint8_tData[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
structmemory_t
2
{
3
uint8_tModul;
4
CallbackResponseFct;
5
uint8_tBytesInArray;
6
uint8_tData[5];
7
}Memory;
1
voidspi_Read(structmemory_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.
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.
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
structspiMsg_t_
2
{
3
uint8_tModul;
4
CallbackResponseFct;
5
uint8_tBytesInArray;
6
uint8_tData[5];
7
};
8
9
typedefstructspiMsg_t_spiMsg_t;
10
11
voidvoidspi_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_teepromMsg;
2
3
voidreadMem(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_tadcMsg;
2
3
voidreadADC(uint8_tchannel)
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.
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
structspiMsg_t_
2
{
3
uint8_tModul;
4
CallbackResponseFct;
5
uint8_tBytesInArray;
6
uint8_tData[5];
7
};
8
typedefstructspiMsg_t_spiMsg_t;
9
10
structspiCtrl_t_
11
{
12
uint8_tStatus;
13
uint8_tDirection;
14
spiMsg_t*pMsg;
15
};
16
typedefstructspiCrtl_t_spiCtrl_t;
17
18
staticspiCtr_tspiCtrl;
19
20
voidvoidspi_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
structspiMsg_t_
2
{
3
uint8_tModul;
4
CallbackResponseFct;
5
uint8_tBytesInArray;
6
uint8_tData[5];
7
};
8
9
structspiCtrl_t_
10
{
11
uint8_tStatus;
12
uint8_tDirection;
13
structspiMsg_t_Msg;
14
};
15
16
typedefstructspiMsg_t_spiMsg_t;
17
typedefstructspiCtrl_t_spiCtrl_t;
18
19
staticspiCtrl_tspiCtrl;
20
21
voidvoidspi_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.
)
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.
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.
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!
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
intmax(inta,intb)
2
{
3
...
4
}
und
1
doublemax(doublea,doubleb)
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
> ... 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.
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?
> 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.
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.