read() und Puffer problem



  • Hallo zusammen

    1. Ich hab versucht mittels read( ) eine Textdatei auszulesen. In der Textdatei steht folgendes:

    0123456789abcdefghijklmnopqrstuvwxyz

    für SIZE = 2 bekomme ich die Ausgabe wie erwartet:
    01
    23
    45
    67
    .......

    für SIZE = 3 bekomme ich diese Ausgabe:
    012¿Œöÿ¿höÿ¿
    345¿Œöÿ¿höÿ¿
    678¿Œöÿ¿höÿ¿
    .......

    Warum werden zusätzlich Zeichen ausgegeben??

    #include <iostream.h>
    
    int main() {
    
            FILE *fp;
            fp = fopen("36.txt", "r");
    
            const int SIZE = 3;
            char Puffer[SIZE];
            int gelesen = 0;
            while((gelesen = read(fileno(fp),Puffer,sizeof(Puffer)))> 0){
            printf("Anzahl der gelesenen Bytes %i\n", gelesen);
            printf("%s\n", Puffer);
            }
            fclose(fp);
    return 0;
    }
    

    2. wenn ich einen Speicherbereich von:

    int Puffer[10] deklarier, werden 56Byte statt 40Byte reserviert.

    wenn ich einen Speicherbereich von:

    char Puffer[10] deklarier, werden 24Byte statt 3Byte reserviert.
    Warum???



  • R.E.T schrieb:

    Hallo zusammen

    1. Ich hab versucht mittels read( ) eine Textdatei auszulesen. In der Textdatei steht folgendes:

    0123456789abcdefghijklmnopqrstuvwxyz

    für SIZE = 2 bekomme ich die Ausgabe wie erwartet:
    01
    23
    45
    67
    .......

    Das das funktioniert, ist reiner Zufall. Da steht auf dem Stack zufällig das richtige. Aber siehe weiter unten...

    für SIZE = 3 bekomme ich diese Ausgabe:
    012¿Œöÿ¿höÿ¿
    345¿Œöÿ¿höÿ¿
    678¿Œöÿ¿höÿ¿
    .......

    Warum werden zusätzlich Zeichen ausgegeben??

    Wenn du den Buffer mit printf("%s",...) ausgibst, dann erwartet printf einen String, der mit einem 0-Byte terminiert ist. Das ist bei deinem Beispiel aber nicht gegeben. Du füllst den gesamten Buffer mit dem Input aus der Datei. Wenn du den dann an printf() übergibst, gibt dieses solange Zeichen für Zeichen aus, bis es ein Byte mit dem Inhalt 0 findet. Das wird irgendwo hinter dem Ende des Buffers sein. Die komischen Zeichen, die ausgegeben werden, sind der Inhalt des Speichers zwischen dem Ende deines Buffers und dem 0-Byte.
    Hier die korrigierte Version:

    #include <iostream.h>
    
    int main() {
    
            FILE *fp;
            fp = fopen("36.txt", "r");
    
            const int SIZE = 3;
            char Puffer[SIZE + 1];
            int gelesen = 0;
            while((gelesen = read(fileno(fp),Puffer,SIZE))> 0){
            Puffer[gelesen] = 0; // sicherstellen, daß der Buffer 0-terminiert ist.
            printf("Anzahl der gelesenen Bytes %i\n", gelesen);
            printf("%s\n", Puffer);
            }
            fclose(fp);
    return 0;
    }
    

    Jetzt wird der Buffer um ein Byte größer angelegt, als du einlesen willst. Somit ist nach dem read() immer noch mindestens ein Byte übrig, in das eine Null geschrieben werden kann (für printf).
    Das read()-Kommando wurde angepaßt, um höchstens SIZE Zeichen zu lesen. Das letzte Byte im Buffer brauchen wir ja als Reserve für die Null.
    Nach dem read() wird dann der Buffer null-terminiert, indem hinter das letzte eingelesene Byte eine Null geschrieben wird. Da der Buffer die Größe SIZE+1 hat, wissen wir auch, daß wir mit Puffer[gelesen] nie jenseits des Buffers schreiben.
    (Kleiner Hinweis: Puffer[SIZE] = 0 wäre falsch, da read() auch weniger als SIZE Zeichen lesen könnte, z.B. am Ende der Datei. Deshalb Puffer[gelesen].)

    2. wenn ich einen Speicherbereich von:

    int Puffer[10] deklarier, werden 56Byte statt 40Byte reserviert.

    wenn ich einen Speicherbereich von:

    char Puffer[10] deklarier, werden 24Byte statt 3Byte reserviert.
    Warum???

    Das sollte nicht so sein. Könntest du uns ein Test-Programm posten, mit dem du zeigst, wie du an diese Zahlen kommst? Ich bekomme bei mir mit sizeof(Puffer) die richtigen Werte.

    Ich hoffe, ich konnte dir helfen.

    - Raving Tux

    P.S.: Manche Probleme sind Compiler- oder Plattform-abhängig, deshalb ist es immer gut, diese mit anzugeben.



  • Hallo, danke für die schnelle Hilfe!
    !!!!ps: ich hab mich oben vertippt, es müsste 12Byte nicht 3Byte heißen!!!!

    zu Punkt 2 ein Beispiel:

    void funktion() {
         char Puffer[10];
    }
    
    int main() {
        funktion();
    }
    

    Mit sizeof(Puffer) bekomme ich als Rückgabewert 10 OK!
    Wenn man sich das jedoch in Assembler anschaut:

    [cpp]
    gdb a.out
    disassemble funktion

    0x804832c <funktion>: push %ebp
    0x804832d <funktion+1>: mov %esp,%ebp
    0x804832f <funktion+3>: sub $0x18,%esp //hier wird Speicher reserviert
    0x8048332 <funktion+6>: leave
    0x8048333 <funktion+7>: ret

    [/cpp]

    0x804832f <funktion+3>: sub $0x18,%esp

    0x18 ist Hexadezimal und Decimal 24 also werden 24Byte reserviert
    eigendlich müssten 12Byte reichen, da char nur 1Byte belegt, wenn man pro
    Speicherzelle 4Byte hat reichen 12Byte aus:

    [c][c][c][c] [c][c][c][c] [c][c][-][-]

    Warum werden aber 24Byte reserviert oder rechne ich einfach nur falsch?



  • R.E.T schrieb:

    Warum werden aber 24Byte reserviert oder rechne ich einfach nur falsch?

    Du rechnest schon richtig.
    Aber:

    - Welcher Compiler?
    - Welche Plattform?
    - Welche Compiler-Settings (z.B. Optimierung aktiviert oder nicht, wenn ja: welche Stufe etc. etc. etc.).

    Letztlich ist das alles abhängig vom Compiler.
    Ich habe das jetzt mal mit dem C++-Compiler von Visual Studio 6.0 ausprobiert.
    Im Release-Modus (Optimierungen ausgeschaltet; mit Optimierungen würde der ganze Code wegoptimiert 😉 ) ist der Wert beim 'sub' immer ein Vielfaches von 4, d.h., der Stack-Pointer wird auf 4 bytes aligned.
    Im Debug-Modus werden immer noch zusätzliche 0x40 Bytes abgezogen; keine Ahnung, wofür.

    Das Verhalten im Release-Modus ist absolut notwendig (für gute Performance).
    Das Verhalten im Debug-Modus ist wohl für ein spezielles Feature notwendig (edit and continue, d.h. man kann während dem Debuggen Code-Teile ändern und on-the-fly neu übersetzen, ohne das Debuggen zu beenden).

    Was ich damit sagen will ist: der Compiler kann hier machen, was er will. Solange er den C/C++-Code semantisch korrekt umsetzt, hat er mehr oder weniger absolute Freiheit in der Gestaltung des Codes.

    Für dein Programm ist der Buffer so groß, wie sizeof() zurückgibt. Alles andere sind Details, die ein Programm niemals ausnutzen darf.

    - Raving Tux


Anmelden zum Antworten