Forum: Compiler & IDEs C-Funktion call by reference: beliebiger Datentyp


von Sascha (Gast)


Lesenswert?

Hallo,

ich habe noch ein Problem:

eine Funktion liest 256 Words vom I/O ein. Der Funktion wird ein Zeiger
eines Arrays vom Typ uint16_t übermittelt. Die eingelesenen Daten werden
dort reingeschrieben.

Also z.B.:

void readio(uint16_t *Buffer) {
  for(i=0; i<256; ++i) {
    *Buffer=readwordio();
    Buffer++;
  }
}


Das funktioniert auch wunderbar:

uint16_t IrgendeineVar[256];
readio(IrgendeineVar);


Das Problem ist, dass ich manchmal Statusdaten einlesen will. Die
möchte nicht in ein uint16_t Var[256] bekommen, sondern in ein

typedef struct statusdaten {
  uint16_t Status1;
  uint16_t Status2;
...
};

struct statusdaten IrgendeineVar;
readio(IrgendeineVar);

funktioniert ja nicht.


Ist es möglich, dass man der Funktion Zeiger eines beliebigen Datentyps
übergeben kann (vorrausgesetzt die Länge der Datentypen ist gleich)?

von Thilo Wawrzik (Gast)


Lesenswert?

Also zur Not gibt es da immernoch ein void*.
Damit kann man eigentlich alles übergeben ...

1 > void readio(uint16_t *Buffer) {
2 >   for(i=0; i<256; ++i) {
3 >     *Buffer=readwordio();
4 >     Buffer++;
5 >   }
6 > }

Das Problem liegt bei Dir in Zeile 4.
Der ++Operator benötigt die Größe des Feldes, damit er
weiß, wie weit er gehen soll :-)
Es gibt also 2 Möglichkeiten:
1. in readio() wieder mit typ (z.B. uint16_t) casten
und anschließend ++ benutzen.
2. statt ++ die Adresse von Buffer selber um die Länge
des Types erhöhen. (sizeof())
Falls Du structs verwenden willst, geht eigentlich
nur Methode 2. Besser wäre meiner Ansich nach z.B.
ein case in readio() mit zwei verschiedene Routinen -
A für Daten, B für Statusinfos.

Könnte sein, dass ein "schlauer" Compiler, dann eh das
tut, was Du ihm aufzwingen willst. Keine Ahnung.
Aber Du bist erstmal auf der sicheren Seite ...

von Chris (Gast)


Lesenswert?

Halb-OT: Wenn man ganz korrekt ist, kennt C gar kein 'call by
reference', ausschließlich 'call by value'. Funktions-Parameter
werden in jedem Fall als Wert, also als Kopie, übergeben. Ob der
Parameter ein int oder ein Zeiger ist, spielt keine Rolle.

Echtes 'call by reference' kennt erst C++:
void foo(int& i);

int i = 2;
foo(i);

Hier wird der Parameter wirklich als Referenz übergeben, eine Kopie
findet (semantisch) nirgendwo statt.

von Rufus T. Firefly (Gast)


Lesenswert?

Diese Referenzen sind das, was mir an C++ unangenehm auffällt (und nicht
nur dort, in Pascal* ist da ja auch schon so gewesen).

Beim Funktionsaufruf foo(i) kann man -ohne sich den Prototypen von
foo() näher anzusehen- nicht herausfinden, ob i verändert wird oder
nicht.
Da ist mir die "gute alte" Pointer-Schreibweise doch deutlich
lieber.
Bei der ist es eindeutig, was geschieht, ohne daß man den Prototypen
ansehen muss:

  foo(&i);

Auch führt hier eine einseitige Änderung des Funktionsprototypens oder
des Funktionsaufrufes zu einem Fehler - was bei einer Referenz nicht
der Fall wäre.

Allerdings lässt sich das Überladen der Operatoren nicht ohne
Referenzen implementieren; daher sind die Dinger gerechtfertigt.


*) ich muss das nicht mögen. "Wer nichts wird, wird Wirth"
(trollbait)

von Chris (Gast)


Lesenswert?

> Allerdings lässt sich das Überladen der Operatoren nicht ohne
> Referenzen implementieren; daher sind die Dinger gerechtfertigt.

Nicht nur das, auch ein Copy-Konstruktor wäre ohne Referenzen schwer
möglich.

Ich finde, bei Referenzen überwiegen die Vorteile eindeutig die
Nachteile.
Zum Beispiel Klassen (etwa einen großen string) werden in C++ selten by
value übergeben, einfach aus dem Grund, weil das Kopieren "ewig"
dauern würde. Dafür gibts die schöne Schreibweise foo(const string&)
anstelle foo(string).
In C müsste man in so einem Fall gleich wieder mit Zeigern hantieren,
was den Funktionsaufruf doch etwas unintuitiver macht.


...aber das war ja gar nicht die Frage des OPs, merk' ich gerade. ;-)

von Bartli (Gast)


Lesenswert?

Tatsächlich ist es nicht allzu schlimm, wenn man einen C++ string by
value übergibt, weil das eigentliche string Objekt nur einen Zeiger auf
die eigentlichen Zeichendaten enthält.

Bei einer Zuweisung von einem string zu einem anderen hat man dann zwei
strings, welche auf dieselben Zeichendaten verweisen. Das ist kein
Problem, weil die string Objekte mit Hilfe von Reference Counting
wissen, wieviele Objekte auf die Zeichendaten zeigen. Wird eine
Operation auf einem string ausgeführt, welche die Zeichendaten ändert,
legt der string eine eigene Kopie der Zeichendaten an, falls mehr als
ein string auf sie verweist (Copy on Write Schema).

Dies ist natürlich kein Grund, keine konstanten Referenzen zu
verwenden, wenn möglich.

von Chris (Gast)


Lesenswert?

> Tatsächlich ist es nicht allzu schlimm, wenn man einen C++ string
> by value übergibt, weil das eigentliche string Objekt nur einen
> Zeiger auf die eigentlichen Zeichendaten enthält.

Natürlich enthält ein string nur einen Zeiger auf die Daten. Ein
normaler C-String ist nunmal ein char*, also ein normaler Zeiger.

Allerdings wird beim Kopieren eines string-Objekts nicht nur dieser
Zeiger kopiert (flat copy), sondern die ganzen Daten (deep copy).

Das, was du beschreibst, nennt man auch "lazy operations", also
verzögert ausgeführte Operationen. Allerdings ist mir keine
string-Implementation bekannt, die diese Methode benutzt.


p.s.: grr Jetzt durfte ich diesen Beitrag wieder mehrmals schreiben,
weil das Forum mein Posting nicht immer beim ersten Mal annimmt.

von Bartli (Gast)


Lesenswert?

>Natürlich enthält ein string nur einen Zeiger auf die Daten. Ein
>normaler C-String ist nunmal ein char*, also ein normaler Zeiger.

Ich rede nicht von C-Strings, sondern von der Klasse string (bzw. der
Templateklasse basic_string) der C++ Library.

Soweit ich weiss arbeitet z.B. die Implementation der libstdc++ mit
einem Verfahren ähnlich dem das ich oben beschrieben habe.

von Chris (Gast)


Lesenswert?

> Ich rede nicht von C-Strings, sondern von der Klasse string (bzw. der
> Templateklasse basic_string) der C++ Library.

Ich auch.

Ok, meine Aussage war vielleicht etwas missverständlich. Einige
string-Implementationen benutzen aber soweit ich weiß intern C-Strings,
also char*. Dadurch vereinfacht sich dann std::string::c_str() ziemlich,
aber auf Kosten anderer string-Methoden.

sizeof(std::string) ist bei jeder Implementation konstant, allein aus
dem Grund muss die string-Klasse ja schon einen Zeiger auf den
eigentlich String enthalten. So hab ich das gemeint. :)

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.