Nun bekomme ich, egal wo ich dies aufrufe, eine Zugriffsverletzung genau an der Stelle, an der die Funktion aufgerufen wird.
Was mache ich falsch? Habe ich vielleicht etwas übersehen?
(hab schon sämtliche Foren und Beiträge durchsucht, habe aber leider keine Lösung gefunden)
Schritte in so einem Fall: Zunächst solltest du dich vergewissern, dass dein Quelltext auch das tut, was du möchtest. Ein Blick in die Dokumentation zu
printf verrät:
|
C-/C++-Quelltext
|
1
|
int printf ( const char * format, ... );
|
Print formatted data to stdout
Writes to the standard output (stdout) a sequence of data formatted as the format argument specifies.
Standard Output meint in aller Regel die Konsole, dein Quelltext schreibt also zunächst irgendwas in die Konsole, und dann irgendwas auf den Bildschirm. Der Text, auf den
m_pText zeigt, wird jedoch auf gar keinen Fall durch den Aufruf von
printf aktualisiert, wie du schon am Typ des printf-Parameters
format ablesen kannst: Das
const in
const char * bedeutet, dass keines der Zeichen, auf die
format zeigt, im Ablauf der aufgerufenen Funktion je verändert werden
darf.
Was du suchst, ist offensichtlich
sprintf.
|
C-/C++-Quelltext
|
1
|
int sprintf ( char * str, const char * format, ... );
|
Write formatted data to string
Writes into the array pointed by str a C string consisting on a sequence of data formatted as the format argument specifies. After the format parameter, the function expects at least as many additional arguments as specified in format.
This function behaves exactly as printf does, but writing its results to a string instead of stdout. The size of the array passed as str should be enough to contain the entire formatted string.
Wie du siehst, nimmt
sprintf einen Parameter mehr, was auch so sein muss, weil die gesamte
printf- und
scanf-Familie immer einen Formatierungsstring erwartet, der angibt, was im Anschluss überhaupt noch folgt. Zur Ausgabe eines Integers entnimmst du der Tabelle unter
format, dass der entsprechende String
"%d" sein muss, wenn im Anschluss ein Integer folgt. Damit wärst du also bei:
|
C-/C++-Quelltext
|
1
|
sprintf(m_pText, "%d", 10);
|
Dies löst jedoch noch nicht die Zugriffsverletzung.
sprintf erwartet, dass du selbst Speicher für den Ergebnisstring bereitstellst. Das heißt konkret: Das Char-Array, auf das dein
m_pText zeigt, muss schon vor Aufruf der Funktion
sprintf groß genug für das Ergebnis sein. Ich vermute mal, dass dein
m_pText bisher einfach auf gar nichts zeigt, wodurch natürlich schon ein Schreib- oder Lesezugriff mit
m_pText[0] unweigerlich zum Absturz führen muss. Integers werden in Textform nicht allzu groß (vgl. Minimal- und Maximalwerte), du könntest deinen
m_pText-Zeiger also direkt durch ein Char-Array
char m_text[16] ersetzen. Es gibt in C/C++ eine implizite Konvertierung von
T[] zu
T*, das heißt Arrays lassen sich immer auch als Zeiger übergeben, und du kannst schreiben:
|
C-/C++-Quelltext
|
1
2
3
|
// mit char m_text[16]; anstatt m_pText
sprintf(m_text, "%d", 10); // schreibt "10" in den Speicherbereich m_text
TTF_RenderText_Solid(..., m_text, ...); // zeichnet den neuen Text
|
Die Tatsache, dass du die Länge des Ergebnis schon vorher wissen musst, ist auch der Grund für das, was BlueCobold nennt: Für komplexere Strings ist es manchmal schwierig bis unmöglich, die maximale Länge des Ergebnis einigermaßen genau vorauszusagen. Und selbst wenn es möglich wäre, bleibt es eine bedeutende Fehlerquelle, sollte sich der String mal ändern, aber der Programmierer vergessen, auch die Maximallänge anzupassen. Die von C++ bereitgestellten Klassen
std::string und
std::stringstream nutzen intern zwar auch nur
sprintf, verwalten den notwendigen Speicher jedoch völlig autonom und wurden so oft getestet, dass du dir der Fehlerfreiheit einigermaßen sicher sein kannst. Damit sähe dein Anwendungsfall so aus:
|
C-/C++-Quelltext
|
1
2
3
|
// mit std::string m_text; anstatt m_pText
m_text = (std::stringstream() << 10).str(); // schreibt "10" in den stringstream und speichert das Ergebnis im String m_text
TTF_RenderText_Solid(..., m_text.c_str(), ...); // zeichnet den neuen Text
|
Nichtsdestotrotz ist es wichtig, dass du auch die erste Variante und die Zugriffsverletzung verstehst, weil du auch in C++ immer wieder auf das Konzept von Zeigern und Speicherbereichen stoßen wirst. Im Falle einer sehr häufigen Textänderung (z.B. einmal pro Frame) würde ich persönlich sogar bei
sprintf mit festem Speicherbereich bleiben, um ständigen Speicherallokationen aus dem Weg zu gehen.