Forum: Compiler & IDEs C Frage Zeiger bzw. Variablen Kopie


von paulchen (Gast)


Lesenswert?

Hallo,
hab mal ne Verständnisfrage:


Müsste doch beides gleich funktionieren doch was ist besser?

Version a:
1
// rechne Variable +5
2
int8_t test = 5;
3
4
test = addiere5_Copy(test);
5
        
6
7
8
int8_t addiere5_Copy(int8_t variable)
9
{
10
  int8_t return_value=0;
11
  return return_value = variable +5;
12
}
--> Ergebnis 10

Version b:
1
// rechne Variable +5
2
int8_t test = 5;
3
4
addiere5_Zeiger(&test);
5
6
void addiere5_Zeiger( int8_t *variable  )
7
{
8
       *variable +=5;
9
}
--> Ergebnis 10


Gruß paulchen

von Dr. Sommer (Gast)


Lesenswert?

paulchen schrieb:
> int8_t addiere5_Copy(int8_t variable)
> {
>   int8_t return_value=0;
>   return return_value = variable +5;
> }
Das ist doch total sinnlos, warum nicht
1
int8_t addiere5_copy (int8_t variable) {
2
  return variable + 5;
3
}
Ansonsten sind beide Varianten zwar funktional identisch, aber der 
generierte Maschinencode sieht etwas anders aus. Generell ist die 1. 
Version zu bevorzugen, weil bei dieser ganz genau klar ist, was Eingabe 
& Ausgabe ist ("variable" ist Eingabe, Rückgabewert ist Ausgabe) und bei 
der zweiten der Parameter doppeltgenutzt wo nicht immer so 
offensichtlich ist was passiert.
Parameter sind für Eingaben, Rückgabewerte für Ausgaben.

Die 1. Version ist in diesem Fall vermutlich auch minimal effizienter...

von paulchen (Gast)


Lesenswert?

Hallo,
Danke für die Erklärung.

>Das ist doch total sinnlos, warum nicht
>int8_t addiere5_copy (int8_t variable) {
>  return variable + 5;
>}

Ja das ist klar sollte nur ein Beispiel sein.

Gruß paulchen

von fop (Gast)


Lesenswert?

Wie Du schon richtig geschreiben hast, wird in der ersten Variante eine 
Kopie der Variable für die Funktion erstellt. Bei einem Byte geht das 
ratz fatz und braucht bestimmt weniger Platz als ein Zeiger. Ausserdem 
pflichte ich meinem Vorredner bei, dass klarer ist was in die Funktion 
nur rein gehen kann und sich dadurch nicht ändert und wo etwas aus der 
Funktion raus kommt.
Variante 2 kommt erst wieder zum Zug, wenn nicht ein Byte, sondern 
Hunderte bis Tausende übergeben werden sollen und Ausführungszeit oder 
Datenspeicherplatz ein kanppes Gut sind. Dann werden nämlich auf einmal 
Hunderte bis Tausende von Bytes kopiert. Das Kopieren braucht Zeit und 
die Werte stehen doppelt im Speicher. Dagegen ist ein Zeiger, der 
übergeben wird eher klein.
Problematisch ist dabei, dass die Funktion Zugriff auf das Original der 
Eingangsdaten hat und daran herumfummeln könnte. C bietet zwar die 
Möglichkeit einen Zeiger auf eine Konstante zu definieren, aber wenn man 
Mikrocontroller programmiert hat man u.U. einen Compiler der über die 
Frage konstant oder nicht nicht versucht den Adressraum (Programm für 
Konstante; Daten für Variablen) zu bestimmen. Da bleibt also nur 
entweder im Büro eine Guillotine zu errichten und anzudrohen, dass jeder 
drauf landet, der in so einer Funktion schreibend auf die Eingabedaten 
zugreift oder einen leistungsfähigeren Mikrokontroller einzusetzen, bei 
dem das Kopieren nicht auffällt.

von Dr. Sommer (Gast)


Lesenswert?

fop schrieb:
> C bietet zwar die
> Möglichkeit einen Zeiger auf eine Konstante zu definieren, aber wenn man
> Mikrocontroller programmiert hat man u.U. einen Compiler der über die
> Frage konstant oder nicht nicht versucht den Adressraum (Programm für
> Konstante; Daten für Variablen) zu bestimmen.
Das bedeutet also bei einem solchen Compiler kann man einen non-const 
Pointer nicht in einen const-Pointer konvertieren, weil der Zugriff auf 
const-Pointer anders geregelt wird? Das wäre dann aber weit weg von C 
und geradezu gefährlich. Funktionen wie memcpy, strlen etc. kann man 
dort dann also auch nicht verwenden. Gibt es tatsächlich Compiler, die 
so etwas machen?!
Der umgekehrte Fall sieht natürlich ganz anders aus, wenn der Compiler 
eine Konstante in Read-Only-Memory legt und man bei einem Pointer darauf 
das "const" wegcastet und dann darauf schreibt - da ist es ok wenn es 
nicht funktioniert.

von Klaus W. (mfgkw)


Lesenswert?

Dr. Sommer schrieb:
> Das bedeutet also bei einem solchen Compiler kann man einen non-const
> Pointer nicht in einen const-Pointer konvertieren, weil der Zugriff auf
> const-Pointer anders geregelt wird? Das wäre dann aber weit weg von C
> und geradezu gefährlich. Funktionen wie memcpy, strlen etc. kann man
> dort dann also auch nicht verwenden. Gibt es tatsächlich Compiler, die
> so etwas machen?!

non-const in const casten geht natürlich.
Nur andersrum geht es nur in Einzelfällen und nicht unbedingt portabel.

Z.B. void *memcpy(void *dest, const void *src, size_t n):
Da darf als dest kein const-Pointer angegeben werden, auch wenn es bei 
manchen Systemen durch den Compiler geht und vielleicht sogar 
funktioniert - es geht aber dann nur auf eigene Gefahr.
Richtig wäre es nur, Daten, die ursprünglich nicht-const sind, dort zu 
übergeben (bzw. einen Zeiger auf solche).

Für src dagegen garantiert das obige const nur, daß die Funktion memcpy 
über den Zeiger nichts verändert.
Der Aufrufer darf aber auch nicht-const-Zeiger übergeben, die Quelle muß 
also nicht const sein.

Hier ist also das nicht-const von dest einschränkender als das const von 
src.

Was fop wohl meint mit der unterschiedlichen Behandlung von Const und 
nicht-const je nach Compiler, ist nicht das Herumreichen von Zeigern, 
sondern die ursprüngliche Anlage von Daten in einem schreibgeschützten 
oder beschreibbaren Bereich.
Das wird im Nachhinein durch das Herumreichen von Zeigern natürlich 
nicht mehr geändert, auch wenn man mehr oder weniger frei const dazu 
oder wegcasten kann.

Beispiel:
1
   const char *p = "Hallo";
2
   *p = 'X';          // Compiler sagt: geht nicht, weil p ist const char*
3
   *(char*)p = 'X';   // mag für den Compiler ok sein, scheitert evtl. zur Laufzeit
4
...
5
   char *q  = "Hallo";
6
   *q = 'Y';   // mag für den Compiler ok sein, scheitert evtl. zur Laufzeit

Ob das Überschreiben des 'H', auf das p zeigt, zur Laufzeit klappt, 
hängt vom System ab: wenn die Konstante in schreibbarem RAM liegt, fällt 
der Fehler gar nicht auf. Auf anderen Systemen kann es einen 
Programmabbruch geben.

Interessant ist, daß die letzte Zeile mit *q auf manchen Systemen sogar 
den String verändert, auf den p zeigt, nämlich dann, wenn der Compiler 
nicht mit der Blödheit vieler Programmierer rechnet. Da "Hallo" eine 
Konstante ist, darf man sie nicht über Zeiger ändern (auch wenn es 
gelegentlich klappt), und ein halbwegs intelligenter Compiler legt 
gleiche Stringkonstanten aus Geiz nur einmal an.

Das Casten zwischen const und nicht-const wird häufig falsch gemacht und 
zeigt schnell, wer Herumpfuschen als Programmieren verkauft.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> fop schrieb:
>> C bietet zwar die
>> Möglichkeit einen Zeiger auf eine Konstante zu definieren, aber wenn man
>> Mikrocontroller programmiert hat man u.U. einen Compiler der über die
>> Frage konstant oder nicht nicht versucht den Adressraum (Programm für
>> Konstante; Daten für Variablen) zu bestimmen.
> Das bedeutet also bei einem solchen Compiler kann man einen non-const
> Pointer nicht in einen const-Pointer konvertieren, weil der Zugriff auf
> const-Pointer anders geregelt wird? Das wäre dann aber weit weg von C
> und geradezu gefährlich. Funktionen wie memcpy, strlen etc. kann man
> dort dann also auch nicht verwenden. Gibt es tatsächlich Compiler, die
> so etwas machen?!

Das ist übliche Praxis: (nicht-volatile) readonly-Objekte werden nach 
.rodata gelegt, andere nach .data oder .bss.  Bei relocatiblem Code 
heißen die Sections anders, aber im Endeffekt läuft es aufs gleiche 
hinaus.

Für die Anwendung muß das aber 100% transparent sein, d.h. ein Zugriff 
auf .rotata sieht genauso aus wie einer auf .data.  Evtl. unterscheiden 
sich die Zugriffe im Timing, aber darüber macht der Sprachstandard eh 
keine Aussage.

Übrigens legt auch avr-gcc const Objekte nach .rodata, allerdings legt 
das Standard-Linkerscript .rodata in den RAM — eben weil nur so 
sichergestellt ist, daß es 100% transparent ist.  Es gibt auch Devices, 
die den Flash in den RAM-Adressbereich mappen.  Hier könnte man .rodata 
ins Flash legen und Linkerscript und Startup-Code darauf anpassen.  Aber 
selbst dann bleibt es für die Applikation transparent, da all zugriffe 
gleich aussehen denn für den Flash-Zugriff wird eben kein LPM mehr 
gebraucht.

> Der umgekehrte Fall sieht natürlich ganz anders aus, wenn der Compiler
> eine Konstante in Read-Only-Memory legt und man bei einem Pointer darauf
> das "const" wegcastet und dann darauf schreibt - da ist es ok wenn es
> nicht funktioniert.

Jo, steht auch im C-Standard drinne das dies undefiniertes Verhalten 
bedeutet.

von Dr. Sommer (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> non-const in const casten geht natürlich.
> Nur andersrum geht es nur in Einzelfällen und nicht unbedingt portabel.

Ja, genau das meinte ich ja...

Johann L. schrieb:
> Für die Anwendung muß das aber 100% transparent sein, d.h. ein Zugriff
> auf .rotata sieht genauso aus wie einer auf .data.
Ja.

Johann L. schrieb:
> Hier könnte man .rodata
> ins Flash legen und Linkerscript und Startup-Code darauf anpassen.  Aber
> selbst dann bleibt es für die Applikation transparent, da all zugriffe
> gleich aussehen denn für den Flash-Zugriff wird eben kein LPM mehr
> gebraucht.
Ja, zB alle ARM-Controller. Das ist der große Vorteil von vermutlich 
allen(?) 32bit-Controllern, weil deren Adressraum groß (4GB) genug ist 
für Flash, RAM, I/O, s.d. man alles mit den selben Instruktionen 
abhandeln kann. An eine Funktion die ein "const char*" nimmt kann man so 
flash-strings und RAM-Strings übergeben...

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.