Access violation?
-
Hallo,
Ich habe jetzt eine Weile an diesem Problem herumgebastelt, aber ich scheine einfach irgend ein Konzept nicht zu verstehen. Was auch immer ich mache - ein Element eines char* zu ueberschreiben gibt me eine 'Access violation'.
Ich nehme mal an, dass es irgendwie moeglich ist, ich weiss nur nicht wie?#include <iostream> using namespace std; char* upper(char* c, const unsigned int len) { unsigned i = 0; do { c[i] = char(c[i] - 32); // hier? } while(++i < len); return c; } int _tmain(int argc, _TCHAR* argv[]) { std::cout << upper("hello",3); return 0; }
-
Du übergibst eine Zeichenkette, die nicht verändert werden darf.
so sollte es gehen:int _tmain(int argc, _TCHAR* argv[]) { char hello[] = "hello"; std::cout << upper(hello,3); return 0; }P.S.: in C++ würde man std::string und std::transform nehmen
-
Das ist sinnvoll. Gibt es eine Moeglichkeit, rauszufinden ob der Parameter modifiziert werden darf?
Ich schau mal std::transform an, es ist aber mehr oder minder eine Uebung, weshalb ich es gerne auch so zum laufen kriegen wuerde.
-
lyschoening schrieb:
Das ist sinnvoll. Gibt es eine Moeglichkeit, rauszufinden ob der Parameter modifiziert werden darf?
Du verwendest hier einen konstanten Ausdruck: upper("hello",3);
Verendern kannst du nur Variableninhalte (die nicht konstant deklariert sind).Wenn du ganz sicher sein willst, arbeite in deiner Upperfunktion mit stringobjekten, dann wird der Inhalt in diesen String kopiert:
std::string upper(std::string text, const unsigned int laenge);
Der übergebene Text ist hier absichtlich Call-By-Value gewählt (um nur die Kopie zu modifizieren, nicht das Original).
cu André
-
Nun in diesem Fall kannst Du nicht unterscheiden, ob veränderbar oder nicht.
Als Aufrufer musst Du wissen, dass die Funktion was verändert und dementsprechend auch beschreibbaren Speicher zur Verfügung stellen.Wenn eine Funktion etwas nicht verändern wird, dann sollten die Parameter per const übergeben werden.
-
Aber kann man die Zugriffsverweigerung vielleicht 'catchen'?
Ich stelle mir eine Funktion vor, die nur eine Kopie erstellt, wenn es nicht moeglich ist das Original zu veraendern.
-
Wenn Du eine Kopie auf dem Stack erstellst, dann kannst Du keine Referenz/Zeiger auf diesem zurückgeben, weil dieser ungültig wird beim Verlassen der Funktion.
Erstellst Du die Kopie auf dem Heap muss der Aufrufer dran denken den Speicher wieder freizugeben. Also so oder so nicht schön.Du kannst Smartpointer verwenden, aber willst Du das hier wirklich?!
Am einfachsten ist, der Aufrufer kümmert sich um den Speicher.
-
Erstell dir einfach zwei überladene Funktionen upper, eine die char* und eine die const char* übernimmt. Bei der zweiteren musst du dann kopieren. Der Compiler ruft dann schon die passende Version auf.
-
lyschoening schrieb:
Aber kann man die Zugriffsverweigerung vielleicht 'catchen'?...
Allgemeingültig: Nein.
Es mag irgendwelche compilerspezifischen Möglichkeiten gebene, aber sowas würde ich sowieso nur im extremen Notfall (Nein: Das hier ist definitiv keiner !) anwenden.Wie Du an Deiner Ursprungsfrage schon selbst gelernt hast: Zeichenketten per char* zu behandeln ist zeitaufwendig, umständlich und fehleranfällig.
Eine kleine Änderung und schon klappt alles wunderbar:#include <iostream> #include <string> using namespace std; string upper(string const& s) { string ret(s); size_t i=0; do { ret[i] = char(c[i] - 32); // hier? } while(++i < s.length()); return ret; } int _tmain(int argc, _TCHAR* argv[]) { std::cout << upper("hello"); return 0; }... und schon klappt's sicher und und zuverlässig
(naja:
- die do/while-Schleife sollte noch "umgedreht" oder als for implementiert werden (dann klappts auch mit leeren Strings)
- darauf, dass die Umrechnung "-32" überall funktioniert, würde ich mich auch nicht verlassen ... schließlich gibt's ja std::toupper().
)Gruß,
Simon2.
-
Natürlich werde ich dies nicht zur praktischen Verwendung ziehen, unicode unterstützt es ja höchstens marginal. Ich mag die Idee des Überladens der Funktion. Das Problem mit der Addresse der ausserhalb des Geltungsbereichs tritt da zwar auf, aber nur als Kompilerwarnung, funktionieren tut es trotzdem.
Ist das einfach ein Zufall, weil der Speicher nicht gebraucht und daher nicht überschrieben wird?#include <iostream> using std::cout; char* upper(const char* c, unsigned int l) { char _c[l]; while(l--) _c[l] = (c[l] >= 'a' && c[l] <= 'z') ? char(c[l] - 32) : c[l]; return _c; } char* upper(char* c, unsigned int l) { while(l--) c[l] = (c[l] >= 'a' && c[l] <= 'z') ? char(c[l] - 32) : c[l]; return c; } int main() { char h[] = "abc123\n"; cout << upper(h, 6); cout << upper("hallo123", 8); return 0; }
-
Das hier
char _c[l];ist kein gültiges C++. Diese Art der Arrays stammen aus C99. Manche C++Compiler unterstützen sie allerdings (gcc). Portabel ist so ein Code aber nicht. Normalerweise würde man hier
char* _c = new char[l];schreiben. Wobei sich natürlich das Problem mit dem delete einhandelt.
-
Braunstein schrieb:
char* _c = new char[l];schreiben. Wobei sich natürlich das Problem mit dem delete einhandelt.
Sicher? Ich hab es jetzt in dem Format, und schon warnt mich GCC nicht mal mehr.