Hilfe bei Matrixmultiplikation in C als Funktion



  • Win 2D-Array ist ein zusammenhängender Speicherbereich, bei dem der ganze rechte Index am schnellsten wechselt.

    Wenn du ein 2D-Array hast (int matrix[ZEILEN][SPALTEN]), dann kannst du auf die Elemente über einen Zeiger (*int zeigeraufint) zugreifen, der auf den Anfang vom Speicher zeigt.
    Dazu musst du wissen, wieviel Spalten das Array hat:

    matrix[z][s] ist dabei äquvalent zu *(zeigeraufint + z * SPALTEN + s)



  • Okay, vielen Dank für eure Hilfestellungen, aber ich checks gerade gar nicht.
    Das, was ich davor gemacht habe, war auch nur Stückwerk, weil ich einfach überall nen Pointersymbol vorgeschrieben habe.
    Ich versuchs jetzt mal ganz von vorne, vielleicht kommt ja was dabei heraus.



  • Ich bin schon wieder einen Schritt vorwärts gekommen.
    Jetzt habe ich die Funktion Zeiger auf Matrix1 und Matrix2 anfordern lassen.

    int multiplikation(int (*m1)[10],int (*m2)[10],int *a1, int *b1, int *b2){
    	int h1,h2,h3,Summe=0;
    	int ergebnis[10][10];
    	for (h1 = 0; h1 < *a1; h1++){
    				for (h2 = 0; h2 < *b2; h2++){
    					for (h3 = 0; h3 < *b1; h3++){
    						Summe = Summe + (*m1)[10]*(*m2)[10];
    					}
    				ergebnis[h1][h2] = Summe;
    				Summe = 0;
    				}
    			}
    			for (h1 = 0; h1 < *a1; h1++) {
    				for (h2 = 0; h2 < *b2; h2++){
    					printf("%i\t", ergebnis[h1][h2]);
    				      }
    				printf("\n");
    				}
    	return 0;
    }
    
    int main(void){
    	int a1,a2,b1,b2;
    	int h1,h2;
    	int matrix1[10][10], matrix2[10][10];
    	printf("Zeilen der ersten Matrix angeben:");
    	scanf("%i",&a1);
    	printf("Spalten der ersten Matrix angeben:");
    	scanf("%i",&a2);
    	printf("Zeilen der zweiten Matrix angeben:");
    	scanf("%i",&b1);
    	printf("Spalten der zweiten Matrix angeben:");
    	scanf("%i",&b2);
    	if (a2!=b1){
    		printf("Die Matrizen k�nnen nicht multipliziert werden!\n");
    	}
    	printf("Elemente der ersten Matrix eingeben(v.l.n.r.)");
    				for (h1 = 0; h1 < a1; h1++){
    				    for (h2 = 0; h2 < a2; h2++){
    				    	scanf("%i", &matrix1[h1][h2]);
    				    }
    				}
    				printf("Elemente der zweiten Matrix eingeben(v.l.n.r.)");
    				for (h1 = 0; h1 < b1; h1++){
    					for (h2 = 0; h2 < b2; h2++){
    						scanf("%i", &matrix2[h1][h2]);
    					}
    				}
    	multiplikation(matrix1, matrix2,*a1,*b1,*b2);
    return 0;
    }
    

    Jetzt scheint wider was bei der Übergabe falsch zu sein. Es erscheint dieselbe Fehlermeldung wie vorhin.



  • Broetchen93 schrieb:

    ..., weil ich einfach überall nen Pointersymbol vorgeschrieben habe.

    So geht es nicht.
    Du musst schon wissen, was da passiert.

    Warum sind a1, b1, b2 in multiplikation Pointer?

    Warum dereferenzierst du a1, b1, b2 in main beim Aufruf von multiplikation, obwohl es einfach int sind?

    Wie bist du auf (*m1)[10] gekommen (das ist richtig) , wenn der Rest so verkehrt ist?



  • Broetchen93 schrieb:

    Zudem ist die Multiplikation ziemlich sinnfrei, wenn du das Ergebnis nicht aus der Funktion raus bekommst.

    Hast du recht, aber ich schaffe es anders nicht. Funktionieren tut es ja.

    Was heißt "anders"?
    Da funktioniert überhaupt nichts.
    Du kannst das Ergebnis nicht verwenden und das sollte selbst dir als Anfänger klar sein, dass das Schrott ist; Note 6, setzen.
    Höre auf rumzufrickeln und übernehme die konkreten gegebenen Hinweise;
    was fragst du überhaupt hier im Forum wenn du die Hinweise nicht übernimmst?
    Hinweis:

    int multiplikation(int ergebnis[10][10], int matrix1[10][10],int matrix2[10][10], int b1, int h1, int b2, int h2){
    ....
    


  • Was heißt "anders"? 
    Da funktioniert überhaupt nichts. 
    Du kannst das Ergebnis nicht verwenden und das sollte selbst dir als Anfänger klar sein, dass das Schrott ist; Note 6, setzen. 
    Höre auf rumzufrickeln und übernehme die konkreten gegebenen Hinweise; 
    was fragst du überhaupt hier im Forum wenn du die Hinweise nicht übernimmst?
    

    Du hast natürlich recht, ich habe viele Hilfestellungen einfach übergangen.
    Deswegen springe ich jetzt auch noch einen Schritt zurück.
    Den Programmcode der Teilaufgabe a habe ich nun abgeändert.

    int multiplikation(int matrix1[10][10],int matrix2[10][10],int ergebnis[10][10],int a1,int b1, int b2,int h1, int h2, int Summe){
    	int h3;
    	for (h1 = 0; h1 < a1; h1++){
    				for (h2 = 0; h2 < b2; h2++){
    					for (h3 = 0; h3 < b1; h3++){
    						Summe = Summe + matrix1[h1][h3]*matrix2[h3][h2];
    					}
    				ergebnis[h1][h2] = Summe;
    				Summe = 0;
    				}
    			}
    	return ergebnis[10][10];
    }
    

    Das ist nur die Funktion, in der ich allerdings immernoch die Variable h3 deklariert habe, da ich diese in der Hauptfunktion ja gar nicht brauche.
    Der Rest wird aus dem main übergeben. Die Ausgabefunktion findet auch dort statt.

    int main(void){
    	int a1,a2,b1,b2;
    	int h1,h2,Summe=0;
    	int matrix1[10][10], matrix2[10][10],ergebnis[10][10];
    	printf("Zeilen der ersten Matrix angeben:");
    	scanf("%i",&a1);
    	printf("Spalten der ersten Matrix angeben:");
    	scanf("%i",&a2);
    	printf("Zeilen der zweiten Matrix angeben:");
    	scanf("%i",&b1);
    	printf("Spalten der zweiten Matrix angeben:");
    	scanf("%i",&b2);
    	if (a2!=b1){
    		printf("Die Matrizen k�nnen nicht multipliziert werden!\n");
    	}
    	printf("Elemente der ersten Matrix eingeben(v.l.n.r.)");
    				for (h1 = 0; h1 < a1; h1++){
    				    for (h2 = 0; h2 < a2; h2++){
    				    	scanf("%i", &matrix1[h1][h2]);
    				    }
    				}
    				printf("Elemente der zweiten Matrix eingeben(v.l.n.r.)");
    				for (h1 = 0; h1 < b1; h1++){
    					for (h2 = 0; h2 < b2; h2++){
    						scanf("%i", &matrix2[h1][h2]);
    					}
    				}
    	multiplikation(matrix1,matrix2,ergebnis,a1,b1,b2,h1,h2,Summe);
    	for (h1 = 0; h1 < a1; h1++) {
    					for (h2 = 0; h2 < b2; h2++){
    						printf("%i\t", ergebnis[h1][h2]);
    					      }
    					printf("\n");
    					}
    return 0;
    }
    

    So, zur nächsten Teilaufgabe:

    ..., weil ich einfach überall nen Pointersymbol vorgeschrieben habe.

    So geht es nicht.
    Du musst schon wissen, was da passiert.

    Absolut, das war bloß ein Versuch.

    Wie bist du auf (*m1)[10] gekommen (das ist richtig) , wenn der Rest so verkehrt ist?

    Das übernahm ich aus dem Skript, wo ein ähnliches Beispiel behandelt wurde.
    So. Die Übergabe und die Berechnung sollen also per Pointerarithmetik umgesetzt werden.
    So wie ich das verstanden habe, zeigt int (*m1)[10] auf den Anfang einer Matrix der Dimension 10 mit Datentyp int. Das gleiche eben für die anderen beiden int (*m2)[10] und int (*e)[10].
    Ich übergebe immernoch matrix1,matrix2 und ergebnis. Also auf deren [0][0] "Koordinate" wird quasi gezeigt.



  • Wofür ist denn Summe als Paramter von multiplikation?.
    Wenn der Wert ungleich 0 ist, kommt Müll raus.

    Laut http://cdecl.org ist *int (m2)[10] "declare m2 as pointer to array 10 of int"
    Also ein Zeiger auf ein Array aus 10 int.

    Die Benutzung ist innerhalb der Funktion auch nich anders als mit Arrayschreibweise.
    m2[0] ist die erste Zeile
    m2[3] ist die vierte Zeile
    m2[0][3] ist das 4. Element in der 1. Zeile

    Warum hatte ich ergebnis als ersten Paramter stehen?
    Nun, in der C-Standardlibrary ist das so üblich, dass das Ziel zuerst kommt. (strcpy, memcpy, fgets, )
    Es soll etwas die Reihenfolge der Gleichung nachbilden. Da steht ergebnis auch links ( ergebnis = m1 * m2 )
    😉



  • also auf die einzelnen Elemente einer Matrix greifst du z.B. so zu:

    *(matrix+zeile*anzahl_spalten+spalte)=wert;
    

    das 1. Element in der 1. Spalte ist also *(matrix+0), das zweite *(matrix+1).
    das 1. Element in der 2. spalte *(matrix+1*anzahlspalten+0), also bei einer Matrix mit 10 Spalten *(matrix+10) usw.

    vereinfacht gesagt zeigt *matrix auf das 1. Element und *(matrix+x) auf das x-te Element, den Rest machen dann die Kenntnisse der Mathematik.



  • HansKlaus schrieb:

    also auf die einzelnen Elemente einer Matrix greifst du z.B. so zu:

    *(matrix+zeile*anzahl_spalten+spalte)=wert;
    

    das 1. Element in der 1. Spalte ist also *(matrix+0), das zweite *(matrix+1).
    das 1. Element in der 2. spalte *(matrix+1*anzahlspalten+0), also bei einer Matrix mit 10 Spalten *(matrix+10) usw.

    vereinfacht gesagt zeigt *matrix auf das 1. Element und *(matrix+x) auf das x-te Element, den Rest machen dann die Kenntnisse der Mathematik.

    Dann soltest du auch dazu schreiben, wie matrix definiert ist.
    Nur so viel: *int (matrix)[10] ist es nicht.

    ^(Im Übrigen hatte ich das auch schon geschrieben.)^



  • naja er wollte doch Zeigerarithmetik benutzen, also kann man doch schreiben

    int funktion(int *matrix)
    {
    *(matrix+3*10+3)=0; //zulässig
    matrix[3][4]=0; //auch zulässig aber keine Zeigerarithmetik
    }
    
    int main()
    {
    int matrix[10][10];
    
    funktion(matrix);
    }
    

    oder sehe ich das falsch?



  • Wofür ist denn Summe als Paramter von multiplikation?.
    Wenn der Wert ungleich 0 ist, kommt Müll raus.

    Summe dient als Zwischenspeicherung der Zwischenergebnisse der einzelnen Rechnung. Diese werden so lange aufsummiert, dass der Wert des Ergebnisvektors herauskommt.
    Außerdem wird summe nach jeder Übergabe an den Ergebnisvektor wieder auf 0 zurückgesetzt.

    Ich verstehe jetzt nicht, in wie fern man mit Pointern rechnen kann.
    Eigentlich enthalten die ja nur die Adresse. Gibt es da nicht dieses "Gegensymbol &", um den Inhalt der (nicht-Pointer) Variable wieder hervorzuholen?



  • ja genau Pointer zeigen auf eine Adresse.

    wenn du z.B. schreibst

    int *pointer;
    
    pointer=0;
    

    zeigt dieser auf die Adresse 0 (wir nehmen jetzt mal für die Zukunft an, dass dir das Betriebssystem keine Speicherzugriffsverletzung um die Ohren haut) und du kannst über

    *pointer=12345;
    

    den Wert 12345 an die Adresse 0 schreiben. Schreibst du dann

    *(pointer + 1)=23456;
    

    so wird in die um 1 erhöhte Adresse in pointer (also 1) der Wert 23456 geschrieben. Also vereinfacht gesagt: mit * schreibst du irgendwas in die Adresse rein, ohne *legst du die Adresse fest.

    Das & gibt die Adresse der Variable raus, also wenn du möchtest, dass pointer auf x zeigt, schreibst du pointer=&x, und wenn du möchtest, dass pointer auf die zweite Zeile von matrix zeigt, schreibst du pointer = matrix[1]; 😃



  • HansKlaus schrieb:

    naja er wollte doch Zeigerarithmetik benutzen, also kann man doch schreiben

    int funktion(int *matrix)
    {
    *(matrix+3*10+3)=0; //zulässig
    matrix[3][4]=0; //auch zulässig aber keine Zeigerarithmetik
    }
    
    int main()
    {
    int matrix[10][10];
    
    funktion(matrix);
    }
    

    oder sehe ich das falsch?

    Ja, siehst du.

    In funktion kannst du matrix nicht zweimal dreferenzieren. Das versuchst du mit matrix[3][4]
    funktion erwartet einen Zeiger auf int.
    matrix (nur der Name) ist in main aber die Adresse von 10 Arrays aus Arrays von 10 int

    Auch wenn die Adresse identisch ist, warnt da der Compiler.



  • Broetchen93 schrieb:

    Wofür ist denn Summe als Paramter von multiplikation?.
    Wenn der Wert ungleich 0 ist, kommt Müll raus.

    Summe dient als Zwischenspeicherung der Zwischenergebnisse der einzelnen Rechnung. Diese werden so lange aufsummiert, dass der Wert des Ergebnisvektors herauskommt.
    Außerdem wird summe nach jeder Übergabe an den Ergebnisvektor wieder auf 0 zurückgesetzt.

    Ich wollte nicht wissen wozu Summe da ist, sondern warum es als Paramter von multiplikation auftritt.
    Warum übergibst du Summe von außen in die Funktion?

    Broetchen93 schrieb:

    Ich verstehe jetzt nicht, in wie fern man mit Pointern rechnen kann.
    Eigentlich enthalten die ja nur die Adresse. Gibt es da nicht dieses "Gegensymbol &", um den Inhalt der (nicht-Pointer) Variable wieder hervorzuholen?

    Zeiger sind auch nur Variablen. Sie Speichern keinen Zahlenwert (oder Zeichen) sondern Adressen.
    Damit kann man auch rechnen.
    Du kannst die Differenz ausrechnen oder einen Offset dazu rechnen.
    Du kannst auch die Adresse (den Inhalt) verändern.

    & ist der Adressoperator. Du ermittelst die Referenz.
    Mit * (oder []) dereferenzierst du.

    Der Compiler macht beim übersetzen aus array[4] gleich *(array+4)



  • Okay.. die Syntax stimmt jetzt immerhin, jetzt kommt noch der logische Teil der Rechnung.

    Summe = Summe + *(matrix1+h1*a2+h3)**(matrix2+h3*b2+h2);
    

    So sieht meine Rechenzeile gerade aus.
    Am Anfang bin ich in Zeile 0 von Matrix1, die Matrix hat ein Maximum von a2 Spalten und befindet sich zu Anfang in der Spalte h3=0. Also sollte hier eigentlich die allererste Eingabe eingelesen werden, bzw. verrechnet werden.
    Das Ergebnis

    *(ergebnis + h1 * b2 + h2) = Summe;
    

    Sieht allerdings alles anders als zufriedenstellend aus.

    Warum übergibst du Summe von außen in die Funktion?

    War noch aus der alten Version. Ich habs jetzt abgeändert.



  • DirkB schrieb:

    Auch wenn die Adresse identisch ist, warnt da der Compiler.

    Das stimmt natürlich. Aber bei C kann ich ja (wieder von geschützten oder sonstwie dem Betriebssystem nicht angemeldeten Bereichen abgesehen) "einfach mal so" was in den Speicher schreiben.

    aber mir ist gerade aufgefallen, dass matrix[3][4] gar nicht funktioniert. woher soll der Compiler auch wiessen, wie er das zuordnen soll?

    Sieht allerdings alles anders als zufriedenstellend aus.

    sicher, dass du da nichts vertauscht hast? wenn doch: jetzt weißt du, warum man dir erzählt hat, dass klar verständliche Variablennamen verwendet werden sollen. 😃



  • sicher, dass du da nichts vertauscht hast? wenn doch: jetzt weißt du, warum man dir erzählt hat, dass klar verständliche Variablennamen verwendet werden sollen. :D
    

    Ganz im Gegenteil, aber nur was und wo?. Ich checke nochmal nach der Vorlage alles durch

    *(zeigeraufint + z * SPALTEN + s)
    

    SPALTEN bedeutet doch die maximale Anzahl der Spalten, oder?



  • HansKlaus schrieb:

    Aber bei C kann ich ja (wieder von geschützten oder sonstwie dem Betriebssystem nicht angemeldeten Bereichen abgesehen) "einfach mal so" was in den Speicher schreiben.

    Trottel. Wenn du keine Ahnung hast, halt einfach die Klappe. Du verleitest nur die Leser hier, deinen Unsinn zu glauben.

    HansKlaus schrieb:

    aber mir ist gerade aufgefallen, dass matrix[3][4] gar nicht funktioniert. woher soll der Compiler auch wiessen, wie er das zuordnen soll?

    Genau. Der Compiler weiß, im Gegensatz zu dir, was er tut. Und deshalb war dieser Hinweis von dir blanker Unsinn.



  • naja es bedeutet die anzahl der spalten, die die matrix hat, nicht die anzahl der spalten, die sie maximal haben kann.



  • Deine Arrayzeilen haben 10 Elemente. Davon hast du möglicherweise nur einen Teil beschrieben.
    Daher ist deine Berechnung nicht richtig.

    Du kannst es dir ja selber aufmalen.

    0 1 2 3 4 5 6 7 8 9
       +-+-+-+-+-+-+-+-+-+-+
     0 |x|x|x|x|x| | | | | | 
       +-+-+-+-+-+-+-+-+-+-+
     1 |x|x|x|x|x| | | | | | 
       +-+-+-+-+-+-+-+-+-+-+
     2 |x|x|x|x|a| | | | | | 
       +-+-+-+-+-+-+-+-+-+-+
     3 | | | | | | | | | | | 
       +-+-+-+-+-+-+-+-+-+-+
     4 | | | | | | | | | | | 
       +-+-+-+-+-+-+-+-+-+-+
     5 | | | | | | | | | | | 
       +-+-+-+-+-+-+-+-+-+-+
     6 | | | | | | | | | | | 
       +-+-+-+-+-+-+-+-+-+-+
     7 | | | | | | | | | | | 
       +-+-+-+-+-+-+-+-+-+-+
     8 | | | | | | | | | | | 
       +-+-+-+-+-+-+-+-+-+-+
     9 | | | | | | | | | | | 
       +-+-+-+-+-+-+-+-+-+-+
    

    Der Offset vom a vom Anfang des Array ist 2 * 10 + 4 = 24.
    Auch wenn der gefüllte Bereich nur 3 * 5 Elemente hat.


Anmelden zum Antworten