Forum: PC-Programmierung Zeigerarithmetik und Dereferenzierung


von Rüdiger B. (ruedigerbritzen)


Angehängte Dateien:

Lesenswert?

Moin Forum,

so, nachdem ich jetzt zwei Abende vergeblich geknobelt habe und zu
keinem Ergebnis komme, möchte ich euch an meinem (wahrscheinlich ist es
gar keins) Problem teilhaben lassen. Ich übergebe aus der Funktion
ReadData einen Zeiger auf ein Array aus unsigned chars. Diesen Zeiger
versuche ich dann in der Funktion OnPollTimer zu dereferenzieren, um
die empfangenen Characters weiterzuverarbeiten. Das Auslesen in
ReadData funktioniert, Buffer hat den richtigen Inhalt. Aber das
Dereferenzieren in OnPollTimer bringt nur Grütze zustande. Sieht jemand
von euch, was ich da falsch mache? Besten Dank!

Gruß, Rüdiger

von Frank (Gast)


Lesenswert?

Ha! zu so später Stunde kann ich sogar mit meinen leicht verstaubten
C++-Kenntnissen noch punkten (hoff ich zumindest).

Du erzeugst in ReadData() den Puffer auf dem Stack und gibst dann den
Zeiger darauf zurück. Nachdem die Funktion wieder verlassen wird, ist
der Zeiger nicht mehr gültig. Du musst den Puffer mit new auf dem Heap
erzeugen und (unter Umständen wichtig) mit delete wieder löschen, wenn
Du ihn nicht mehr brauchst (sonst hast Du schon ein Memory Leak).
Alternativ eine Ebene höher deklarieren, z.B. global.

HTH

von Tobi H. (tobi-) Benutzerseite


Lesenswert?

Stimmt genau.
Nie nie nie und nimmer Zeiger auf lokale Variablen aus einer Funktion
zurückgeben. Das ist in c(++) eine absolute Todsünde ;)

von Rolf Magnus (Gast)


Lesenswert?

Ja, das ist das Problem. Der Buffer ist lokal in der Funkton definiert.
Sobald die Funktion verlassen wird, existiert er nicht mehr.

von Rüdiger B. (ruedigerbritzen)


Angehängte Dateien:

Lesenswert?

Vielen Dank für die schnellen Antworten, aber es funzt leider immer noch
nicht.

Ich habe nun einen globalen Zeiger namens PointerToReadBuffer
deklariert. Die Funktion ReadData gibt jetzt keinen Zeiger mehr zurück,
sondern kopiert die Adresse des ersten Array-Elements auf diesen
globalen Zeiger. Die Funktion OnPollTimer klappt jetzt in der ersten
Variante wunderbar, aber nicht in der Variante zwei. Sobald die
for-Schleife betreten wird und das erste Mal die Funktion IntToStr
aufgerufen wird, zeigt der PointerToReadBuffer wieder nur auf Murks...
Warum das so ist, kann ich mir leider nicht erklären. Habt ihr da noch
Vorschläge?

Gruß, Rüdiger

von Frank (Gast)


Lesenswert?

Also nochmal: Du darfst den Puffer nicht lokal erzeugen! Wie Du den
Zeiger zurückgibst ist vollkommen egal. Der Puffer muss global
(unschön)  oder mit new (besser aber schwieriger -> Memory Leak)
erzeugt werden.

von Tobi H. (tobi-) Benutzerseite


Lesenswert?

Der Fehler ist immer noch in ReadData. Du hast immer noch einen Zeiger
auf eine lokale Variable. Ob du den pointer als Rückgabewert hast oder
als globale Variable übergibt ist egal. Das entscheidene ist, wo sich
der Speicherplatz befindet, auf den der Zeiger zeigt. Und das ist immer
noch lokal in der ReadData. Du solltest entweder im Hauptprogramm keinen
Pointer, sondern den Buffer anlegen (global z.B unsignec char
Buffer[128], und keinen lokalen Buffer mehr verwenden) oder einen neuen
Speicherbereich mittels new in ReadData reservieren und darauf einen
Pointer zurückgeben. Nach verwendung im Hauptprogramm dann mit delete
löschen.

Version 1:
..
unsigned char gBuffer[128];

void ReadData(..) {
ft_read(... ,Buffer,... )
..}


Version 2:
unsigned char* ReadData(..) {
unsigned char*buffer=new unsigned char[128];
ft_read(..., buffer, ...)
return buffer;
}

OnPolltimer {
unsigned char*buffer = readData()
... buffer benutzen
delete[] buffer;

von Tobi H. (tobi-) Benutzerseite


Lesenswert?

Korrektur:
Version 1:
ft_read(... ,gBuffer,... )
             ^

von Karl H. (kbuchegg)


Lesenswert?

Um es nochmal ganz klar und eindeutig zu sagen:

Lokale Variablen existieren nicht mehr, nachdem eine
Funktion verlassen wurde.
Hast du also eien Funktion:

void foo()
{
  unsigned char Buffer[20];

  ...
}

dann wird die Variable Buffer nach verlassen der Funktion
wieder in Luft aufgelöst. Es gibt sie einfach nicht mehr.
Daran ändert sich auch nichts, wenn es einen Pointer auf
diese Variable gibt. Der Pointer zeigt dann ins Nirwana
aber nicht mehr auf die Variable 'Buffer'. Denn die gibt
es ja nicht mehr.

Wenn dein Aufrufer Zugang zu diesem Buffer braucht, dann ist
die einfachste Variante die, dass der Aufrufer den Buffer
zur Verfügung stellt:

void foo( char* Buffer, int BufferLen )
{
  // foo schreibt was in den Buffer
  // ausserdem ist es immer gut, wenn man, wie in diesem
  // Fall dem foo() auch mitteilt wie gross den das
  // übergebene Array tatsächlich ist. Dann kann foo()
  // dafür sorgen, dass nichts zerstört wird

  if( BufferLen > 2 ) {
    Buffer[0] = 'a';
    Buffer[1] = 'b';
    Buffer[2] = '\0';
  }
}

void bar()
{
  char Buffer[20];
  foo( Buffer, sizeof( Buffer ) );

  ... Hier kann dann bar() mit den Werten arbeiten, die
  ... foo im Buffer hinterlassen hat
}

von Rüdiger B. (ruedigerbritzen)


Lesenswert?

Hey klasse, es funktioniert! Danke an alle, insbesondere für die
verständlichen Erklärungen.

Grr, in Ada erscheint mir das Zeigerkonzept aus sich heraus irgendwie
schlüssiger zu sein. Insofern noch eine Verständnisfrage zur Version 2
von Tobi (die jetzt so auch bei mir läuft): Buffer ist ein Zeiger auf
einen unsigned char. Dieser Zeiger zeigt auf den Anfang eines
Speicherbereiches, der Platz für 128 unsigned chars bietet. Die
Funktion ftRead möchte ja nun dieses Array übergeben bekommen, um darin
etwas speichern zu können. Eine Derefenzierung mittels "ft_read(...,
*buffer, ...)" macht keinen Sinn, da dies ja nur den Inhalt der ersten
Speicherzelle darstellt. Unabhängig davon ob Buffer nun gemäß "unsigned
char Buffer[128]" oder aber nach "unsigned char*buffer=new unsigned
char[128]" definiert ist, ist der Aufruf von "ft_read(..., buffer,
...)" gültig. Warum ist das so?

Gruß, RÜdiger

von Rolf Magnus (Gast)


Lesenswert?

Meinst du, weil im einen Fall der Buffer ein Array ist, im anderen Fall
ein Zeiger?
In vielen Fällen "zerfällt" ein Array in einen Zeiger auf sein erstes
Element. Daher kann man oft (aber nicht immer) ein Array wie einen
Zeiger nutzen. Ein Funktionsaufruf ist ein Fall, in dem bei Übergabe
eines Array immer automatisch ein Zeiger auf das erste Element des
Arrays in der Funktion ankommt.

von Frank (Gast)


Lesenswert?

Hinter dem Namen des Arrays verbirgt sich nichts anderes als ein Zeiger
auf das erste Element. Wie Du die Dereferenzierung vornimmst ist
vollkommen egal. Buffer[0] ist identisch zu *Buffer, genauso wie
Buffer[n] identisch zu *(Buffer+n) ist. Man kann das beliebig mischen:

char * Buffer = new char[128];
...
Buffer[123] = 16;

oder auch

char Buffer[128];
...
*(Buffer+12) = 'c';

von Frank (Gast)


Lesenswert?

kurzer Nachtrag: Wenn Du mit new Speicher vom Heap anforderst, ist es
zwingend erforderlich, dass Du diesen wieder freigibst. Macht sich bei
Deinem Problem vielleicht noch nicht bemerkbar, aber irgendwann wird es
das. Also den Speicher unbedingt wieder mit

delete [] Buffer;

freigeben! Natürlich erst, wenn Du ihn nicht mehr brauchst.

von Rüdiger B. (ruedigerbritzen)


Lesenswert?

So erstmal sorry für die späte Antwort.

@Magnus: Ja, genau das meinte ich. Für mich ist das unrund, wie gesagt
in Ada ist ein Zeiger ein Zeiger und ein Array ist ein Array. Und ein
Zeiger auf ein Array ist etwas ganz anderes als das Array selbst.

@Frank: Vielen Dank!

Gruß, Rüdiger

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.