2-Dimensionales Feld auf anderes Feld zeigen lassen



  • Hallo,
    ich hänge momentan bei meiner Klausurvorbereitung an der folgenden Aufgabe:

    Schreiben Sie sein C–Programm–Fragment (also kein vollst¨andiges Programm), welches zun¨achst einen
    positiven ganzzahligen Wert n einliest, danach voll dynamisch eine n × n–Matrix A von int–Zeigern
    allokiert und anschließend daf¨ur sorgt, dass jeder dieser n2 Zeiger auf ein (dynamisch angelegtes) Feld
    der L¨ange n vom Typ int zeigt.
    Alle notwendigen Header–Dateien m¨ussen eingebunden werden!
    Alle ben¨otigten Variablen sind zu vereinbaren!
    (Es kann davon ausgegangen werden, dass kein Speichermangel auftritt — entsprechende Fehlerabfragen
    k¨onnen fortgelassen werden!)

    Das hier habe ich bereits, jedoch der Teil, in dem ich jeder der n^2 Zeiger auf ein weiteres Feld zeigen lassen soll, verwirrt mich... Jemand hier der vielleicht versteht was da zu machen ist? 😕

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	int n,**A,i,j;
    
    	//n einlesen
    	printf("n = ");
    	scanf("%d",&n);
    
    	//Zeiger-Feld reservieren
    	A=(int**)malloc(n*sizeof(int*)); 
    
    	//Matrix Zeilen reservieren
    	for(i=0;i<n;i++) 
    		A[i]=(int*)malloc(n*sizeof(int));
    
    	//A mit Zufallszahlen belegen
    	for(i=0;i<n;i++)
    		for(j=0;j<n;j++)
    			A[i][j]=rand();
    
    	//Zeilen freigeben
    	for(i=0;i<n;i++) 	
    		free(A[i]);
    
    	//Felder freigeben
    	free(A); 
    	free(b);
    
    	return 0;
    }
    


  • Im Augenblick hast du eine n x n Matrix von int.
    Augebaut aus einem Vektor mit Zeigern auf int. Jeder dieser Zeiger verweist auf einen Bereich von int.

    Du sollst aber eine n x n Matrix von Zeigern auf int erzeugen (*int).

    Du kannst einen Vektor mit Zeigern auf Zeiger int generieren. Jeder dieser Doppelziger verweist auf einen Bereich mit Zeigern auf int.

    (Du hast ein paar * und [] zuwenig)



  • Du sollst eine Matrix von Zeigern bauen, nicht eine Matrix von Ganzzahlen. A muss vom Typ int*** sein.

    Um nicht einen Knoten im Kopf zu kriegen (was zweifellos der Sinn dieser Aufgabe ist), kann es helfen, so anzufangen:

    typedef int* IntArray;
    typedef IntArray* RowPtr;
    typedef RowPtr* MatrixPtr;
    
    MatrixPtr A;
    

    Und dann zuerst A zu allozieren (eine Spalte von n RowPtr), danach n-mal eine Row und danach in jeder Row n-mal ein IntArray von n Elementen.

    A=(int**)malloc(n*sizeof(int*));

    Und lass den Typecast (int**) weg. In C brauchst du den nicht. Unnötige Typecasts helfen nicht und können Fehler verschleiern.



  • also ich würde das so machen:

    int ***A; //variable für matrix aus n zeilen zu n spalten zu n arrays 
    int i, j, k;
    
    //Speicher für die Zeilen anfordern
    A = malloc(n * sizeof(int**));
    
    //Speicher für die Spalten anfordern
    for(i = 0; i < n; i++)
    {
         *(A + i) = malloc(n * sizeof(int*));
    }
    
    //Speicher für die dynamisch angelegten arrays anfordern
    for(i = 0; i < n; i++)
    {
         for(j = 0; j < n; j++)
         {
              *(*(A + i) + j) = malloc(n * sizeof(int));
         }
    }
    
    //jedes element mit 0 initialisieren
    for(i = 0; i < n; i++)
    {
         for(j = 0; j < n; j++)
         {
              for(k = 0; k < n; k++)
              {
                    *(*(*(A + i) + j) + k) = 0;
              }
         }
    }
    

    die schleifen kannst du natürlich auch zusammenfassen, wodurch das programm schneller ablaufen würde, allerdings habe ich das aus gründen der übersichtlichkeit jetzt nicht getan.

    ps: eigentlich ist das keine matrix sondern irgendwas dreidimensionales (wie auch immer das heißen mag).



  • Wade1234 schrieb:

    *(*(*(A + i) + j) + k) = 0;
    

    ist alles andere als übersichtlich.

    A[i][j][k] = 0;
    

    finde ich deutlich lesbarer.



  • Vielen Dank schon mal. Ich habe es jetzt mal so übernommen und das Programm läuft.
    Jedoch verstehe ich noch nicht so ganz die Bedeutung von diesen Ausdrücken, wie zum Beispiel

    ((*(A + i) + j) + k) = 0;



  • A[i] ist identisch mit *(A+i)

    demnach ist A[i][j] identisch mit *(A[i]+j) und nochmal ersetzt mit ((A+i)+j)

    die nächste Stufe ist für dich.

    Hinweis:
    Da für die Addition das Kommutativgesetz gilt, ist A+i = i+A
    Und für *(i+A) kannst du auch i[A] schreiben.



  • Ich habe das ganze also jetzt noch ein wenig angepasst und auch die anderen Aufgabenteile erfüllt.

    Und lass den Typecast (int**) weg.

    Das habe ich auch im Buch "C-für Dummies" so gelesen. Unser Professor besteht allerdings darauf, habe ihm extra deswegen auch eine Mail geschickt. Er ist der Meinung ich solle nicht so ein "Low-Budget" Buch nehmen und dann würde ich verstehen warum man den typecast braucht... 🙄

    Naja, so sieht jetzt jedenfalls das Ganze aus. Falls noch jemand nen Fehler findet bin ich dankbar für Verbesserungen 🙂

    #include <stdio.h>
    #include <stdlib.h>
    
    void drucken(int ***a,int n)
    {
    	int i,j,k;
    
    	for(i=0;i<n;i++)
    		for(j=0;j<n;j++)
    			for(k=0;k<n;k++)
    				printf("a[%d][%d][%d] = %d\n",i,j,k,a[i][j][k]);
    }
    
    void swap(int *x,int *y)
    {
    	int puffer;
    
    	puffer=*x;
    	*x=*y;
    	*y=puffer;
    }
    
    int main()
    {
    	//variable für matrix aus n zeilen zu n spalten zu n arrays
    	int ***A; 
    	int n,i,j,k;
    	int p=10,q=20;
    
    	//n einlesen
    	printf("n = ");
    	scanf("%d",&n);
    
    	//Speicher für die Zeilen anfordern 
    	A = (int***)malloc(n*sizeof(int**));
    
    	//Speicher für die Spalten anfordern 
    	for(i = 0; i < n; i++)  
         	A[i] = (int**)malloc(n*sizeof(int*)); 
    
    	//Speicher für die dynamisch angelegten arrays anfordern 
    	for(i = 0; i < n; i++) 
        	for(j = 0; j < n; j++)  
            	A[i][j] = (int*)malloc(n*sizeof(int)); 
    
    	//jedes element mit 0 initialisieren 
    	for(i = 0; i < n; i++) 
         	for(j = 0; j < n; j++) 
              	for(k = 0; k < n; k++) 
                	A[i][j][k] = 0;
    
    	//A an Funktion übergeben für Ausgabe
    	drucken(A,n);
    
    	//Funktion zum Tauschen von p und q
    	swap(&p,&q);
    	printf("\np = %d\nq = %d",p,q);
    
    	//Zeilen freigeben
    	for(i = 0; i < n; i++) 
        	for(j = 0; j < n; j++) 
         		free(A[i][j]);
    
        for(i = 0; i < n; i++)
         	free(A[i]);
    
    	//Feld freigeben
    	free(A); 
    
    	return 0;
    }
    


  • dennis98 schrieb:

    Vielen Dank schon mal. Ich habe es jetzt mal so übernommen und das Programm läuft.
    Jedoch verstehe ich noch nicht so ganz die Bedeutung von diesen Ausdrücken, wie zum Beispiel

    ((*(A + i) + j) + k) = 0;

    A + i: erhöhe die adresse von A um i * sizeof(A), faktisch i * 4 oder i * 8

    *(A + i): hole den wert an dieser adresse hervor (adresse des zeigers auf die i-te zeile)

    (A + i) + j: erhöhe die adresse um j * sizeof((A + i)), wieder j * 4 oder j * 8

    ((A + i) + j): hole wieder den wert an dieser adresse hervor (adresse des zeigers auf die j-te spalte in der i-ten zeile)

    (((A + i) + j) + k: erhöhe die adresse um k * sizeof(...), blabla k * 4 oder k * 8

    ((*(A + i) + j) + k): hole den wert an dieser adresse hervor, dies ist der wert des k-ten elements des arrays in der j-ten spalte der i-ten zeile.



  • dennis98 schrieb:

    Und lass den Typecast (int**) weg.

    Das habe ich auch im Buch "C-für Dummies" so gelesen. Unser Professor besteht allerdings darauf, habe ihm extra deswegen auch eine Mail geschickt. Er ist der Meinung ich solle nicht so ein "Low-Budget" Buch nehmen und dann würde ich verstehen warum man den typecast braucht... 🙄

    Echt jetzt? Sowas schreibt ein Professor?

    Bitte hier die erste Antwort lesen, die mit den 1800 Likes:
    https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc

    Wohlgemerkt, wir sind hier in C. In C++ müsste man den Cast machen, weil es sonst ein Fehler wäre. In C++ würde man aber malloc überhaupt nicht verwenden.



  • @Wade1234:
    Danke, das hat es etwas klarer gemacht 🙂

    @Printe

    Echt jetzt? Sowas schreibt ein Professor?

    Fand ich auch nicht ganz angebracht... Müll steht ja sicher auch nicht in diesem Buch drin. Er war da aber klipp und klar und besteht darauf, dass wir in der Klausur mit dem Typecast arbeiten. Da bleibt mir wohl nichts anderes übrig



  • dennis98 schrieb:

    Er war da aber klipp und klar und besteht darauf, dass wir in der Klausur mit dem Typecast arbeiten.

    Hat er irgendeine sachliche Begründung dafür gegeben? Würde mich interessieren.

    Da bleibt mir wohl nichts anderes übrig

    Nö. Macht das in der Prüfung so und vergesst es danach wieder.



  • Hat er irgendeine sachliche Begründung dafür gegeben? Würde mich interessieren.

    Nein, nur dass ich, wie schon gesagt, in einem "besseren" Buch nachschlagen soll und dann schon herausfinden würde aus welchem Grund das in C auch nötig ist.



  • So, jetzt wollte ich es wissen und hab mal gesucht: google "c malloc tutorial"

    Ich finde es erstaunlich, wie viele Online-Tutorials den Cast im Beispielcode verwenden. Erstaunlich auch, wie viele davon im erklärenden Text den Cast entweder überhaupt nicht erwähnen oder zwar erwähnen, aber nicht begründen.

    "Rühmliche" Ausnahme ist http://www.c-howto.de/tutorial/arrays-felder/speicherverwaltung, mit folgender Begründung:

    Es ist jedoch sauberer und erleichtert die Arbeit, wenn man den Code später in C++ Projekten verwenden möchte.

    Verschwiegen wird freilich, warum das Weglassen "unsauber" sein soll. Und ob man denselben Code mit C- und C++-Compilern übersetzen sollte, darüber kann man auch geteilter Meinung sein.

    Übrigens: Keins der gefundenen Tutorials castet den Pointer, der an free() übergeben wird, zurück auf void*. Warum nicht? Wäre das nicht "sauberer"?



  • dennis98 schrieb:

    Nein, nur dass ich, wie schon gesagt, in einem "besseren" Buch nachschlagen soll

    Welche Bücher empfiehlt er denn? Kannste die Liste hier mal posten?



  • dennis98 schrieb:

    Schreiben Sie sein C–Programm–Fragment (also kein vollst¨andiges Programm), welches zun¨achst einen
    positiven ganzzahligen Wert n einliest, danach voll dynamisch eine n × n–Matrix A von int–Zeigern
    allokiert ...

    warum nicht so?

    ...
        // Pointer-Matrix erzeugen
        int **matrix = malloc(n*n*sizeof(int*));  
        // Matrix füllen
        int x,y;
        for (x=0; x<n; x++)
            for (y=0; y<n; y++)
                matrix[x*y] = malloc (L);
        ...
    

    Also ohne hässliche Dreifachzeiger.


  • Mod

    Dynamische mehrdimensionale Felder sind übrigens DER Anwendungszweck für den VLAs (variable length arrays) perfekt sind. Also nicht in dem Sinne, dass man das ganze Dingen als VLA macht (das wäre genau der falsche Anwendungszweck), sondern dass man damit dynamisch den korrekten Zeigertypen für das malloc erzeugen kann. Somit hat man ganz korrekt ein zusammenhängendes Feld (viele Leute hier im Thread haben fälschlich etwas gezeigt, wo alles wild und ineffizient kreuz und quer verteilt ist) und hat trotzdem die bequeme Möglichkeit, die Adressierung der Indizes vom Compiler automatisch berechnen zu lassen, anstatt selber dafür eine Funktion schreiben zu müssen.

    Beispielhaft für ein 2D-Feld von ints:

    int N = 10, M = 20;  // keine Konstanten!
    int (*array)[M] = malloc(N*M*sizeof(int)); 
    array[4][7] = 3;  // Geht!
    

    Ich überlasse es dem geneigten Leser, dies auf die Aufgabenstellung zu übertragen, wo verlangt wird, dass es ein 2D-Feld von int* sein soll, die dann jeweils auf ein dynamisch erzeugtes 1D-Feld zeigen sollen. Wobei ich es komisch finde, dass man da kein 3D-Feld haben möchte. Absicht, um verschiedene Mechanismen zu üben, oder Unwissen, dass dies überhaupt möglich wäre?



  • Welche Bücher empfiehlt er denn?

    Dazu sagt er nichts genaues



  • Say no to triple* schrieb:

    warum nicht so?

    Weil das hier falsch ist:

    matrix[x*y] = malloc (L);
    

    Spiel die Doppelschleife mal durch und achte drauf, welche Werte x*y annimmt.



  • dennis98 schrieb:

    Welche Bücher empfiehlt er denn?

    Dazu sagt er nichts genaues

    Nicht mal ne Literaturliste zur Vorlesung? Das gehört sich aber eigentlich so ...


Anmelden zum Antworten