• Wir präsentieren Dir heute ein Stellenangebot für einen Frontend-Entwickler Angular / Java in Braunschweig. Hier geht es zur Jobanzeige

Array in C++ ausgeben funktioniert nicht

W

wantri

Mitglied
Hallo,

ich habe eine Klasse PPM geschrieben, in der über die Funktion einlesen() eine PPM-Datei eingelesen wird und deren Daten in Membervariablen gespeichert werden.
Für die Pixel habe ich ein Array erstellt. Ich habe durch Tests mit printf festgestellt, dass das Array korrekt befüllt wird.

In der Funktion dateiSchreiben() sollen nun die Daten aus den Membervariablen in eine neue Datei geschrieben werden.
Wenn ich die Funktion wie gezeigt aufrufe, ist die Datei image.ppm jedoch leer.
Merkwürdigerweise wird jedoch die Kopfzeile korrekt in die Datei geschrieben, wenn ich den Teil, der die Pixel in die Datei schreibt, vorher auskommentiere.

C++:
#include <iostream>
#include <string.h>

class PPM
{
public:
    char path[255];
    FILE *in;
    char format[3];
    int bildbreite;
    int bildhoehe;
    int maxHelligkeit;
    int* pixelArrayP3;

public:
    PPM(char path[255])
    {
        std::copy(path, path + 255, this->path);
    }

    void einlesen()
    {
        char letter;
        int zahlenInHeaderGespeichert=0;
        in=fopen(path, "rb");

        FILE* out;
        out = fopen("ohneKommentare.ppm", "wb");

        char c;
        //Kommentare entfernen
        while(!feof(in)) {
            c=fgetc(in);
            if(c=='#') {
                do {
                    c=fgetc(in);
                } while(c!='\n');

                continue;
            }
            fprintf(out,"%c",c);
        }
        fclose(in);

        //Ab hier wird die neue Datei ohne Kommentare genutzt
        FILE *datei;
        datei=fopen("ohneKommentare.ppm","rb");

        //Informationen der Datei in Membervariablen speichern
        while(!feof(datei)) {
            letter=fgetc(datei);
            //Format speichern
            if(letter=='P') {
                format[0]=letter;
                letter=fgetc(datei);
                format[1]=letter;
                format[2]=0;
                continue;
            }

            //Zahlen für Bildbreite,Helligkeit und max. Helligkeit speichern
            if(!zahlenInHeaderGespeichert) {
                fscanf(datei,"%d",&bildbreite);
                fscanf(datei,"%d",&bildhoehe);
                fscanf(datei,"%d",&maxHelligkeit);
                zahlenInHeaderGespeichert=1;
            }

            //Pixel dezimal speichern, wenn Datei im P3-Format vorliegt
            if(format[0]=='P' && format[1]=='3') {
                int i;
                pixelArrayP3=new int[bildbreite*bildhoehe*3];
                for(i=0; i<(bildbreite*bildhoehe*3); i++) {
                    fscanf(datei,"%d",pixelArrayP3);
                    //printf("%d\n",*pixelArrayP3);
                    pixelArrayP3++;
                }

            }

        }

    }

    void dateiSchreiben()
    {
        FILE *datei;
        datei=fopen("image.ppm","wb");
        fprintf(datei,"%c",format[0]);
        fprintf(datei,"%c\n",format[1]);
        fprintf(datei,"%d ", bildbreite);
        fprintf(datei,"%d\n", bildhoehe);
        fprintf(datei,"%d\n", maxHelligkeit);

        if(format[0]=='P' && format[1]=='3') {
            int i;
            for(i=0; i<(bildbreite*bildhoehe*3); i++) {
                fprintf(datei,"%d\n",pixelArrayP3[i]);
            }
        }

    }
};
int main()
{
    char path[255];
    scanf("%s", path);
    PPM file(path);
    file.einlesen();
    file.dateiSchreiben();
    return 0;
}

Es wäre sehr nett, wenn mir jemand erklären könnte, wie ich das Array korrekt in die neue Datei schreibe.

Vielen Dank im Voraus!
 
H

httpdigest

Top Contributor
Ich habe durch Tests mit printf festgestellt, dass das Array korrekt befüllt wird.
Nein, das wird es nicht. Die Datei "ohneKommentare.ppm", die du schreibst, wird niemals korrekt/vollständig auf Disk geflushed, weil du sie nie schliesst oder flushed, bevor du sie wieder im in/read-Modus öffnest. Du musst einmal in einlesen() ein `fclose(out);` machen, nachdem du die ohneKommentare.ppm geschrieben hast.
In der Funktion dateiSchreiben() sollen nun die Daten aus den Membervariablen in eine neue Datei geschrieben werden.
Wenn ich die Funktion wie gezeigt aufrufe, ist die Datei image.ppm jedoch leer.
Das liegt daran, dass deine Member-Variablen noch niemals korrekt eingelesen wurden, da die ohneKommentare.ppm Datei noch leer ist, wenn du sie lesen möchtest.
Merkwürdigerweise wird jedoch die Kopfzeile korrekt in die Datei geschrieben, wenn ich den Teil, der die Pixel in die Datei schreibt, vorher auskommentiere.
Es ist Zufall, was hier passiert. Da du aufgrund der oben genannten Fehler hauptsächlich Speichermüll in den width/height und dem Byte-Array hast.
Ausserdem schliesst du die Datei in dateiSchreiben() auch nicht korrekt.

Nachdem du das alle behoben hast, wirst du desweiteren das Problem haben, dass dein pixelArrayP3 Pointer immer ans Ende deines Pixelarrays zeigt, nachdem die einlesen() Funktion fertig ist, da du den Pointer selbst immer inkrementierst. Du brauchst hier in einlesen() eine temporäre Pointer-Variable als Alias auf das Pixelarray, um die Pointerarithmetik für das Einlesen durchzuführen (oder du verwendest einfach einen Index für den Zugriff auf pixelArrayP3).

Verwende am besten einfach eine C++ IDE und einen Debugger, und lass dir dort die Werte der Member-Variablen ausgeben, dann siehst du, was ich meine.
 
W

wantri

Mitglied
Vielen Dank für Deine Antwort!

Ich habe das Programm nun folgendermaßen abgeändert:

C++:
#include <iostream>
#include <string.h>

//Dateinamen in PPM umwandeln
class PPM
{
public:
    char path[255];
    FILE *in;
    char format[3];
    int bildbreite;
    int bildhoehe;
    int maxHelligkeit;
    int* pixelArrayP3;

public:
    PPM(char path[255])
    {
        std::copy(path, path + 255, this->path);
    }

    void einlesen()
    {
        char letter;
        int zahlenInHeaderGespeichert=0;
        in=fopen(path, "rb");

        FILE* out;
        out = fopen("ohneKommentare.ppm", "wb");

        char c;
        //Kommentare entfernen
        while(!feof(in)) {
            c=fgetc(in);
            if(c=='#') {
                do {
                    c=fgetc(in);
                } while(c!='\n');

                continue;
            }
            fprintf(out,"%c",c);
        }
        fclose(in);
        fclose(out);

        //Ab hier wird die neue Datei ohne Kommentare genutzt
        FILE *datei;
        datei=fopen("ohneKommentare.ppm","rb");

        //Informationen der Datei in Membervariablen speichern
        while(!feof(datei)) {
            letter=fgetc(datei);
            //Format speichern
            if(letter=='P') {
                format[0]=letter;
                letter=fgetc(datei);
                format[1]=letter;
                format[2]=0;
                continue;
            }

            //Zahlen für Bildbreite,Helligkeit und max. Helligkeit speichern
            if(!zahlenInHeaderGespeichert) {
                fscanf(datei,"%d",&bildbreite);
                fscanf(datei,"%d",&bildhoehe);
                fscanf(datei,"%d",&maxHelligkeit);
                zahlenInHeaderGespeichert=1;
            }

            //Pixel dezimal speichern, wenn Datei im P3-Format vorliegt
            if(format[0]=='P' && format[1]=='3') {
                int i;
                pixelArrayP3=new int[bildbreite*bildhoehe*3];
                int *ptrP3=pixelArrayP3;
                for(i=0; i<(bildbreite*bildhoehe*3); i++) {
                    fscanf(datei,"%d",ptrP3);
                    ptr3++;
                    //printf("%d\n",*pixelArrayP3);

                }

            }

        }
            fclose(datei);
    }

    void dateiSchreiben()
    {
        FILE *image;
        image=fopen("image.ppm","wb");
        fprintf(image,"%c",format[0]);
        fprintf(image,"%c\n",format[1]);
        fprintf(image,"%d ", bildbreite);
        fprintf(image,"%d\n", bildhoehe);
        fprintf(image,"%d\n", maxHelligkeit);
        int *ptr=pixelArrayP3;
        if(format[0]=='P' && format[1]=='3') {
            int i;
            for(i=0; i<(bildbreite*bildhoehe*3); i++) {
                fprintf(image,"%d\n",*ptr);
                ptr++;
            }
        }

        fclose(image);
    }
};
int main()
{

    char path[255];
    scanf("%s", path);
    PPM file(path);
    file.einlesen();
    file.dateiSchreiben();
    return 0;
}

Die Datei enthält jetzt die Kopfzeile und die richtige Anzahl an Pixel allerdings stehen nach der maximalen Helligkeit ausschließlich Nullen.
 
Thallius

Thallius

Top Contributor
Kein wunder wenn du ptrP3 speicherst aber ptr3 incrementierst...

Übrigens nicht nur in Java sollte man aussagekräftige Variablen Namen verwenden. Gerade in komplexeren Sprachen bietet sich das an um solche Fehler zu vermeiden
 
W

wantri

Mitglied
Achso nein, tut mir leid, dass war nur ein Tippfehler. In dem eigentlich Program steht es als ptrP3.
 
H

httpdigest

Top Contributor
Hast du etwa den ganzen Programmcode per Hand hier in den JavaForum Editor abgeschrieben, statt ihn copy/pase zu kopieren? :)
Nächster Fehler: Die Prüfung von `while(!feof(datei))` auf EOF wird mehrere male true ergeben, da nicht alle Zeichen der Datei konsumiert werden. Erst nach einem nicht erfolgreichen getc() oder fscanf() (bzw. jeder Funktion, die aus einem Stream lesen möchte) wird der EOF Marker des Streams gesetzt, den feof() abfragt: https://stackoverflow.com/questions/1428911/detecting-eof-in-c#answer-1428924
Ich empfehle dir DRINGED einen Step-Debugger zu verwenden, und dann Zeile für Zeile deinen Code zu debuggen!!!! Du wirst merken, dass deine while-Schleife so keinen Sinn ergibt.
Du wirst nämlich feststellen, dass du beim Lesen der Pixelwerte jedesmal ein neues mit 0 initialisiertes Array (da das Array nicht in einem local scope deklariert wurde) erzeugst, und dann fscanf aber keinen Wert mehr lesen/verändern kann.
Nochmal: Das alles habe ich herausgefunden, indem ich einfach mal deinen Code in CLion geschmissen und kurz debugged habe. NUTZE EINEN DEBUGGER!
 
W

wantri

Mitglied
Mit Debuggern habe ich noch nie gearbeitet und der Debugger meiner IDE scheint nicht intuitiv zu bedienen zu sein. Da ich diese Übung sehr bald abgeben muss und sie die Grundlage für weitere Aufgaben ist, habe ich leider auch keine Zeit, um mich da einzuarbeiten.

Könntest Du mir bitte einen konkreten Tipp geben, wie der Programmcode abgeändert werden muss?
 
kneitzel

kneitzel

Top Contributor
Also der Hinweis mit dem Debugger ist extrem wichtig. Welche IDE nutzt Du denn, so dass der Debugger nicht zu bedienen ist? Evtl. ist es einfach Zeit, die IDE zu wechseln.

Dann hast Du aber auch ohne Debugger doch schon einen Hinweis bekommen: Deine While-Schleife ist ganz offensichtlich Unsinn. Das sogar mit Begründung!

Also so wie bei allen Problemen bei der Entwicklung kommt man immer wieder auf die gleichen Punkte zurück:
Beschreibe doch erst einmal, was Du genau machen willst. Das ist immer der Ausgangspunkt. Wenn Du den nicht hast, dann kannst Du es gleich sein lassen!, denn dann wäre es Zufall, wenn etwas funktionierendes heraus kommt.

Aber extra für Dich einfach einmal noch in klaren Worten incl. Anfang der Lösung:
Die ganze Schleife von wegen
Code:
 //Informationen der Datei in Membervariablen speichern
        while(!feof(datei)) {
erfüllt absolut keinen Sinn!
Du hast genau ein Bild in einer Datei. Und die Datei liest Du doch ein. Was musst Du lesen:
a) Format der Datei ("P3")
b) Header (Größe, Breite, Maximale Helligkeit)
c) Die Pixel

Und da kann ich erst einmal keine Schleife erkennen (Außer dann später bei dem Lesen der Pixel!)

Dann noch ein paar weitere Hinweise zum Code:
a) Wozu erst dieses Entfernen der Kommentare? Ist das ein fester Bestandteil der Anforderungen oder nur ein Workaround weil Kommentare dir Probleme machen? @mihe7 hatte Dir doch in einem anderen Thread bereits eine Funktion an die Hand gegeben, die Token liest und dabei Kommentare ignoriert...

b) Ich verstehe nicht, wieso Leute immer mit Pointern rumhantieren müssen. Der Array operator macht es aus meiner Sicht deutlich einfacher lesbar und erspart einem Increments auf Pointern und ganz Wichtig: Vermeidet idiotische Fehler wie in #1, wo du nicht einen lokale Variable sondern den gespeicherten Pointer auf das Array veränderst (pixelArrayP3 ist es in Deinem Code). Was dann die Sinnhaftigkeit des Array Operators hoffentlich klar aufzeigt ehe da Diskussionen aufkommen, dass Pointer doch auch gut zu lesen sind und so :)!
 
W

wantri

Mitglied
Also der Hinweis mit dem Debugger ist extrem wichtig. Welche IDE nutzt Du denn, so dass der Debugger nicht zu bedienen ist? Evtl. ist es einfach Zeit, die IDE zu wechseln.

Ich verwende CodeBlocks und habe zumindest auf den ersten Blick nicht herausgefunden, wie man hier debuggt.

Das Problem hat sich nun aber glücklicherweise durch den Tipp von httpdigest und Deiner Präzisierung erledigt.

Vielen Dank dafür!
 
kneitzel

kneitzel

Top Contributor
Also ich habe jetzt einmal CodeBlocks installiert und geschaut. Da scheint dann der gdb eingebunden zu sein, aber im Debugger Fenster sehe ich auch nicht wirklich viel Integration. Aber evtl. habe ich da etwas übersehen.

==> der gdb ist aber natürlich direkt nutzbar: https://www.geeksforgeeks.org/gdb-step-by-step-introduction/

Also breakpoints lassen sich im Editor setzen, schritt für schritt lässt sich mit der Oberfläche durchschalten. Variablen kann man sich im Debugger Fenster anzeigen lassen ("p variablenname" als Befehl eingeben!)

Aber ggf. willst Du nach anderen IDEs Ausschau halten, die den Debugger richtig integrieren. Unter Windows ist Visual Studio sehr stark verbreitet und die IDE bietet sehr viel. Die Community Edition ist zudem kostenlos und sie haben eine gute Webseite mit diversen Resourcen unter Dev Essentials. Da findet sich einiges - so man unter Windows entwickeln möchte.

Ich selbst nutze für C++ CLion von JetBrains. Diese IDE ist aber kostenpflichtig (Individuell, nur CLion ist 89€ im ersten Jahr, folgende Jahre werden günstiger) - ich selbst nutze das komplette Angebot, was 249€(ab 3. Jahr nur noch 149€) kostet - aber dafür habe ich alle Möglichkeiten, die JetBrains bietet.

Aber man kann sich auch andere IDEs ansehen für C++. Eclipse gibt es noch. Wenn man Linux verwendet, dann gibt es da diverse gute IDEs, die auch Debugger integrieren (Oder den xgdb als graphischen Debugger). Unter Mac wäre xcode evtl. tauglich, wobei ich xcode bisher nie für c++ genutzt habe sondern nur Objective-C und swift genutzt habe.
 
kneitzel

kneitzel

Top Contributor
Ich war jetzt am überlegen, im IDE Forum mal etwas zu schreiben zu dem Thema IDE, aber ich packe es erst einmal hier rein um dann evtl. das Ganze etwas erweitert mal als einen Blog Eintrag zu schreiben.

Jede Arbeit setzt voraus, dass man die notwendigen Werkzeuge kennt und diese auch anwenden kann. Da ist auch die Software Entwicklung nicht anders.

Klar - wenn ich etwas baue, dann brauche ich am Anfang oft kein Werkzeug. Ich kann mit Bauklötzen Türme bauen - Werkzeug braucht man da nicht. So braucht man auch nichts groß, um ein Hello World Programm zu schreiben.

Aber sobald man da etwas mehr machen will, dann braucht man gewisses Werkzeug. Und man muss mit dem Werkzeug um gehen können. Und es muss für die Aufgabe etwas taugen!

Bei der Software Entwicklung haben wir "Integrierted Development Environment" (Integrierte Entwicklungsumgebung), kurz IDE. Das bedeutet, man hat dann ein Programm, das man aufruft und in dieses sind viele andere Dinge mit integriert. So gehört zu den meisten IDEs:
- ein "intelligenter" Editor, der einen aktiv unterstützt. Hier sind viele Funktionen enthalten - vom automatischen Einrücken über farbliche Markierungen bis hin zur Analyse des Codes und der Unterbreitung von Vorschlägen.
- ein Debugger, der es einem Entwickler ermöglicht, Schritt für Schritt durch ein Programm zu gehen und sich dabei den Inhalt der Variablen anzeigen lassen kann.
- viele weitere Dinge, die teilweise auch im folgenden noch erwähnt werden (Und die sich auch gut integrieren in die IDE) ...

Sobald man Code schreiben will, der etwas mehr an Umfang haben soll und man sich dann auch mit eigenen Klassen und ähnlichem beschäftigt, dann sind auch weitere Tools sehr wichtig:
- Eine Source Code Verwaltung ist sehr wichtig. Damit kann man Änderungen betrachten und ggf. alte Versionen wieder her stellen. Das vermeidet ein Zumüllen des Codes, da Dinge einfach gelöscht werden können. Bei Bedarf kann man diese ja wieder aufrufen!
- Automatische Tests. Unit Tests sind in meinen Augen existenziell. Dazu kann man ein Unit Test Framework verwenden. Aber zur Not reichen hier auch viele kleine Programme, die dann gewisse Dinge testen. Dies hat den Vorteil, dass man nach Änderungen prüfen kann, ob man dabei etwas kaputt gemacht hat.

Klar kann man eine Wand ohne gewisse Hilfsmittel aufbauen. Aber wenn man gewisse Dinge nicht macht (z.B. ein Lot verwendet), dann wird die Wand nicht gerade sein... Die Hilfsmittel sollte man also einsetzen, denn diese vermeiden Fehler. Und sie beschleunigen (Beim Hausbau nutzt man einen Mischer um die Massen an Mörtel und Co zu mischen. Das fangen wir nicht mit Kelle in einem Bottich an ...)

Daher mein Appell an jeden, der etwas machen will:
- Beschafft euch zeitnah die notwendigen Tools.
- Erlernt den Umgang mit diesen Tools.

Die Zeit, die ihr am Anfang investiert werdet Ihr am Ende massiv einsparen. Und ihr erspart euch massiv Frust.

Und vieles ist eine einfache Gewöhnung.

Und nur um es klar zu stellen: Ich behaupte nicht, dass es nicht auch anders geht. Natürlich kann man auch komplexe Programme ohne IDE entwickeln. Auch ohne Unit Tests. (Aber dann gibt es andere Tests um die Qualität sicher zu stellen ....) Aber wenn man die Unterschiede sieht, dann will man doch eine IDE haben... :)
 

Ähnliche Java Themen

Anzeige

Neue Themen


Oben