Forum: Mikrocontroller und Digitale Elektronik C-Problem Funktionsaufruf


von Joachim .. (joachim_01)


Lesenswert?

Hi,
hab hier n C-Problem, bin wohl zu lange aus der Übung...
Ich möchte in einer Test-Funktion nen String erzeugen und dann in main 
auf nem Standard 4x20LCD ausgeben.

Etwa so:
in test:
char * test(void) {
char *Xstring = "abc";
char Ystring[3] = "ABC";
return Xstring;
// return Ystring;
}




in main:

strcpy (buffer, test());
putsXLCD (buffer);




Das ganze läuft auf nem PIC 18 mit MPLAB C18. Es wird nur Speichermüll 
oder gar nichts angezeigt.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Die Funktion gibt einen Pointer auf lokalen Speicher zurück, also auf 
einen Speicherbereich, der nach Beenden der Funktion nicht mehr zur 
Verfügung steht.

von Stefan E. (sternst)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Die Funktion gibt einen Pointer auf lokalen Speicher zurück, also auf
> einen Speicherbereich, der nach Beenden der Funktion nicht mehr zur
> Verfügung steht.

Nicht die gepostete Variante.

von radiostar (Gast)


Lesenswert?

XString ist eine lokale Variable, die hat nur in der Funktion test 
Gültigkeit. Mach entweder ein static davor oder zieh' sie aus der 
Funktion heraus und mach sie global.

von Jonas B. (jibi)


Lesenswert?

C kann keinen String als Rückgabewert!

Gruß jonas

von Stefan E. (sternst)


Lesenswert?

radiostar schrieb:
> XString ist eine lokale Variable, die hat nur in der Funktion test
> Gültigkeit. Mach entweder ein static davor oder zieh' sie aus der
> Funktion heraus und mach sie global.

Auch falsch. Es wird ja der Pointer selber (also eine Kopie von ihm) 
zurückgegeben, und nicht etwa ein Pointer auf den Pointer. An der 
geposteten Version der Funktion gibt es nichts auszusetzen.

von Jonas B. (jibi)


Lesenswert?

Besser: C will keinen String als Rückgabewert ;)

Gruß Jonas

von Stefan E. (sternst)


Lesenswert?

Jonas Biensack schrieb:
> C kann keinen String als Rückgabewert!

Argh, noch einer.
Es wird ein Pointer auf einen globalen String zurückgegeben! Das ist 
absolut kein Problem.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:
> Nicht die gepostete Variante.

Richtig, gut erkannt.

Die nächste Frage ist, wie hier

> strcpy (buffer, test());
> putsXLCD (buffer);

"buffer" deklariert ist.

Dieses völlig irrelevante Detail hat joachim leider unterschlagen.

von Jonas B. (jibi)


Lesenswert?

return Xstring;

ist das pointer ??????

von radiostar (Gast)


Lesenswert?

Stefan Ernst schrieb:
> An der
> geposteten Version der Funktion gibt es nichts auszusetzen.

Und wo wird der String abgelegt? Auf dem Heap. Sobald die Funktion Test 
abgearbeitet ist, wird dieser Speicher wieder freigegeben und kann durch 
andere Daten überschrieben werden. Oder täusche ich mich?

von Jonas B. (jibi)


Lesenswert?

Eh...> Vielleicht aufs erste Zeichen....

phh....

Ratlos...

von Stefan E. (sternst)


Lesenswert?

Jonas Biensack schrieb:
> return Xstring;
>
> ist das pointer ??????

Ja.

von Klaus W. (mfgkw)


Lesenswert?

Und überhaupt braucht man für 3 Zeichen im String ein Feld mit 4 
Elementen (wg. abschließender 0), falls du mal YSTRING verwenden willst.

von Jonas B. (jibi)


Lesenswert?

Oh man nehm doch mal ein Buch zur Hand, so Fragen würd ich mich nicht 
trauen hier zu stellen, das impliziert immer das man vorher keine Mühe 
geopfert hat es mal selber auszuprobieren...

Trotzdem Gruß Jonas

von Εrnst B. (ernst)


Lesenswert?

radiostar schrieb:
> Und wo wird der String abgelegt? Auf dem Heap.

Nein. Im Konstantenpool.
Der Pointer ist am Heap, aber es wird ja eine Kopie des Pointers 
zurückgegeben.

Die gepostete Variante ist genau wie ein
return "Test";
zulässig.

von Stefan E. (sternst)


Lesenswert?

radiostar schrieb:
> Stefan Ernst schrieb:
>> An der
>> geposteten Version der Funktion gibt es nichts auszusetzen.
>
> Und wo wird der String abgelegt? Auf dem Heap. Sobald die Funktion Test
> abgearbeitet ist, wird dieser Speicher wieder freigegeben und kann durch
> andere Daten überschrieben werden. Oder täusche ich mich?

Nein. String-Lieterale sind immer statische global Objekte. Und Xstring 
ist ein Pointer darauf.

von radiostar (Gast)


Lesenswert?

Na gut... Bleibt trotzdem anzumerken, daß Strings nicht die Stärke von C 
sind. Meine auch nicht :-(

von Εrnst B. (ernst)


Lesenswert?

Εrnst B✶ schrieb:
> Der Pointer ist am Heap,

Am Stack natürlich... Halb im Blindflug abgetippt...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

"Am" auch nicht, "auf".

von Stefan E. (sternst)


Lesenswert?

Εrnst B✶ schrieb:
> Εrnst B✶ schrieb:
>> Der Pointer ist am Heap,
>
> Am Stack natürlich... Halb im Blindflug abgetippt...

Ich habe wohl auch im Blindflug getippt, "Lieterale", peinlich. ;-)

von Joachim .. (joachim_01)


Lesenswert?

Uuups... was hab ich losgetreten...?
20 min und tausend Leutchen haben geantwortet... danke. Allerdings auch 
vieele verschiedene Meinungen dazu...
Hab eben noch mal in nem Buch geschaut, da steht ebenfalls, daß XString 
nur im "scope" der lokalen Funktion liegt.Hmmm. Aber wie bekomm ich die 
jetzt von "innen" nach "aussen"?

von Karl H. (kbuchegg)


Lesenswert?

Ich würds mal so sagen:

Der gepostete Code ist hart an der Kippe.
Es gibt eine Reihe von potentiellen Fehlermöglichkeiten bei einer 
Codeänderung (zb durch Auskommentieren eines anderen Codepfades oder die 
unbekannte Definition einer Variablen), aber noch ist das Gepostete 
soweit in Ordnung.

von Udo S. (urschmitt)


Lesenswert?

Rufus Τ. Firefly schrieb:
> "Am" auch nicht, "auf".

Na ja eher "im", da obendrüber noch "char Ystring[3]" liegt :-)
(Duck und weg)

von Jonas B. (jibi)


Lesenswert?

Halb im Blindflug == Pirat mit Augenklappe lernt Fliegen ?

Ach...gleich ist Feierabend!

Gruss Jonas

von Udo S. (urschmitt)


Lesenswert?

Joachim ... schrieb:
> Allerdings auch vieele verschiedene Meinungen dazu...

Stefan, Rufus und Karl Heinz sagen dir das richtige.

von Karl H. (kbuchegg)


Lesenswert?

Joachim ... schrieb:

> Hab eben noch mal in nem Buch geschaut, da steht ebenfalls, daß XString
> nur im "scope" der lokalen Funktion liegt.Hmmm. Aber wie bekomm ich die
> jetzt von "innen" nach "aussen"?


Das Problem ist nicht das XString.
Das ist einfach nur eine Pointervariable, deren Inhalt zurückgegeben 
wird. Das Problem ist das worauf der Pointer zeigt.
Solange der auf ein Text-Literal zeigt

   char* XPointer = "ABC";

ist alles in Ordnung. Das Literal existiert auch nach dem 
Funktionsaufruf noch.

Ist das aber die Adresse eines lokalen Strings

   char String[] = "ABC";
   char* XPointer = String;

dann existiert der nach dem Funktionsaufruf nicht mehr. Der 
zurückgelieferte Adresswert verweist auf ein Objekt im Speicher, welches 
nicht mehr existiert -> Kaboom.

von Florian (Gast)


Lesenswert?

standart C ist bei mir auch schon ein paar Tage laenger her, aber darf 
man char * wirklich so initialisieren?
ich haette

    char *XString = mallo( sizeof(char) * 3 );
    *XString = "ABC";

    return XString;

erwartet.

von Karl H. (kbuchegg)


Lesenswert?

Studiere
String-Verarbeitung in C
speziell den Abschnitt 4, wenn auch die anderen Abschnitte nicht schaden 
dürften.

von Karl H. (kbuchegg)


Lesenswert?

Florian schrieb:
> standart C ist bei mir auch schon ein paar Tage laenger her, aber darf
> man char * wirklich so initialisieren?

Gegenfrage: Warum sollte man das nicht dürfen?

Ein String-Literal
"ABC"
ist ein Objekt, welches für die Dauer des Programmlaufs irgendwo im 
Speicher rumlungert. Wo genau ist Sache des Compilers/Linkers.

Und als solches hat es selbstverständlich auch eine Adresse im Speicher. 
Das unterscheidet es von zb einem Integer-Literal, welches keine hat, 
ist aber der Tatsache geschuldet, dass ein String-Literal den Datentyp 
const char[] hat und Arrays als Literal auf den meisten Architekturen 
zur Verarbeitung im Speicher liegen müssen, weil es keine Assembler 
Befehle dafür gibt.

Eine Pointervariable auf der anderen Seite, ist eine Variable, welche 
eine Speicheradresse speichern kann.

Passt alles perfekt zusammen und es gibt soweit keinen Einwand.
Als Purist könnte man noch einwenden, dass der exakte Datentyp eines 
String-Literals eigentlich ein const beinhaltet, welches hier implizit 
weggecastet wird, da allerdings die ISO-C-Väter diesen Fall explizit 
erlaubt haben, spricht nichts dagegen, die saloppe Formulierung

   char* PointerX = "ABC";

anstelle des eigentlich richtigen

   const char* PointerX = "ABC";

zu verwenden. (Solange man sich bewusst ist, dass dieses String-Literal 
nicht verändert werden darf)

von Joachim .. (joachim_01)


Lesenswert?

> char *XString = mallo( sizeof(char) * 3 );
Yep, mein Buch zielt in etwa auf die gleiche Lösung. Werd wohl n paar 
Basics auffrischen müssen...

von Karl H. (kbuchegg)


Lesenswert?

1
    char *XString = mallo( sizeof(char) * 3 );
2
    *XString = "ABC";
3
4
    return XString;

da sind dann gleich so ziemlich alle Probleme versammelt, in die man als 
C-Programmierer zum Thema "Strings" tappen kann.

Auch dir lege ich den Link von ein paar Posts weiter oben dringenst ans 
Herz.

von Klaus W. (mfgkw)


Lesenswert?

Florian schrieb:
> standart C ist bei mir auch schon ein paar Tage laenger her, aber darf
> man char * wirklich so initialisieren?
> ich haette
>
>     char *XString = mallo( sizeof(char) * 3 );
>     *XString = "ABC";
>
>     return XString;
>
> erwartet.

Der Vorschlag ist so falsch, daß man ihn nicht unbedingt verbreiten muß.

Joachim ... schrieb:
>> char *XString = mallo( sizeof(char) * 3 );
> Yep, mein Buch zielt in etwa auf die gleiche Lösung.

Schlechte Idee.

> Werd wohl n paar
> Basics auffrischen müssen...

Bessere Idee.

von Uli T. (avaron)


Lesenswert?

Ahhh, wenn ich gerade keinen Denkfehler hab liegt der String im 
DATA-Segment und der Code sollte eigentlich funktionieren... Da ist nix 
lokal.... Nur der Pointer auf den String ist lokal, macht aber nix, der 
wird ja beim return "kopiert"...
Also sollte das Beispiel eigentlich gehen... Viel eher ist da was im 
LCD-Teil oder in der Deklaration von buffer falsch....

von Karl H. (kbuchegg)


Lesenswert?

Uli Trautenberg schrieb:
> Ahhh, wenn ich gerade keinen Denkfehler hab liegt der String im
> DATA-Segment und der Code sollte eigentlich funktionieren...

Jein.

In Standard-C: ja.

Nur gibt es auch auf dem PIC Compiler, die String-Literale anders 
behandeln um kostbaren SRAM Speicher zu sparen.

von Uli T. (avaron)


Lesenswert?

Okay, funktioniert das dann ähnlich wie bei den ATMELs mit Strings im 
Flash?? Dann wäre aber eigentlich nur die Pointer zuweisung falsch....

von Joachim .. (joachim_01)


Lesenswert?

>XString ist eine lokale Variable, die hat nur in der Funktion test
>Gültigkeit. Mach entweder ein static davor [...]

static char Ystring[3] = "ABC";
return Ystring;
hat funktioniert. Aber wieso? Ist somit Ystring gewissermaßen an jeder 
Stelle, in jeder Funktion des Programms sichtbar/aufrufbar?

von Karl H. (kbuchegg)


Lesenswert?

Joachim ... schrieb:
>>XString ist eine lokale Variable, die hat nur in der Funktion test
>>Gültigkeit. Mach entweder ein static davor [...]
>
> static char Ystring[3] = "ABC";
> return Ystring;
> hat funktioniert. Aber wieso?

Zufall.
Die Konstellation war im Moment für dich einfach günstig. Wenn Ystring 
innerhalb der Funtktion zerstört wird, dann existiert ja der physische 
Speicher nach wie vor und die Bytes haben auch noch ihren Wert. Nur: Der 
Speicher gehört niemandem mehr und kann daher jederzeit von etwas 
anderem überschrieben werden.

Genauso wie es Zufall war, dass du am LCD ABC gesehen hast und nicht 
hinter ABC noch eine Reihe anderer Zeichen :-)
Um den String "ABC" zu speichern brauchst du ein Array der Länge 4.

Wenn das allerdings das gewünschte aufs LCD zaubert (trotz der beiden 
Fehler) und bei

   const char* Xstring = "ABC"

nicht, dann hast du den Fall vorliegen, dass String-Literale von deinem 
Compiler speziell behandlet werden und du die Compilerdoku zu diesem 
Thema studieren musst.

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.