Void Definition und Verwendung

Hallo zusammen,
mal eine ganz eventuell einfache Frage, die ich mir nicht erklären kann. Es wird immer der Befehl void setup und void loop bei programmieren verwendet. Der Befehl hat nicht wirklich eine Wertung. Warum setzt man Void ein? Hätte man nicht einfach setup oder loop ohne void schreiben können? Zudem würde ich gerne wissen, warum die geschweiften Klammern hinter setup und loop stehen. Es stehen doch noch weitere Klammern hinter den Klammern, wo definiert wird, was hier ausgeführt und definiert werden soll.

Beste Grüße

Stefan

Dir fehlt das C++ Grundlagenbuch!

Alternativ: C++ keyword: void - cppreference.com

Das hätte dir erklärt, dass man das nicht weg lassen kann, denn das ist der Datentype des Rückgabewertes der Funktion.
Wobei void hier "Keinen", oder "nicht vorhanden" meint.

KA, was du meinst.....
Das Buch erklärt dir, wie Funktionen auszusehen haben.
Wenn dir das nicht schmeckt, ........

ich schaue da mal rein

In C muß jede Deklaration mit einem Datentyp beginnen. Bei Funktionen ist das der Typ des Ergebnisses. Wenn eine Funktion kein Ergebnis zurückliefert, wird als Typ void (leer, nichts) angegeben.

Vermutlich meinst Du die runden Klammern, die eine Funktion anzeigen. Folgt ein Semicolon dann ist es eine Deklaration, sonst muß der Code in geschweiften Klammern die Definition vervollständigen.

Gut, ich versuche es mir an Beispielen deutlich zu machen und zu verstehen und nachzuvollziehen.

Es gibt auch die Möglichkeit daß eine Funktion keine Werte übergeben bekommt und keinen Rückgabewert zurückgibt.

zB

void loop( void )

Grüße Uwe

Langsam kommt es an.

Und ein entsprechender Übergabewert könnte so aussehe:

Aufruf:
meine_funktion(daten);

Funktion:
void meine_funktion(long meine_daten)
{
}

meine_funktion wird also in Funktion erneut eingebunden und dann weiter verarbeitet?

Nein, nicht so.
Die Funktion ist eine eigenständige, allein stehende Funktion und wird aus einer anderen Funktion, z.B loop() mit der Anweisung "meine_funktion(daten);" aufgerufen. Damit werden die entsprechenden Daten mit übergeben.

ok, verstanden. Nach 5-mal durchlesen. So meinte ich es auch, konnte es nur nicht richtig wiedergeben.

Alles klar.

Genaugesagt Ist main() die Funktion die in einem C/C++ Programm automatisch aufgerufen wird wenn ein Programm gestartet wird.
Arduino hat auch eine main() Funktion. Die wird aber automatisch zum Sketch dazugetan und diese ruft 1 mal setup() auf und dann in einer Schleife immer wieder loop().
Grüeß Uwe

Aber nur wenn loop() und setup() vorhanden sind.

Diese Möglichkeit habe ich nicht ausgeschlossen weil so ein Sketch die Kompilierung mit einef Fehlermeldung abbricht daß die nicht vorhandene Funktion fehlt.

Grüße Uwe

Das kleinste mögliche Arduino Programm, welches fehlerfrei kompiliert und ebenso fehlerfrei durch läuft.

int main(){}

Nagut, es macht quasi nix, aber das macht es sehr gut!

Es macht: "Prozessor spielt mit seinen Daumen"

Es landet fast unmittelbar in exit()
Womit wir bei deinem Daumen wären.

Dieses ist übrigens der gesamte generierte Code(UNO):
(Flags+Stackpointer einrichten, main() aufrufen und ab nach exit())


00000068 <__ctors_end>:
  68:	11 24       	eor	r1, r1
  6a:	1f be       	out	0x3f, r1	; 63
  6c:	cf ef       	ldi	r28, 0xFF	; 255
  6e:	d8 e0       	ldi	r29, 0x08	; 8
  70:	de bf       	out	0x3e, r29	; 62
  72:	cd bf       	out	0x3d, r28	; 61
  74:	0e 94 40 00 	call	0x80	; 0x80 <main>
  78:	0c 94 43 00 	jmp	0x86	; 0x86 <_exit>

0000007c <__bad_interrupt>:
  7c:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

00000080 <main>:
int main(){}
  80:	90 e0       	ldi	r25, 0x00	; 0
  82:	80 e0       	ldi	r24, 0x00	; 0
  84:	08 95       	ret

00000086 <_exit>:
  86:	f8 94       	cli

00000088 <__stop_program>:
  88:	ff cf       	rjmp	.-2      	; 0x88 <__stop_program>

void meint: "nichts".
Wenn Du eine Funktion hast, die als Return void definiert - dann wird diese KEIN Wert zurueckliefern (und sie wird es auch nicht koennen).
Der Compiler prueft, ob Du was erwartest als Return aber die liefert nichts zurueck.
Umgekehrt genauso:
Hast Du eine Funktion, die was zurueck gibt - und wenn nichts definiert wird 'int' angenommen - programmierst ohne was wirklich mit return value; zurueck zu geben - dann wird er meckern.

Es gibt noch zwei andere Bedeutungen von "void":

  • hast Du einen Pointer (mit einer Speicheraddresse als Wert), aber der is unbestimmt, wie denn der Speicher organisiert ist (als bytes, als sequence von 32bit Woertern oder gar Strukturen) - dann sieht man oft (void *). Das ist z.B. bei memcpy() der Fall: das muss nicht wissen, ob der Pointer ausgerichtet ist, auf welche Objekte er zeigt (welcher Typ, es ist typ-invariant).

  • Du kannst auch eine Funktion haben, die als definiert einen Parameter erwartet, z.B.:
    void myFunction(int i) {
    Aber Du benutzt dann i gar nicht, auch wenn jedesmal beim Aufruf dort ein i uebergeben
    wird.
    Dann kannst Du in der Funktion schreiben:
    (void)i;
    Das sagt dem Compiler: auch wenn beim Aufruf ein Wert fuer i erwartet und auch
    uebergeben wird: der Wert von i (die Variable i) wird nachfolgend nicht benutzt (ansonsten
    ein Warnung des Compilers, dass i nicht verwendet wird.

Vielleicht schon mal das gesehen:

#define NULL  (void *)0

Das ist ein ungueltiger Pointer (Speicheraddresse als 0 ist nicht arlaubt, ungueltig). Es ist auch ein "type-invarianter" Pointer:

Wenn nichts bekannt ist, wie der Speicher strukturiert ist (ob Bytes, 32bit-Woerter oder ganze Strukturen dort erwartet und "interpretiert" werden) - dann benutzen wir void.

Bei einem Void-Pointer kannst Du nicht mehr programmieren wie:

i = ptr++;

Der Compiler hat keine Ahnung mit (void *) wie denn die Woerter im Speicher zu behandeln sind: muss ich jetzt ein Byte weiter, 4 Byte fuer 32bit word oder gar N fuer eine Struktur
(void *) hat keine Ahnung und kann den Pointer NICHT auf das naechste Element setzen, da keine Ahnung, wieviel weiter (als Address + N bytes).

So, void hat drei Anwendungen:

  • Eine Funktion gibt nichts zurueck: es kann nichts zugewiesen werden als Return Value, weil nichts zurueck gegeben wird.

  • Ein Parameter, auch wenn erforderlich und gefordert beim Aufruf einer Funktion, wird nicht in Funktion verwendet: (void)i;

  • Ein Pointer hat keine Ahnung wie der Speicher strukturiert ist: (void *)
    Der Pointer kann nicht bewegt werden (es sei denn man casted den um in einen anderen Typ).

  • ein NULL Pointer (als (void *)0 ): meint: der Pointer ist nicht initialisiert, er ist ungueltig, er hat keine gueltige Speicheraddress (der Zugriff auf Adresse 0 in allen System ist oft verboten und crasht das System).

So, void meint: "nichts", "nicht gueltig", "nicht verwendet" oder "Type ist nicht bekannt".
Ein ziemlich wichtiges "Feature" um sicher zu programmieren. Daher findet sich dieses void so oft in C/C++ code. Es vermeidet Fehler beim Programnmieren:
wenn mal etwas als void definiert war - dann kann man es auch nicht benutzen.
Oft gilt in C/C++: wenn nicht defiiniert, dann ist es 'int'. Aber das kann fatal sein, z.B. wenn vergessen zu sagen, dass Funktion NICHTS zurueck liefert, aber dafuer 'int' angenommen wird, aber Programmierer vergessen wirklich was zurueck zu geben.

Ich verwende selbst VOID auch bei Funktions-Definition:
Auch wenn es ausreichend ist zu schreiben:

MyFunction() {

ich schreibe immer:

void MyFunction(void) {

Damit wird klar: es wird NICHT das Standard 'int' zurueck gegeben und es werden auch keinerlei Parameter erwartet. Es macht das Programmieren sicherer.

Habe ich getestet!

test(){return 0;}
int main(){test();}

Meldet:

sketch_aug06e:1:8: error: expected constructor, destructor, or type conversion before ';' token
    1 | test(){return 0;}
      |        ^

Da muss sich also in den letzten 100 Jahren was geändert haben.

E:\Programme\arduino\portable\sketchbook\sketch_aug06e\sketch_aug06e.ino:1:1: warning: ISO C++ forbids declaration of 'test' with no type [-fpermissive]
    1 | test(){return 0;}
      | ^~~~

Selbst in C schreit es auf

E:\Programme\arduino\portable\sketchbook\sketch_aug06e\test.c:1:1: warning: return type defaults to 'int' [-Wimplicit-int]
    1 | test(){return 0;}
      | ^~~~

Was zwar kein Error ist, aber doch zu einer Korrektur anregen sollte.

Das ist vollkommen richtig.
Aber in unserm Arduino und seinem C++
Reicht auch:

void myFunction(int) {}