[C++] Zugriffsverletzung beim Schreiben

Avance

Cadet 2nd Year
Registriert
Feb. 2006
Beiträge
22
Hi folks,

ich hab ein kleines, ungewöhnliches Problem. Ich habe eine Funktion für meine Klassenbibliothek erstellt. Die Funktion heißt asrepc - was für "Ansi String Replace Character" steht. So sieht sie aus:

Code:
inline uint asrepc( char* pcString, char cFindWhat, char cReplaceWith )
{
    assert( pcString ); // muss ungleich Null sein
    assert( cFindWhat > 32 ); // darf kein Steuerzeichen sein
    assert( cReplaceWith > 32 ); // darf auch kein Steuerzeichen sein

    register uint ui = 0; // Zähler für die ersetzten Zeichen

    while( 0x00 != *pcString )
    {
        if( *pcString == cFindWhat ) {
            *pcString = cReplaceWith; // hier ergibt sich die Zugriffsverletzung
            ui++;
        }
        pcString++;
    }

    return( ui );
}

Das Problem ergibt sich wie man sehen kann beim Schreiben auf die Speicherstelle. Die Variablen die ich im Funktionsaufruf übergebe ist weder konstant noch besitzt sie irgendeinen anderen Modifizierer. Lesen von der Speicherstelle funktioniert ganz normal. Hier 2 verschiedene Arten von Aufrufen:

Code:
char* pcText = "this is my text";
asrepc( pcText, ' ', '_' ); // Zugriffsverletzung

char acText[] = "this is my text";
asrepc( acText, ' ', '_' ); // keine Zugriffsverletzung, das Ersetzen funktioniert

Ich habe euch hier nochmal ein Screenshot während des Debuggens hochgeladen: Bitte hier klicken.

In einer anderen Funktion schreibe ich ebenfalls auf eine Speicherstelle, nur nicht auf die Quelle. Es handelte sich dabei um eine eigene Konvertierungsroutine, die von ANSI Strings in MBCS umwandelte. Da meine Bibliothek jetzt jedoch kein MBCS mehr unterstützen soll, sondern nur ANSI und Unicode, habe ich auch die Funktion entfernt. Jedenfalls lief diese Funktion auch mit der Übergabe eines Zeigers auf ein Zeichen (char*). Und die obengenannte Funktion bringt dies komischerweise nicht fertig.

Hat irgendjemand eine Ahnung was ich falsch mache? Oder ist der Compiler schuld? :D Übrigens: Es macht keinen Unterschied ob in Debug, Release oder mit ANSI oder Unicode kompiliert wird.

Vielen Dank für Eure Hilfe!
 
Avance schrieb:
Code:
char* pcText = "this is my text";

Dieses Konstrukt wird auch liebevoll "Conversion of Death" genannt. Das Literal ist ein const char[], auf das die array-to-pointer-conversion angewandt wird und danach die (veraltete) qualification-conversion nach char*. Dadurch zeigt der nicht-konstante Zeiger auf konstante Daten und es kracht.

Ist veraltetes Zeug, dass du vermeiden solltest. Jeder halbwegs aktuelle Compiler sollte dich auch davor warnen.


Btw, die assert's >32 gehen schief, wenn du einen erweiteren ASCII-Zeichensatz benutzt (Windows CP1252 z.B.) und z.B. äöü übergibst, char aber signed ist.
 
Zuletzt bearbeitet:
7H3 N4C3R schrieb:
Ist veraltetes Zeug, dass du vermeiden solltest. Jeder halbwegs aktuelle Compiler sollte dich auch davor warnen.
Danke für Deine Hilfe - jetzt weis ich wie ich das umgehen kann. Also ich arbeite mit Visual Studio 8 - scheint mir eigentlich ganz aktuell zu sein :D

Achso und was die asserts angeht: Auch danke für den Tip, war erstmal nur provisorisch diese Sicherheitschecks. Wollte erst einmal das Hauptproblem lösen, bevor ich mich an die Sicherheit mache.
 
Evtl. mal den Warn-Level hochschrauben? Der 8er Compiler ist eigentlich recht gut geworden. Wundert mich, dass der sowas nicht anmeckert.
 
Achja falls du den Rueckgabewert nicht brauchst kannst du auch die STL verwenden:

std::replace(str, str + sizeof(str), 't', 'f');
 
Das sizeof ist aber gefährlich, funktioniert an der Stelle nur mit einem array fester Größe, das noch nicht zu einem Pointer verkommen ist. strlen wäre sonst wohl verlässlicher.
 
Aral schrieb:
Achja falls du den Rueckgabewert nicht brauchst kannst du auch die STL verwenden
Na es ist nicht nur der Rückgabewert. Jetzt habe ich die Funktion eh in meine eigene String-Klasse integriert, die sich meinen Bedürfnissen anpasst (ich mag std::CString nicht :P) und werde die Funktion später auch noch mit Assembler realisieren. Ich setze auf Geschwindigkeit :)
 
In der Version sind dann aber die bisher fehlenden Sicherheitsabfragen enthalten :-)?

MfG

Arnd
 
Arnd schrieb:
In der Version sind dann aber die bisher fehlenden Sicherheitsabfragen enthalten :-)?
Etwas Fehlendes ist darin enthalten? :freak: :D um deine Frage zu beantworten: nein. Jedenfalls wenn wir von der finalen Version sprechen
 
Ich will dich an dieser Stelle ja nicht ausbremsen oder deprimieren, aber die mit deinem Compiler gelieferten C-Routinen zur String-Verarbeitung sind zumindest bei Optimierung winzigkleine Assembler-Instrinsics, die hochoptimiert sind und optimal zum Datenlayout des Compilers passen.

Wenn man obige Funktion straight-forward in Assembler runterschreibt, wird sie typischerweise um einiges langsamer sein als wenn man den Compiler einfach machen lässt. Man braucht jede Menge Knowhow über aktuelle Prozessoren, deren Datenorganisation etc. um ansatzweise genauso schnell zu sein.

Eine Assembler-Routine nimmt dem Compiler jede Möglichkeit weiter zu optimieren. Wenn sie geinlinet wird, kann sie nicht mit umgebendem Code verwebt und optimiert werden, sondern wird einfach reingeklatscht. Außerdem ist es für den Compiler eine Blackbox im Gegensatz zu C-Code und er muss den schlimmst-möglichen Fall hinsichtlich Cache-Strategie, Prozessorpipelines etc. annehmen.

Du nimmst also jede Menge Optimierungsmöglichkeiten raus. Zur Übung ist es immer gut, sich eine eigene Stringklasse und Stringverarbeitungsroutinen zu schreiben. Im praktischen Fall fährt man aber mit den mitgelieferten Sachen fast immer besser.
 
Code:
inline uint asrepc( char* pcString, char cFindWhat, char cReplaceWith )
{
    assert( pcString ); // muss ungleich Null sein
    assert( cFindWhat > 32 ); // darf kein Steuerzeichen sein
    assert( cReplaceWith > 32 ); // darf auch kein Steuerzeichen sein
    register uint ui = 0; // Zähler für die ersetzten Zeichen

    if( pcString ) 
    {

    while( 0x00 != *pcString )
    {
        if( *pcString == cFindWhat ) {
            *pcString = cReplaceWith; // hier ergibt sich die Zugriffsverletzung
            ui++;
        }
        pcString++;
    }

    }
    return( ui );
}

Ich halte das if für wesentlich. Asserts sind zwar ganz nett, aber nicht sicher. Ist denn sichergestellt das der Code alle möglichen Pfade durchlaufen hat, wenn er ausgeliefert wird? Eher nicht. Was ist mit späteren Änderungen sind die auch komplett getestet?

D.h. es ist möglich das der Code abstürzt und das ist schliesslich nicht gewünscht :-).

MfG

Arnd
 
Zurück
Oben