Probleme bei übergabe von Werten mittels Pointern



  • Hallo ihr lieben,

    ich habe im Rahmen eines C-Kurses an der Uni eine Programmieraufgabe, die mir Probleme bereitet. Grundsätzlich soll das Programm einen Wald simulieren und ihn anschließend abbrennen. Der Teil, bei dem ich Probleme habe bezieht sich aber auf eine Funktion des Programms, die eine Statistik erstellen soll. Dazu lasse ich den Wald 30x generieren und abbrennen ohne mir das ganze auf dem Bildschirm anschauen zu müssen. Jetzt wird die Belegung des Waldes mittels einer Zufallsfunktion durchgeführt. Diese benutzt einen Startwert, den ich mittels der time(NULL) funktion beim ersten mal generiere. Genau hier ist der Knackpunkt: der Startwert sollte eigentlich im Rahmen der Zufallsfunktion verändert werden und dann wieder an die Hauptfunktion zurück gegeben werden. Dadurch sollte eigentlich der Startwert für jeden Schleifendurchlauf unterschiedlich sein. Ist er aber nicht und genau das ist mein Problem :x

    Ich habe versucht unnötige Programmteile für euch zu entfernen, also nicht wundern, wenn irgendwo Bruchstücke sind, die euch unnötig erscheinen. Ich hoffe der Sinn ist dabei nicht verloren gegangen, stecke schon ganz schön drin in dem Programm und daher fällts mir schwer aus der Distanz drüber zu schaun! Bitte um Vergebung.

    int main (VOID)
    
    {
    int menue;
    long int startwert;
    
    printf ("Dieses Programm kann einen Wald Darstellen und abbrennen lassen oder die Brenndauer ermitteln. \n Bitte waehlen sie: \n");
    
    startwert=time(NULL)%253177;
    
                printf("statistik wird berechnet");
                statistik(&startwert);
    
    return 0;
    }
    
    int statistik(long int *startwert)
    {
    int rueckgabe=1,i=0,j;
    char wald[DIM][DIM];
    float p=0.8;          /* p ist eine Pflanzendichte, die zur belegung des Waldes benutzt wird. sie wird mit dem zufallswert verglichen */
    
    FILE *ergebnisfile;
    ergebnisfile=fopen("ergebnisdatei","w");     
    
    for (j=0;j<=30;j++)
    	{
    
    	belegung(p,wald,startwert);         //hier wird der wald generiert
    	randanzuenden(wald);                
    
    	while (rueckgabe==1)  //diese Schleife dient zur bestimmung der schritte
    		{
    		rueckgabe=zeitschritt(wald);
    		i++;
    		}
    	fprintf (ergebnisfile, "%i ", i);
    
    	}
    
    return 1;
    }
    
    //funktion zur generierung eines zufallswertes
    float zufallsfkt (long int *startwert)
    
    {
    float zufall;
    
    *startwert=(*startwert*7141+54773)%253177;   //hier wird der startwert neu best.
    
    zufall=*startwert/253177.0;
    return zufall;
    }
    
    //funktion zur waldgenerierung
    int belegung (float p, char a[DIM][DIM], long int *startwert)
    
    {
    int i,j;
    float zufall;
    
    for (i=0;i<DIM;i++)
    	{
    	for (j=0;j<DIM;j++)
    		{
    		zufall=zufallsfkt(&startwert);
    
    		if (zufall < p)
    			{
    			a[i][j]='T';
    			}
    		else
    			{
    			a[i][j]=' ';
    			}
    
    		}
    	}
    printf ("\n");
    return 0;
    }
    

    Danke, dass ihr bis hierher gelesen habt! Wenn ihr mir nun auch noch helfen könnt wäre das perfekt 😃


  • Mod

    Zeile 68 ist falsch. Das soltle dir dein Compiler anmeckern, wenn du ihm sagst, dass er mehr verdächtige Stellen anmeckern soll (d.h. du sollst Warnungen einschalten. Sollte man immer machen).



  • Tatsächlich ein Syntaxfehler an der Stelle... hab jetzt das & weg gemacht - leider schreibt er nun gar nichts mehr in mein Ergebnisfile :x
    Danke dir aber für den Hinweis!
    Mein Compiler hatte mir allerdings nichts gemeldet vorher! Hab Warnungen standardmäßig sowieso an.



  • Ok sorry die Info stimmte nicht... hatte nur die Datei nicht geschlossen, was dazu führte, dass während ich das Programm geöffnet hatte noch nicht in die Datei geschrieben wurde... Das ist jetzt zwar behoben, mein Problem ist allerdings noch immer nicht gelöst. Er rechnet in der Funktion Belegung immer mit dem Gleichen Startwert 😞


  • Mod

    Dann zeig bitte ein vollständiges Beispiel zum Problem, mit erwarteter und tatsächlicher Ausgabe. Momentan weiß ich nicht, ob die Fehler in deinem gezeigten Programm (beispielsweise sind die Funktionen allesamt in der falschen Reihenfolge) tatsächlich in deinem echten Programm enthalten sind oder sich bloß versehentlich in deinen Eröffnungsbeitrag eingeschlichen haben.



  • Ok ich hab jetzt versucht das, was ich hier gepostet hab nochmal einzeln zu compilieren um zu sehen, was ihr seht, wenn ihr das kopiert und festgestellt, dass es so nicht geht. Ich kopier mal das ganze Programm rein, damit ihr überhaupt was damit anfangen könnt...

    Wenn ich nach Programmstart "Statistik" wähle, soll (bisher!) einfach ein ergebnisfile erstellt werden, in dem 30 zahlen stehen, und zwar ist jede zahl die anzahl der Schleifendurchläufe der Zeitschrittfunktion. Das Problem ist wie gesagt, dass diese 30 Zahlen immer gleich sind, eben weil immer mit dem gleichen Startwert gerechnet wird. Ich stelle mir das eigentlich so vor, dass bei Aufruf der Funktion "belegung" ein Feld generiert wird mit zufallswerten von "T" oder " " und beim nächsten durchgang ein Feld, an dem die Stellen eben anders belegt sind.
    Dazu müsste aber der Aufruf der zufallsfkt in Zeile 188 einen neuen Startwert zurück bekommen aus der Funktion in Zeile 163. Dass die Zufallsfunktion selbst funktioniert zeigt die Tatsache, dass der erste Programmteil ja funktioniert.

    #include <stdio.h>
    #include <time.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <windows.h>
    #define DIM 20
    
    float zufallsfkt (long int *startwert);
    int belegung (float p, char a[DIM][DIM],long int *startwert);
    int randanzuenden (char anzuenden[DIM][DIM]);
    int ausgabefunktion (char ausgabematrix[DIM][DIM]);
    int zeitschritt (char wald[DIM][DIM]);
    int hauptfunktion (long int *startwert);
    int statistik(long int *startwert);
    
    int main (VOID)
    
    {
    int menue;
    long int startwert;
    int rueckgabe=1;
    
    printf ("Dieses Programm kann einen Wald Darstellen und abbrennen lassen oder die Brenndauer ermitteln. \n Bitte waehlen sie: \n");
    
    while (rueckgabe==1)
        {
    
        printf (" 1 Wald darstellen und abbrennen lassen \n 2 Statistik ermitteln \n 3 Programm beenden \n");
        scanf ("%i", &menue);
    
        startwert=time(NULL)%253177;
    
        switch (menue)
            {
            case 1:
                printf("\n wald wird abgebrannt");
                rueckgabe=hauptfunktion(&startwert);
    
                break;
            case 2:
                printf("statistik wird berechnet");
                rueckgabe=statistik(&startwert);
                break;
            case 3:
                return 0;
                break;
            default:
                printf("bitte geben sie eine zahl von 1-3 ein! \n");
            }
        }
    return 0;
    }
    
    int statistik(long int *startwert)
    {
    int rueckgabe=1,i=0,j;
    char wald[DIM][DIM];
    float p=0.8;
    
    FILE *ergebnisfile;
    
    ergebnisfile=fopen("ergebnisdatei","w");
    
    for (j=0;j<=30;j++)
    	{
    
    	belegung(p,wald,startwert);
    	randanzuenden(wald);
    
    	while (rueckgabe==1)
    		{
    		rueckgabe=zeitschritt(wald);
    		i++;
    		}
    	fprintf (ergebnisfile, "%i ", i);
    
    	}
    fclose(ergebnisfile);
    return 1;
    }
    
    int hauptfunktion (long int *startwert)
    
    {
    int rueckgabe=1;
    char wald[DIM][DIM];
    float p;
    
    printf ("Geben sie die Bepflanzungsdichte als Zahl zwischen 0 und 1 an! \n");
    scanf ("%f", &p);
    
    if (0<=p)
    	{
    	if (p<=1)
    
    		belegung(p,wald,startwert);
    
    	else
    		printf ("Zahlen >1 sind nicht zulaessig \n");
    	}
    else
    	{
    	printf ("Zahlen <0 sind nicht zulaessig \n");
    	}
    
    ausgabefunktion(wald);
    Sleep(1000);
    system("cls");
    
    randanzuenden(wald);
    ausgabefunktion(wald);
    Sleep(1000);
    system("cls");
    
    while (rueckgabe==1)
    	{
    	rueckgabe=zeitschritt(wald);
    	ausgabefunktion(wald);
    	Sleep(1000);
    	system("cls");
    
    	}
    
    return 1;
    }
    
    float zufallsfkt (long int *startwert)
    
    {
    float zufall;
    
    *startwert=(*startwert*7141+54773)%253177;
    
    zufall=*startwert/253177.0;
    return zufall;
    }
    
    int belegung (float p, char a[DIM][DIM], long int *startwert)
    
    {
    int i,j;
    float zufall;
    
    for (i=0;i<DIM;i++)
    	{
    	for (j=0;j<DIM;j++)
    		{
    		zufall=zufallsfkt(startwert);
    
    		if (zufall < p)
    			{
    			a[i][j]='T';
    			}
    		else
    			{
    			a[i][j]=' ';
    			}
    
    		}
    	}
    printf ("\n");
    return 0;
    }
    
     int randanzuenden (char anzuenden[DIM][DIM])
    
    {
    int i;
    
    for (i=0;i<DIM;i++)
    	{
    	if (anzuenden[i][0]=='T')
    		{
    		anzuenden[i][0]='*';
    		}
    
    	}
    return 0;
    }
    
    int zeitschritt (char wald[DIM][DIM])
    
    {
    int i,j,hilfe1;
    
    hilfe1=0;
    
    for (i=0;i<DIM;i++)
    	{
    	for (j=0;j<DIM;j++)
    		{
    		switch(wald[i][j])
    			{
    			case '*':
    				hilfe1++;
    
    				wald[i][j]='!';
    
    				if (wald[i][j-1]=='T' && j>0) 	//das feld nicht "nach oben" verlassen
    					{
    					wald[i][j-1]='x';
    					}
    
    				if (wald[i][j+1]=='T' && j<DIM-1) 	//das feld nicht "nach unten" verlassen
    					{
    					wald[i][j+1]='x';
    					}
    
    				if (wald[i-1][j]=='T' && i>0) 	//das feld nicht "nach links" verlassen
    					{
    					wald[i-1][j]='x';
    					}
    
    				if (wald[i+1][j]=='T' && i<DIM-1)	//das feld nicht "nach rechts" verlassen
    					{
    					wald[i+1][j]='x';
    					}
    
    				break;
    
    			case '!':
    
    				hilfe1++;
    
    				wald[i][j]='.';
    
    				break;
    
    			}
    		}
    	}
    for (i=0;i<DIM;i++)
    	{
    	for (j=0;j<DIM;j++)
    		{
    		if (wald[i][j]=='x')
    			{
    			wald[i][j]='*';
    			}
    		}
    	}
    
    if (hilfe1==0)
    	return 0;
    else
    	return 1;
    
    }
    
    int ausgabefunktion (char ausgabematrix[DIM][DIM])
    
    {
    int i,j;
    
     for (j=0;j< DIM;j++)
    	{
    	printf("\n");
    	for (i=0;i<DIM;i++)
    		{
    		 	printf("%c ", ausgabematrix[i][j]);
    		}
    	}
    	printf("\n");
    	printf("\n");
    
    return 0;
    }
    


  • SeppJ schrieb:

    Dann zeig bitte ein vollständiges Beispiel zum Problem, mit erwarteter und tatsächlicher Ausgabe. Momentan weiß ich nicht, ob die Fehler in deinem gezeigten Programm (beispielsweise sind die Funktionen allesamt in der falschen Reihenfolge) tatsächlich in deinem echten Programm enthalten sind oder sich bloß versehentlich in deinen Eröffnungsbeitrag eingeschlichen haben.

    Spielt es denn eine Rolle, in welcher Reihenfolge die Funktionen im Quelltext stehen? Wenn während des Hauptprogramms eine Funktion aufgerufen wird springt der Compiler doch einfach zu der Funktion oder?



  • Also im Prinzip soll das Programm folgendes machen:

    edit: ich kriegs leider nicht hin, das hier so zu schreiben, dass erkennbar ist, wie die Felder durchlaufen werden ...
    edit2: T = Tree, *=brennender baum,!=rauchender baum,.=ausgebrannter baum
    Wald bepflanzen
    TTT__T
    __T__T
    TTT_TT

    Wald anzünden

    **\__\
    __T__T
    TTT_TT

    Zeitschritt gehen (hier pflanzt sich der Brand fort):

    1. Schritt:
    !!!__!
    \*\*
    TTT_TT

    2. Schritt:
    ...__.
    !!
    TT*_T*

    3. Schritt:
    ...__.
    ..
    T*!_*!

    Undso weiter soll sich der Brand dann forpflanzen. Wenn weder in horizontaler, noch in vertikaler Richtung ein Baum anbrennt, dann erstickt der Brand - die Funktion endet. Sinn der Funktion Statistik ist es nun, diesen Vorgang 30x durchzuführen und nach jedem Durchgang die Anzahl der durchlaufenen Schritte zählt und in ein File ausgibt.
    Nur das momentan eben bei allen 30 Durchgängen das Gleiche Feld konstruiert wird.



  • Hast du deine Theorie, dass das immer der gleiche Startwert ist, auch mal anders überprüft als nur durch Ausgabe der Zahl der Schleifendurchläufe in eine Datei? Vielleicht ist der Fehler auch woanders. Beispielsweise würde ich naiverweise erwarten, dass die Variable rueckgabe jeweils vor der while-Schleife wieder zu 1 initialisiert wird.



  • Bashar schrieb:

    Hast du deine Theorie, dass das immer der gleiche Startwert ist, auch mal anders überprüft als nur durch Ausgabe der Zahl der Schleifendurchläufe in eine Datei? Vielleicht ist der Fehler auch woanders. Beispielsweise würde ich naiverweise erwarten, dass die Variable rueckgabe jeweils vor der while-Schleife wieder zu 1 initialisiert wird.

    Ich hatte versucht, mir die Adressen der einzelnen Pointer anzuzeigen, denn die müssten ja theoretisch alle auf die gleiche Adresse zeigen. Leider funktioniert das Adresse ausgeben auf meinem HeimPC (Windows) nicht so gut, wie in der uni (da haben wir linux)... irgendwie hab ich da keine Adressen bekommen sondern nur Nullen oder kryptische Zeichen - je nachdem, welchen Platzhalter ich benutzt habe.

    Die Variable Rückgabe dient eigentlich nur zur Bestimmung, wann die Schleife beendet werden soll, nämlich dann, wenn nix mehr brennt oder raucht. Daher sollen die ja gerade immer auf den gleichen Wert gebracht werden, damit die Whileschleife läuft.

    Aber ich probier mal mir die Startwerte an unterschiedlichen Stellen ausgeben zu lassen - mal sehen ob sich da was ergibt.

    edit: also: wenn ich in die schleife geh und dort 30x den Startwert printen lass, dann gibt er tatsächlich 30 unterschiedliche Startwerte aus... innerhalb der Whileschleife aber ist der Startwert immer gleich. Das soll ja auch so sein, denn für einen Durchlauf der for-Schleife soll i-mal die while Schleife durchlaufen werden, wobei i die anzahl der Zeitschritte darstellt. Diese Zeitschritte vollziehen sich ja alle am selben Wald.

    for (j=0;j<=30;j++)
    	{
    
    	belegung(p,wald,startwert);
    	randanzuenden(wald);
    
    	while (rueckgabe==1)
    		{
    
    		rueckgabe=zeitschritt(wald);
    		i++;
    		}
    	fprintf (ergebnisfile, "%i ", i);
    
    	}
    fclose(ergebnisfile);
    return 1;
    

  • Mod

    setjd schrieb:

    Spielt es denn eine Rolle, in welcher Reihenfolge die Funktionen im Quelltext stehen?

    Ja.

    Wenn während des Hauptprogramms eine Funktion aufgerufen wird springt der Compiler doch einfach zu der Funktion oder?

    Der Compiler springt gar nicht (jedenfalls nicht in deinem Quelltext), der Compiler liest den Quelltext und übersetzt ihn in Maschinensprache. Dieser enthält dann vielleicht Sprungbefehle.

    Was hier passiert ist implizite Funktionsdeklaration, eine Sonderheit der Sprache C. Wenn eine Funktion aufgerufen wird, ohne vorher deklariert worden zu sein (das Konzept von Deklaration und Definition und wozu das gut ist, sind dir bekannt?), dann wird automatisch ein Funktionsprototyp angenommen, der int zurück gibt und genau die Argumente nimmt, die der Funktion übergeben wurden. Dies kann leicht Probleme verursachen, wenn die so deklarierte Funktion und die eigentliche Funktion nicht zusammen passen.


  • Mod

    setjd schrieb:

    Bashar schrieb:

    Hast du deine Theorie, dass das immer der gleiche Startwert ist, auch mal anders überprüft als nur durch Ausgabe der Zahl der Schleifendurchläufe in eine Datei? Vielleicht ist der Fehler auch woanders. Beispielsweise würde ich naiverweise erwarten, dass die Variable rueckgabe jeweils vor der while-Schleife wieder zu 1 initialisiert wird.

    Ich hatte versucht, mir die Adressen der einzelnen Pointer anzuzeigen, denn die müssten ja theoretisch alle auf die gleiche Adresse zeigen. Leider funktioniert das Adresse ausgeben auf meinem HeimPC (Windows) nicht so gut, wie in der uni (da haben wir linux)... irgendwie hab ich da keine Adressen bekommen sondern nur Nullen oder kryptische Zeichen - je nachdem, welchen Platzhalter ich benutzt habe.

    Die Variable Rückgabe dient eigentlich nur zur Bestimmung, wann die Schleife beendet werden soll, nämlich dann, wenn nix mehr brennt oder raucht. Daher sollen die ja gerade immer auf den gleichen Wert gebracht werden, damit die Whileschleife läuft.

    Aber ich probier mal mir die Startwerte an unterschiedlichen Stellen ausgeben zu lassen - mal sehen ob sich da was ergibt.

    Zeig mal konkret, wie du die Ausgaben machst und was du erwartest. Gerade bei der Ausgabe von Pointern machen Anfänger gerne viel falsch.



  • setjd schrieb:

    Die Variable Rückgabe dient eigentlich nur zur Bestimmung, wann die Schleife beendet werden soll, nämlich dann, wenn nix mehr brennt oder raucht. Daher sollen die ja gerade immer auf den gleichen Wert gebracht werden, damit die Whileschleife läuft.

    Ja, aber wenn rueckgabe einmal ungleich 1 geworden ist und somit die Schleife abgebrochen wird, gibt es nichts mehr, wodurch rueckgabe jemals wieder auf 1 gesetzt werden kann; somit wird die Schleife nie wieder betreten, und i wird sich auch nie wieder ändern. Das könnte deine Ausgabe erklären.

    Wozu du jetzt Zeiger ausgeben willst ist mir schleierhaft. Du hattest gesagt, der Startwert ist immer der gleiche -- dann gib doch den Startwert aus. Falls deine Theorie ist, dass das Feld immer das gleiche ist, dann gib das Feld aus.



  • SeppJ schrieb:

    ]Zeig mal konkret, wie du die Ausgaben machst und was du erwartest. Gerade bei der Ausgabe von Pointern machen Anfänger gerne viel falsch.

    Um mir die Adresse auszugeben, auf die der Pointer "startwert" zeigt tipp ich einfach

    printf("%p",&startwert);
    

    in meinen quellcode



  • Bashar schrieb:

    setjd schrieb:

    Die Variable Rückgabe dient eigentlich nur zur Bestimmung, wann die Schleife beendet werden soll, nämlich dann, wenn nix mehr brennt oder raucht. Daher sollen die ja gerade immer auf den gleichen Wert gebracht werden, damit die Whileschleife läuft.

    Ja, aber wenn rueckgabe einmal ungleich 1 geworden ist und somit die Schleife abgebrochen wird, gibt es nichts mehr, wodurch rueckgabe jemals wieder auf 1 gesetzt werden kann; somit wird die Schleife nie wieder betreten, und i wird sich auch nie wieder ändern. Das könnte deine Ausgabe erklären.

    Wozu du jetzt Zeiger ausgeben willst ist mir schleierhaft. Du hattest gesagt, der Startwert ist immer der gleiche -- dann gib doch den Startwert aus. Falls deine Theorie ist, dass das Feld immer das gleiche ist, dann gib das Feld aus.

    Du hast recht... FETTES DANKE! Ich hatte die Variablen Rückgabe und i im Deklarationsteil der Funktion mit 1 und 0 initialisiert, hätte das aber innerhalb der for-Schleife machen müssen. Danke wie gesagt - jetzt funktionierts.

    Ich wollte die Zeigeradressen ausgeben um zu erfahren, ob die Zeiger alle auf die gleiche Adresse zeigen, denn ich hab ja die ganze Zeit vermutet, dass da irgendwo der Wert nicht übergeben wird. Wenn ich quasi in jeder Funktion die Zeiger, die ich dort definiert habe einmal ausgeben lasse und die alle gleich sind, kann ich davon ausgehen, dass jede Funktion mit dem selben Speicherplatz arbeitet. Das scheint ja jetzt auch der Fall zu sein!


  • Mod

    setjd schrieb:

    SeppJ schrieb:

    ]Zeig mal konkret, wie du die Ausgaben machst und was du erwartest. Gerade bei der Ausgabe von Pointern machen Anfänger gerne viel falsch.

    Um mir die Adresse auszugeben, auf die der Pointer "startwert" zeigt tipp ich einfach

    printf("%p",&startwert);
    

    in meinen quellcode

    Bingo. Das ist die Adresse von Startwert selbst, nicht, worauf gezeigt wird.



  • SeppJ schrieb:

    Das ist die Adresse von Startwert.

    Ja schon, vielleicht ist das ein wenig missverständlich, dass meine ursprüngliche Variable in main() "startwert" heißt und meine Pointer in den Funktionen eben auch "*startwert*" mit dem Operator & greife ich doch immer auf eine Adresse zu und im Prinzip spielt es von der Syntax her doch keine Rolle ob ich mir mit dem & die Adresse eines Pointers oder einer Normalen Variablen ausgeben will oder?

    Ich weiß allerdings jetzt nicht, ob der Pointer selbst auch nochmal einen Speicherplatz hat oder ob das der gleiche Platz ist, auf den er zeigt?! Darauf willst du vermutlich hinaus nehm ich an...
    Von der Logik her würd ich jetzt sagen der Pointer müsste ne eigene Adresse haben.

    edit: ok, nachdem du deine Antwort editiert hast ists klarer 😉
    edit2: aber aus interesse: wie würd ich jetzt die adresse des pointers selbst ausgeben? geht das überhaupt?


  • Mod

    setjd schrieb:

    edit2: aber aus interesse: wie würd ich jetzt die adresse des pointers selbst ausgeben? geht das überhaupt?

    Die Adresse des Pointers selbst hast du doch ausgegeben.

    int foo;
    int *bar = &foo;
    
    printf("Adresse von foo: %p\n", &foo);
    printf("Adresse von bar: %p\n", &bar);
    printf("Wert von bar (also Adresse von foo): %p\n", bar);
    

    http://ideone.com/1PSQGA


Log in to reply