Forum: Mikrocontroller und Digitale Elektronik Cortex M3 - Strukturen per Reference


von Rudolf H. (Gast)


Lesenswert?

Hallo Leute,

wollte einfach mal Fragen ob es Sinn macht (aus sicht der 
Ausführgeschwindigkeit) bei einem Cortex M3 (oder generell bei 32-Bit 
Prozessoren) Strukturen per Reference anstatt per Value zu übergeben?

Beispiel:
1
typedef struct 
2
{
3
   unsigned long ulNumInst;
4
   unsigned long ulQuality;
5
   char* pcName;
6
   unsigned long ulId;
7
}tSettings;
8
9
/////////////////////////////////////////////
10
// 1. Variante per Reference
11
void ConfigureHardware( tSettings* puSettings )
12
{
13
   // ...
14
}
15
16
int main( void )
17
{
18
   tSettings uSettings;
19
20
   ConfigureHardware( &uSettings );
21
}
22
23
/////////////////////////////////////////////
24
// 2. Variante per Value
25
void ConfigureHardware( tSettings puSettings )
26
{
27
   // ...
28
}
29
30
int main( void )
31
{
32
   tSettings uSettings;
33
34
   ConfigureHardware( uSettings );
35
}

von old man (Gast)


Lesenswert?

Rudolf H. schrieb:
> wollte einfach mal Fragen ob es Sinn macht (aus sicht der
> Ausführgeschwindigkeit) bei einem Cortex M3 (oder generell bei 32-Bit
> Prozessoren) Strukturen per Reference anstatt per Value zu übergeben?

Das hat nichts mit Cortex M3 oder 32-Bit zu tun. Wenn du die Struktur 
per Value übergibst, kommt eine Kopie komplett auf den Stack und dass 
will keiner. Außer es ist gezielt so gewollt. Es geht dabei nicht 
vorrangig um die Ausführungsgeschwindigkeit.
Spätestens wenn in der Funktion die Variablen der Struktur geändert 
werden merkst du den Unterschied. Über den Pointer wird die übergebene 
Struktur geändert. Per Value passiert die Änderung in der Kopie auf dem 
Stack und nur dort.

von Rudolf H. (Gast)


Lesenswert?

old man schrieb:
> Per Value passiert die Änderung in der Kopie auf dem
> Stack und nur dort.

Das ist mir schon klar, aber wenn nur lesend auf die Struktur 
zugegriffen wird ist dies egal (habe ich nicht klar hineingeschrieben, 
sorry!). Mich interessiert konkret, ob es sich aus Sicht der 
Geschwindigkeit lohnt, Pointer zu verwenden (diese bringen ja die Gefahr 
von ungültigen Pointern --> Absturzgefahr mit sich)?

von Ralf G. (ralg)


Lesenswert?

(Variante 1 ist nicht per Reference, sondern per Pointer.)

Die Übergabe per Pointer ist speichersparender. Es können aber die 
Strukturelemente in der Funktion verändert werden. Soll das verhindert 
werden, per 'const' übergeben.

Zusatz: Die Struktur sollte schon existieren. Wenn du per Value 
übergibst, würde in dem Fall eben irgendwelcher Mist drinstehen. Also 
egal.

von Ralf G. (ralg)


Lesenswert?

noch zusätzlich:
'per reference' wird ja nicht mal mehr der Zeiger übergeben. Aber auch 
gegen Überschreiben (sicherheitshalber) mit 'const' deklarieren!

[ das ist ein Beispiel für das speicherverschwendende C++ ;-) ]

von Dr. Sommer (Gast)


Lesenswert?

Ralf G. schrieb:
> noch zusätzlich:
> 'per reference' wird ja nicht mal mehr der Zeiger übergeben. Aber auch
> gegen Überschreiben (sicherheitshalber) mit 'const' deklarieren!
>
> [ das ist ein Beispiel für das speicherverschwendende C++ ;-) ]
Bitte was? Die Referenzen bei Funktions-Argumenten in C++ funktionieren 
auf Maschinenebene exakt wie Pointer. Es wird kein Bit mehr Speicher 
verbraucht als bei C-Pointern. Was ich zum "speicherverschwenden von 
C++" sonst noch zu sagen habe kannst du dir ja denken, das wird in 
diesem Forum ja eh kollektiv weggefiltert.

Rudolf H. schrieb:
> Strukturen per Reference anstatt per Value zu übergeben?
Das hängt von der Größe ab. 5 Register (R0-R5) a 32bit werden (zumindest 
beim EABI) für die Übergabe von Argumenten verwendet. Wenn der struct da 
komplett hineinpasst dauert der Aufruf zwar ein paar Takte länger, dafür 
ist der Zugriff auf die Werte von innerhalb der aufgerufenen Funktion 
schneller, da die Indirektion entfällt. Falls die aufrufende Funktion 
außerdem nicht viel mit dem struct anstellt, kann dieser ggf. komplett 
in den Registern verbleiben und muss gar nicht erst auf den Stack gelegt 
werden, und der Aufruf "by value" muss gar nichts mehr machen, während 
der "by Reference" das Ablegen des structs auf den Stack erzwingen 
würde. Wenn man die Originalwerte verändern will könnte man in Erwägung 
ziehen den veränderten struct zurückzugeben (statt eine Referenz/Pointer 
zu verwenden) - aus den gleichen Gründen wie oben kann das effizienter 
sein. Das ganze hängt von einigen Faktoren ab, es hilft wie immer: 
ausprobieren & disassemblen.

von Ralf G. (ralg)


Lesenswert?

Dr. Sommer schrieb:
> Bitte was? Die Referenzen bei Funktions-Argumenten in C++ funktionieren
> auf Maschinenebene exakt wie Pointer. Es wird kein Bit mehr Speicher
> verbraucht als bei C-Pointern.

Ich meinte: Es könnte sogar noch weniger als der Pointer dabei 
rauskommen! Eigentlich kann die Funktion die Variable doch 'wie global 
verwenden'. Hat eben bloß einen anderen Namen.

Ralf G. schrieb:
> [ das ist ein Beispiel für das speicherverschwendende C++ ;-) ]
                                                            ^^^

von Dr. Sommer (Gast)


Lesenswert?

Ralf G. schrieb:
> Eigentlich kann die Funktion die Variable doch 'wie global
> verwenden'. Hat eben bloß einen anderen Namen.
Es sei denn die Funktion wird mehr als 1x und mit verschiedenen 
structs als Argumenten aufgerufen (dafür sind Funktionen da), dann 
braucht man für jeden struct seinen eigenen Speicher und kann nicht 
einfach alles in eine einzelne globale Variable packen.

Ich habe in meinem Projekt einige Klassen die nur einen einzelnen 
Integer enthalten der auch noch "const" ist. Diese Klassen alle per 
Value anstatt per (const) Reference/Pointer zu übergeben macht den Code 
viel kleiner und schneller.

PS: Ich glaube es waren doch nur 4 Register, kann ich grad nicht 
nachgucken

von Ralf G. (ralg)


Lesenswert?

Um das mal nachzuvollziehen, habe ich's gerade mal mit der Übergabe von 
Ports simuliert. Und da fiels mir dann wie Schuppen aus den Haaren:

Dr. Sommer schrieb:
> Es sei denn die Funktion wird mehr als 1x und mit verschiedenen
> structs als Argumenten aufgerufen (dafür sind Funktionen da)

(Verdammt. Wozu braucht man denn da jetzt solche Referenzen?

Edit: ... Außerhalb von Funktionen)

von Dr. Sommer (Gast)


Lesenswert?

Ralf G. schrieb:
> (Verdammt. Wozu braucht man denn da jetzt solche Referenzen?
Referenzen sind praktisch Pointer, die bezüglich des Beschreibens 
eingeschränkt sind. Daher verwendet man sie dort, wo man Pointer 
verwenden würde und diese Einschränkungen passen. Durch diese 
Einschränkung ergibt sich eine Zusicherung beim Lese-Zugriff, die die 
Programmierung vereinfacht, da es weniger Eventualitäten zu beachten 
gibt.
Konkret: Eine Referenz kann nicht auf "nichts" (NULL) zeigen und kann 
ihr Ziel auch nicht ändern. Das heißt man braucht nicht auf NULL zu 
prüfen und kann annehmen dass sie immer auf das selbe Objekt zeigt.
Die abgewandelte Syntax (zum Pointer) sollte diese Vereinfachung 
deutlich machen, ist aber nicht unbedingt so glücklich gewählt...

Referenzen sind also nur ein Mittel des Programmierers, um dem Compiler 
seine Absicht mitzuteilen und von diesem auch daran erinnert zu werden, 
sich daran zu halten; ähnlich zu "const". Auf Maschinenebene sind 
Referenzen einfach Pointer, können aber leicht innerhalb einer Funktion 
komplett wegoptimiert werden.

von arc net (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Konkret: Eine Referenz kann nicht auf "nichts" (NULL) zeigen und kann
> ihr Ziel auch nicht ändern.

nichts leichter als das...
1
static void TestRef(int& anInt) {
2
    anInt = 123;
3
}
4
5
int* array[4] = { (int *)NULL, (int *)NULL, (int *)NULL, (int *)NULL };
6
TestRef(*array[3]);

von Dr. Sommer (Gast)


Lesenswert?

arc net schrieb:
> nichts leichter als das...
Das ist undefiniertes Verhalten. Bevor man einer Referenz einen 
dereferenzierten Pointer zuweist muss man sie natürlich auf Gültigkeit 
(!= NULL) prüfen:
1
void TestRef(int& anInt) {
2
    anInt = 123;
3
}
4
5
int* array[4] = { nullptr, nullptr, nullptr, nullptr };
6
if (array[3] != NULL)
7
  TestRef(*array[3]);
PS: "NULL" ist deprecated und sollte durch nullptr ersetzt werden. 
Dieses ist auch zu jedem Pointertyp konvertierbar.

von arc net (Gast)


Lesenswert?

Dr. Sommer schrieb:
> arc net schrieb:
>> nichts leichter als das...
> Das ist undefiniertes Verhalten. Bevor man einer Referenz einen
> dereferenzierten Pointer zuweist muss man sie natürlich auf Gültigkeit
> prüfen

Selbst dann kann später im Programm irgendetwas die Elemente des Arrays 
aus dem Beispiel auf Null setzen...

von Dr. Sommer (Gast)


Lesenswert?

arc net schrieb:
> Selbst dann kann später im Programm irgendetwas die Elemente des Arrays
> aus dem Beispiel auf Null setzen...
Das ändert nichts, da die Referenz nichts mehr mit dem Array zu tun hat, 
sondern auf das Ziel des Pointers aus dem Array verweist - den hast du 
ja schließlich dereferenziert, er ist also "verschwunden".

von Mitleser (Gast)


Lesenswert?

Immer wieder nett wie die Experten sich kabbeln und jeder für sich das 
absolute Wissen in Anspruch nimmt. Wirklich drollig.

von (prx) A. K. (prx)


Lesenswert?

Mitleser schrieb:
> Immer wieder nett wie die Experten sich kabbeln und jeder für sich das
> absolute Wissen in Anspruch nimmt. Wirklich drollig.

Das muss so sein. Wenn alle einer Meinung sind, ist etwas faul.

von Dr. Sommer (Gast)


Lesenswert?

Mitleser schrieb:
> Immer wieder nett wie die Experten sich kabbeln und jeder für sich das
> absolute Wissen in Anspruch nimmt. Wirklich drollig.
Der Vorteil bei dieser Art Diskussion ist, dass man einfach ausprobieren 
kann wie es richtig ist :o)
http://xkcd.com/386/

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.