www.mikrocontroller.net

Forum: PC-Programmierung Zeigerarithmetik und Dereferenzierung


Autor: Rüdiger Britzen (ruedigerbritzen)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Frank (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Tobi H. (tobi-) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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 ;)

Autor: Rolf Magnus (Gast)
Datum:

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

Autor: Rüdiger Britzen (ruedigerbritzen)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Frank (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Tobi H. (tobi-) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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;

Autor: Tobi H. (tobi-) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Korrektur:
Version 1:
ft_read(... ,gBuffer,... )
             ^

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
}

Autor: Rüdiger Britzen (ruedigerbritzen)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Frank (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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';

Autor: Frank (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rüdiger Britzen (ruedigerbritzen)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.