Forum: Compiler & IDEs Übergebene Variablen in Unterprogramm ändern?


von E.M. (Gast)


Lesenswert?

Hallo,

ich habe ein kleines Problem. Ein Unterprogramm soll zwei Variablen 
verändern, die ich ihr übergebe. Da viele verschiedene Variablen damit 
geändert werden sollen, kann ich keine globalen Variablen benutzen.
1
unsigned char type, length;
2
3
get_info(type, length);
4
5
// Hier sollen type und length nun verändert sein
6
7
[...]
8
9
void get_info(type, length)
10
{
11
    type = 0x15;
12
    length = 0xFF;
13
}

Ich glaube da muss man irgendwie mit Pointern rumfuchteln .. aber ich 
bin mir nicht sicher.

Bitte nicht schlagen, bin noch C-Anfänger.

E.M.

von Gerd G. (gege)


Lesenswert?

> Ich glaube da muss man irgendwie mit Pointern rumfuchteln ..

Genau. Du mußt Der Funktion nicht die Werte, sondern Adressen der 
Variablen übergeben, also statt "get_info(type, length);" muß es sein 
"get_info(&type, &length);".

In der Funktion kannste auf die Werte der übergebenen Variablen über 
"dereferencing" zu greifen, *type liefert z.B. den Wert von type.

Am besten ein C pointer tutorial Deiner Wahl durchlesen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das geht so:
1
unsigned char type, length;
2
3
get_info(&type, &length);
4
5
6
// Hier sollen type und length nun verändert sein
7
8
[...]
9
10
void get_info(unsigned char *type, unsigned char *length)
11
{
12
    *type = 0x15;
13
    *length = 0xFF;
14
}

Literaturhinweis:

"Programmieren in C", Brian Kernighan & Dennis Ritchie, Hanser-Verlag

von Peter D. (peda)


Lesenswert?

Es geht auch mit ner Struktur aus 2 Variablen (oder mehr).

Das hat den Vorteil, daß der Compiler besser optimieren kann 
(Registervariablen statt Pointer auf SRAM).
1
struct type_length {
2
  unsigned char type;
3
  unsigned char length;
4
};
5
6
7
struct type_length get_info( struct type_length val )
8
{
9
  val.type = 0x15;
10
  val.length = 0xFF;
11
  return val;
12
}
13
14
15
unsigned char test( void )
16
{
17
  struct type_length x;
18
19
  x.type = 10;
20
  x.length = 11;
21
  x = get_info( x );
22
  return x.type + x.length;
23
}


Peter

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das ist bei größeren Strukturen sehr ineffizient.

Hier wird mehrfach die gesamte Struktur kopiert, zunächst beim 
Funktionsaufruf auf den Stack, danach bei der Zuweisung des 
Rückgabewertes der Funktion an die Variable x.

Gerade hier ist die Verwendung von Pointern sehr zu empfehlen, weil da 
gar nichts kopiert werden muss.

von Peter D. (peda)


Lesenswert?

Rufus t. Firefly wrote:
> Das ist bei größeren Strukturen sehr ineffizient.

Kann man nicht so pauschal sagen.

Der GCC kann das sehr gut optimieren.

Der obige Code compiliert zu:
1
struct type_length get_info( struct type_length val )
2
{
3
  val.type = 0x15;
4
  val.length = 0xFF;
5
  return val;
6
}
7
  ce:   85 e1           ldi     r24, 0x15       ; 21
8
  d0:   9f ef           ldi     r25, 0xFF       ; 255
9
  d2:   08 95           ret
Besser gehts nicht mal in Assembler.

Aber auch bei mehr Elementen, wenn die Register nicht mehr reichen, wird 
es zumindest nicht größer als die Pointervariante.


Im Gegensatz dazu mit Pointer:
1
void get_info1(unsigned char *type, unsigned char *length)
2
{
3
  ce:   fc 01           movw    r30, r24
4
  d0:   db 01           movw    r26, r22
5
    *type = 0x15;
6
  d2:   85 e1           ldi     r24, 0x15       ; 21
7
  d4:   80 83           st      Z, r24
8
    *length = 0xFF;
9
  d6:   8f ef           ldi     r24, 0xFF       ; 255
10
  d8:   8c 93           st      X, r24
11
}
12
  da:   08 95           ret


Peter

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Und was geschieht, wenn die Struktur nicht mehr in ein paar Register 
passt? Dann muss sie auf den Stack geschoben werden, und da fängt die 
Kopiererei an.

von Peter D. (peda)


Lesenswert?

Rufus t. Firefly wrote:
> Und was geschieht, wenn die Struktur nicht mehr in ein paar Register
> passt? Dann muss sie auf den Stack geschoben werden, und da fängt die
> Kopiererei an.

Der GCC mag seine Schwächen haben, aber oftmals ist er sehr clever.

Auch mit der Struktur im RAM ist er besser, da er dann nur über einen 
Base-Pointer mit Displacement (0..63) zugreift.
Das ist natürlich viel effektiver, als für jede Variable ein extra 
Pointer.


Und bei vielen Parametern ist die Pointerversion erst recht Schwachsinn. 
Dann müssen ja nicht nur die Parameter, sondern auch die einzelnen 
Pointer im RAM abgelegt werden.

Viele Paramter übergibt man daher nicht über einzelne Pointer, sondern 
über eine Struktur oder ein Array (= Struktur aus gleichen Elementen).


Peter

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> Viele Paramter übergibt man daher nicht über einzelne Pointer,
> sondern über eine Struktur oder ein Array (= Struktur aus
> gleichen Elementen).

Ja. Aber man übergibt nicht die Struktur, sondern einen Pointer darauf. 
Eine Kopie der Struktur zu erzeugen, um diese einer Funktion zu 
übergeben und diese Kopie nach Beendigung der Funktion wieder anderswo 
hinzukopieren, das ist nicht effizient.

von E.M. (Gast)


Lesenswert?

Vielen vielen Dank für eure Tipps!

Ich habe es jetzt mit Pointern auf eine Struct realisiert. Das finde ich 
viel eleganter als meine anfängliche Idee.

E.M.

von Peter D. (peda)


Lesenswert?

Rufus t. Firefly wrote:

> Ja. Aber man übergibt nicht die Struktur, sondern einen Pointer darauf.
> Eine Kopie der Struktur zu erzeugen, um diese einer Funktion zu
> übergeben und diese Kopie nach Beendigung der Funktion wieder anderswo
> hinzukopieren, das ist nicht effizient.

Wenn man ihn mit Optimierung -O0 dazu zwingt, wird er eine Kopie 
anlegen.

Ansonsten wird er schon selber den optimalen Zugriff finden, egal wie 
man es hinschreibt.
Solche Zugriffsoptimierungen sind ja Brot und Butter jedes C++ 
Compilers.


Peter

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> Ansonsten wird er schon selber den optimalen Zugriff finden,
> egal wie man es hinschreibt.

Das nenne ich Gottvertrauen. Wenn der Code der Funktion und der Code des 
Aufrufs in unterschiedlichen Übersetzungseinheiten zu finden sind, dann 
dürfte der Optimierer recht wenig Chancen haben.

von Peter D. (peda)


Lesenswert?

Rufus t. Firefly wrote:
> Das nenne ich Gottvertrauen. Wenn der Code der Funktion und der Code des
> Aufrufs in unterschiedlichen Übersetzungseinheiten zu finden sind, dann
> dürfte der Optimierer recht wenig Chancen haben.

Nö, mit Gottvertrauen hat das nichts zu tun.

Der Compiler hat feste Regeln, wie er Argumentenlisten verarbeitet, wenn 
er sie nicht mehr direkt in Registern übergeben kann. Dann muß er einen 
Pointer übergeben.
Er arbeitet also schon mit Pointern, auch wenn man es nicht als Pointer 
hinschreibt.

Ob er nun eine Kopie anlegt oder sich nur intern merkt, daß die 
Originaldaten read-only sind, müßte jemand ausprobieren, der Lust dazu 
hat.


Peter

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.