Hallo zusammen,
ich habe eine Frage bezüglich Pointer Arithmethik auf Strukturen.
Beispiel:
1
constchar*beispiel="Hallo Welt";
2
do{
3
printf("%c",*beispiel);
4
}while(*beispiel++);
Ganz konkret habe ich eine Datei in der Highscores abgespeichert werden.
Jeder Eintrag wird als Structure in der Datei in Binär Form gespeichert.
Ich habe dann eine Funktion die die Datei ausliest und mir die
entsprechenden Einträge als Pointer zurückgibt.
Wenn ich das genau mache, wie beim String, spuckt der Compiler natürlich
den Fehler raus, dass das nur bei Skalaren Variablen geht.
Die Datei hat momentan zwei Einträge.
Eine komische Sache ist mir auch noch aufgefallen.
Und zwar wenn ich folgendes mache:
Beispiel 2:
1
....
2
HIGHSCORE*ReadHighScore()
3
{
4
....
5
printf("Test1 %s - %d\n",entry[i].owner,entry[i].score);// Nicht auskommentiert diese Zeile
Wieso kriegt er den String nicht mehr korrekt dargestellt, es ist ja
nichts anders..
Meine Fragen nun dazu:
1. Wie kann ich die Dateneinträge ausgeben lassen, ohne mir eine
Hilfsfunktion zu schreiben, um die Zahl der Einträge zu bestimmen?
2. Wie kommt das Verhalten in Beispiel 2 zustande ?
Ich habe ja lediglich als Unterschied eine Ausgabe wegkommentiert.
Ich würde mich über Input freuen
Vielen Dank!
Du gibst einen Pointer auf ein lokal alloziertes Array zurück. Der ist
nach Ende der Unterfunktion nicht mehr gültig, und dass es überhaupt
funktioniert ist reines Glück.
Du solltest dir erst einmal die Grundlagen von C aneignen bevor du
versuchst, komplexere Programme zu schreiben.
Ich habe nicht den ganzen Code im Detail angeschaut, allerdings fällt
mir folgendes auf:
1
...
2
else
3
{
4
HIGHSCOREentry[entrys];
5
...
In Begriffen der Sprachbeschreibung von C definierst Du da innerhalb
eines Blockes eine Variable. "Definieren" heisst in diesem
Zusammenhang, dass Speicher auf dem Heap für diese Variable angelegt
wird. "Block" heisst in diesem Zusammenhang, eine Folge von Anweisungen
im Gegensatz zu einer einzelnen Anweisung. "Block" heisst aber in Bezug
auf Definitionen von Variablen auch, dass diese Variable auch nur
innerhalb dieses Blockes zugreifbar ist UND das diese Variable aufhört
zu existieren, sobald der Block verlassen wird.
Das hat zur Folge, dass jede Anweisung, wie Dein printf, die
ausserhalb des fraglichen Blocks ausgeführt wird, NICHT mehr auf diese
Daten zugreifen kann. Fraglos hast Du irgendeine andere Definition mit
gleichem Variablennamen - sonst würde das Programm nicht ohne Fehler
kompiliert werden -, aber diese zweite Definition benennt einen
gesonderten, anderen Speicherbereich, der mit dem innerhalb des
Else-Blockes nichts zu tun hat.
Daher wird das printf auch nichts ausgeben, was zuvor mit dem Code aus
der Datei eingelesen wurde.
Falls Du dazu noch Fragen hast, bitte einfach fragen. Klar, dass das
nicht beim ersten lesen sitzt. Wenn möglich, lies bitte auch über
Definitionen und Geltungsbereich von Variablen nach.
Du legst in ReadHighScore das Array entry auf dem Stack an und gibst
einen Zeiger darauf zurück. Sobald die Funktion zurückkehrt, ist das
Array aber nicht mehr gültig!
Dass es bei Dir manchmal scheinbar funktioniert, ist reines Glück (oder
Pech ...), wenn die Daten im Speicher noch nicht von einer anderen
Funktion überschrieben wurden.
Du hast zwei Möglichkeiten:
- Du legst den Speicher außerhalb von ReadHighScore an und übergibst
einen Zeiger auf den Speicher an ReadHighScore.
- Du legst den Speicher in ReadHighScore mit malloc() an und gibst den
Zeiger darauf zurück. Der Aufrufer der Funktion muss ihn dann mit free()
wieder freigeben!
Die Zahl der Einträge sollte ReadHighScore an den Aufrufer zurückgeben.
Im Gegensatz zu einem String hat Dein Array ja kein String-Ende-Zeichen,
mit dem der Aufrufer feststellen kann, dass die Daten zu Ende sind.
Eine kleine Korrektur:
Ich habe hier:
Beitrag "Re: C - Pointer Arithmethik auf Strukturen?"
geschrieben, dass die Variablen innerhalb des Blocks auf dem Heap
angelegt wird.
Das ist, so nicht richtig, und mindestens zwei Antworter haben korrekt
geschrieben, dass die Variable auf dem Stack angelegt wird. Ein
Flüchtigkeitsfehler von mir. Sorry.
Den Rest meiner Antwort berührt dieser (mein) Fehler aber nicht.
Hallo zusammen,
vielen Dank erstmal für die Antworten!
Ich wollte eben nicht, dass ich in der Funktion Speicher reserviere aber
nicht direkt wieder freigeben kann. Sodass man diesen nach Aufruf der
Funktion irgendwo anders tuen muss, man könnte es ja vergessen ;-)
Das mit dem Gültigkeitsbereich erklärts vielen Dank dafür. Ich werde es
umbauen.
Gruß
MOP schrieb:> Hallo zusammen,>> vielen Dank erstmal für die Antworten!>> Ich wollte eben nicht, dass ich in der Funktion Speicher reserviere aber> nicht direkt wieder freigeben kann. Sodass man diesen nach Aufruf der> Funktion irgendwo anders tuen muss, man könnte es ja vergessen ;-)>> Das mit dem Gültigkeitsbereich erklärts vielen Dank dafür. Ich werde es> umbauen.>> Gruß
Habe diese umgebaut, falls sich jemand dafür interessiert:
Habe dazu eine übergeordnete Struktur angelegt, in der auch die Anzahl
der Einträge gespeichert wird.
Funktioniert jetzt auch super.
Vielen Dank.
Gut. Die Lösung ist formal korrekt. Dein Problem gelöst.
Einen Hinweis allgemeiner Natur möchte ich allerdings hinzufügen.
Es spielt bei Deiner Anwendung keine grosse Rolle, aber Du gibst eine
Struktur zurück, die aus einem Zeiger und einem Integer besteht. Das
bedeutet, dass die Struktur kopiert wird, denn sie existiert innerhalb
der Funktion auf dem Stack und muss in den Stackbereich, der aufrufenden
Funktion kopiert werden. Das verlangsamt das Programm, wenn auch nur
minimal.
Soweit so gut. Wie gesagt: Die Zeit spielt bei dieser kleinen Struktur
auch nur eine geringe Rolle, da die Struktur klein ist.
Allerdings vermeiden, - jedenfalls ältere Programmierer -, das aus
Gewohnheit grundsätzlich, weil der zusätzliche Aufwand dazu gering ist.
Sie übergeben fast immer Zeiger auf Strukturen, weil das beim Verlassen
der Funktion die minimal mögliche Datenmenge kopiert.
Du wendest, - ganz richtig -, ein, dass Du den Speicher dann wieder
freigeben musst. Allerdings musst Du das bei Deiner jetzigen Lösung
ohnehin tun. Zwar nicht die HIGHSCORELISTE aber die entries. Es wäre
also nur ein zusätzlicher Aufruf von malloc und free.
Das nur als Hinweis für weitere Projekte. Ob Du das berücksichtigst oder
nicht, muss Dir überlassen bleiben.
@A.K.
Vielen Dank für den Hinweis, war eben noch etwas zu schnell beim ändern.
File Pointer wird jetzt mit NULL verglichen.
Und die andere Abfrage sollte natürlich so sein:
if(entrys != NULL && entry_count > 0)
Ich versuchs prinzipiell erstmal zum laufen zu bringen, außer mein
Compiler(mingw32) warnt mich oder spuckt Fehler heraus. Optimieren kann
man ja später, sonst wird man ja nie fertig ;-)
@Theor
Mal Offtopic - weil du es gerade geschrieben hast -
entries oder entrys? Bin mir gerade nicht sicher ;-)
In meinen aktuellen Hobbyprojekt habe ich sehr viele Strukturen und
unterschiedliche Funktionen in unterschiedliche Dateien ausgelagert.
Weil das sonst für mich zu unübersichtlich wird.
Wäre es dann später, wenns irgendwann fertig ist, sinnvoll z.B. eine
"Globale" Header/Programm Datei anlege, in der ich alle Strukturen
definiere, und initialisiere. Und mittels Übergabe an den verschiedenen
Subdateien beschreiben/bearbeiten lasse?
Vielen Dank Gruß!
Wenn du einfach so ein solches struct aus dem Speicher in eine Datei
schreibst/einlest, kann es passieren, dass das Program die Datei auf
anderen Geräten nicht richtig einlesen kann, wegen endianness,
Datentypen mit anderen grössen oder padding, etc. Man kann das auf 3
wegen vermeiden:
1) Nutze das packed attribut, datentypen mit fester breite aus stdint.h
(z.B. uint32_t), und verwende vor dem speichern/einlesen hton/ntoh
2) Schreibe parser und serializer Funktionen, die die umwandlung der
Daten in ein Binärformat übernehmen.
3) Kombiniere 1 und 2
MOP schrieb:> Habe dazu eine übergeordnete Struktur angelegt, in der auch die Anzahl> der Einträge gespeichert wird.>> Funktioniert jetzt auch super.
Das funktioniert nur deswegen, weil Du jetzt die Struktur selbst
zurückgibst und nicht ihre Adresse:
1
// alt
2
HIGHSCORE*ReadHighScore()
3
4
// neu
5
HIGHSCORELISTEReadHighScore()
Das hättest Du aber auch ohne Deine übergeordnete Struktur hinbekommen
können.
Daß es nicht ratsam ist, größere Datenstrukturen als
Funktionsrückgabewerte zu verwenden, sollte Dir klar sein, die Dinger
landen nämlich jeweils als Kopie vollständig auf dem Stack, und das
Zuweisen des Ergebnisses ist wiederum auch eine potentiell
performancefressende Operation.
Rufus Τ. F. schrieb:> Daß es nicht ratsam ist, größere Datenstrukturen als> Funktionsrückgabewerte zu verwenden, sollte Dir klar sein, die Dinger> landen nämlich jeweils als Kopie vollständig auf dem Stack, und das> Zuweisen des Ergebnisses ist wiederum auch eine potentiell> performancefressende Operation.
Muss nicht sein. Der Compiler kann die Werte auch gleich an ihre
Zieladresse schreiben (return value optimization).
Hans schrieb:> Muss nicht sein.
Gewiss. Aber ohne Not so zu programmieren, daß einen nur noch der
Optimierer aus dem Sumpf holt, halte ich für ... suboptimal.