DIESER FILE IST DAZU GEDACHT, KOLLEGINNEN UND KOLLEGEN MIT NOCH GERINGER ERFAHRUNG BEIM ARBEITEN MIT C ZU HELFEN. DABEI GEHE ICH AUF THEMEN EIN, DIE ERFAHRUNGSGEMAESS AM ANFANG DIE GROESSTEN PROBLEME MACHEN. DIE FOLGENDEN PUNKTE BILDEN KEINE LOGISCHE REIHENFOLGE, SONDERN SIND WILLKUERLICH GEREIHT, WIE ES SICH AUS DER UEBUNGSPRAXIS DER LETZTEN JAHRE ERGEBEN HAT. NOCHMALS: DIE FOLGENDEN ANMERKUNGEN, BEISPIELE USW. SIND NICHT DAZU GEEIGNET, EINEN C-KURS ZU ERSETZEN. H. Sormann September 2004 *************************** STICHWORTE: =========== (1) Ein einfaches C-Programm (2) Aufruf von Header-Dateien (3) Die math-Bibliothek (4) Haeufige Fehler beim Arbeiten mit mathematischen Ausdruecken (5) Arbeiten mit Konstanten (6) Die Bibliothek NRUTIL.C aus den 'Numerical Recipes' (7) Dynamische Allokierung von Feldern (8) Datentransfer zwischen Programm-Teilen (9) Funktions-Prototypen *************************** (1) Wie ein typisches, einfaches C-Programm aufgebaut ist, koennen Sie im File 'arbeitssitzung.txt' studieren. Dort wird im Beispiel 'test2' auch gezeigt, wie man Daten von einem externen File einliest und auf einen externen File schreibt. (2) In praktisch jedem C-Programm rufen Sie (gewoehnlich am Beginn des Programms) eine Reihe von 'Header-Dateien' auf (erkenntlich an der extension .h). Die fuer uns wichtigsten sind die folgenden Aufrufe: #include // enthaelt die Standard-Ein- und Ausgabe- // Funktionen: scanf, printf, fscanf, fprintf,... #include // enthaelt u.a. einen random number generator #include // Bibliothek mathematischer Funktionen und // Konstanten . . . (3) DIE MATH-BIBLIOTHEK: ==================== Im folgenden eine knappe Dokumentation der wichtigsten Funktionen und Konstanten, die diese Bibiliothek bietet: //********************************************* #include int main() { int ierg; double x,y,erg; x=...; y=...; erg=acos(x); // Arcuscosinus erg=asin(x); // Arcussinus erg=atan(x); // Arcustangens erg=ceil(x); // naechstgroessere ganze Zahl erg=cos(x); // Cosinus erg=cosh(x); // Cosinus hyperbolicus erg=exp(x); // Exponentialfunktion erg=fabs(-x); // Absolutbetrag einer reellen Zahl erg=floor(x); // naechstkleinere ganze Zahl erg=log(x); // natuerlicher Logarithmus erg=log10(x); // Logarithmus der Basis 10 erg=sin(x); // Sinus erg=sinh(x); // Sinus hyperbolicus erg=sqrt(x); // Quadratwurzel erg=tan(x); // Tangens erg=tanh(x); // Tangens hyperbolicus erg=atan2(x,y); // arcustangens(x/y), // korrekter Quadrant erg=fmod(x,y); // x modulo y erg=pow(x,y); // x hoch y /* mathem. Konstanten: M_E e M_LOG2E log2(e) = 1/ln(2) M_LOG10E log10(e) M_LN2 ln(2) M_LN10 ln(10) M_PI pi M_PI_2 pi/2 M_PI_4 pi/4 M_1_PI 1/pi M_2_PI 2/pi M_2_SQRTPI 2/Wurzel(pi) M_SQRT2 Wurzel(2) M_SQRT1_2 Wurzel(1/2) */ x=...; erg=erf(x); // error function erg=erfc(x); // 1 - error function(x) erg=gamma(x); // Gammafunktion ACHTUNG: FEHLERHAFT // (liefert das Ergebnis fuer lgamma(x) !!! erg=lgamma(x); // natuerlicher Log. der Gammafunktion erg=j0(x); // Besselfunktion J nullter Ordnung erg=j1(x); // Besselfunktion J erster Ordnung erg=y0(x); // Besselfunktion Y nullter Ordnung erg=y1(x); // Besselfunktion Y erster Ordnung return (0); } (4) HAEUFIGE FEHLER BEIM ARBEITEN MIT MATH. AUSDRUECKEN: ======================================================== exp(-x^2/a^2): wird oft so programmiert: exp(-x*x/a*a) FALSCH; dieser Befehl bedeutet exp(-x^2 'durch a' 'mal a') !!! RICHTIG: exp(-x*x/a/a) ODER: exp(-x*x/(a*a)) z.B.: 'dritte Wurzel aus a': wird oft so programmiert: pow(a,1/3) FALSCH; man erhaelt das Ergebnis 1.0 !!! URSACHE: '1/3' bedeutet eine Integer:Integer-Division mit dem Ergebnis 0.0, und 'a hoch Null' ist Eins! RICHTIG: pow(a,1.0/3) oder pow(a,1/3.0) oder pow(a,1.0/3.0) Absolutbetrag einer reellen Zahl: abs(a) FALSCH, denn math.h enthaelt keine Funktion 'abs' RICHTIG: fabs(a) (5) ARBEITEN MIT KONSTANTEN: ============================ Betrachten Sie das folgende kleine Programm: #include #include int main() { int j; double x,wert,const; const=M_2_SQRTPI/17.0; x=3.33; wert=0.0; for(j=1;j<=5;j++) wert+=sin(j*x); wert*=const; printf("wert(%12.6f) = %12.6f\n",x,wert); return (0); } Wenn Sie dieses Programm mit g++ compilieren, erhalten Sie die folgenden Fehlermitteilungen: loeschen.c: In function `int main (...)': loeschen.c:7: parse error before `const' loeschen.c.15: parse error before `;' Die Ursache dafuer ist, dass 'const' in C ein reserviertes Schluesselwort ist und daher nicht als normaler Variablennamen verwendet werden darf. Wenn Sie anstatt 'const' den Namen 'con' waehlen, ist alles ok, und die Exekution des Programms ergibt die Ergebniszeile wert( 3.330000) = -0.031821 Im konkreten Fall ist es natuerlich noch besser, die verwendete Konstante von Anfang an als solche zu kennzeichnen: #include #include #define CON M_2_SQRTPI/17.0 int main() { int j; double x,wert; x=3.33; wert=0.0; for(j=1;j<=5;j++) wert+=sin(j*x); wert*=CON; printf("wert(%12.6f) = %12.6f\n",x,wert); return (0); } Anmerkung: Es ist in C ueblich, aber nicht verbindlich, Namen von Konstanten in GROSSBUCHSTABEN zu schreiben. (6) DIE BIBLIOTHEK NRUTIL.C AUS DEN 'NUMERICAL RECIPES': ========================================================== Ich komme nun zu einem wichtigen Punkt des C-Programmierens, der am Anfang traditionell Schwierigkeiten macht. Es geht um das Arbeiten mit Datenfeldern, d.h., mathematisch gesprochen, mit Vektoren, Matrizen, Tensoren. Beginnen wir ganz simpel: Multiplikation einer (3x3)-Matrix 'mat' mit einem 3-komponentigen Vektor 'vek': C-Programm: #include int main() { int i,j; double mat[4][4], vek[4], sum, ergvek[4]; mat[1][1]=1.0; mat[1][2]=2.0; mat[1][3]=3.0; mat[2][1]=4.0; mat[2][2]=5.0; mat[2][3]=6.0; mat[3][1]=7.0; mat[3][2]=8.0; mat[3][3]=9.0; vek[1]=2.5; vek[2]=-1.0; vek[3]=-3.5; for(i=1;i<=3;i++){ sum=0.0; for(j=1;j<=3;j++) sum+=mat[i][j]*vek[j]; ergvek[i]=sum; } printf("\nErgebnisvektor: "); for(i=1;i<=3;i++) printf("%9.6f ", ergvek[i]); printf("\n"); return (0); } Kommentar: C-Anfaenger haben oft Probleme mit der 'Dimensionierung' der Felder. Was bedeutet die Programmzeile double mat[4][4], vek[4], sum, ergvek[4]; Sie bedeutet, dass fuer das Feld 'mat' 4x4=16 Datenplaetze reserviert werden, fuer 'vek' und 'ergvek' je 4 Datenplaetze. Das deshalb, weil C die Indexzaehlung von Null beginnt und demnach fuer einen Vektor mit maximalem Index=3 Datenplaetze fuer die Indizes 0, 1, 2 und 3 reserviert werden muessen. Ueberzeugen Sie sich davon, was geschieht, wenn Sie die obige Zeile durch double mat[3][3], vek[3], sum, ergvek[3]; ersetzen: Es wird der fuer Index-Ueberschreitungen typische Fehler "Segmentation fault" angezeigt! Eine sehr benutzerfreundliche Moeglichkeit, mit Feldern in C umzugehen, bieten die 'Numerical Recipes C' mit ihrer frei zugaenglichen Bibliothek nrutil.c , die ich im folgenden vorstellen werde. Zuvor zeige ich Ihnen noch, wie das obige Beispiel bei Verwendung von 'nrutil.c' aussieht: #include #include "nrutil.c" int main() { int i,j; double **mat, *vek, sum, *ergvek; mat=dmatrix(1,3,1,3); vek=dvector(1,3); ergvek=dvector(1,3); mat[1][1]=1.0; mat[1][2]=2.0; mat[1][3]=3.0; mat[2][1]=4.0; mat[2][2]=5.0; mat[2][3]=6.0; mat[3][1]=7.0; mat[3][2]=8.0; mat[3][3]=9.0; vek[1]=2.5; vek[2]=-1.0; vek[3]=-3.5; for(i=1;i<=3;i++){ sum=0.0; for(j=1;j<=3;j++) sum+=mat[i][j]*vek[j]; ergvek[i]=sum; } printf("\nErgebnisvektor: "); for(i=1;i<=3;i++) printf("%9.6f ", ergvek[i]); printf("\n"); free_dmatrix(mat,1,3,1,3); free_dvector(vek,1,3); free_dvector(ergvek,1,3); return (0); } Kommentar: Was bedeutet die Definitionszeile double **mat, *vek, sum, *ergvek; ? Wie Sie wahrscheinlich bereits wissen, erlaubt C nicht nur die Definition von Konstanten und Variablen verschiedener Datentypen, sondern auch von sog. 'Zeigern' ('pointer'), die im wesentlichen die Adresse abgeben, unter der eine Groesse im Computerspeicher zu finden ist. Im Falle von Datenfeldern gibt der 'Zeiger' die Startadresse des jeweiligen Feldes an. D.h.: double *ergvek bedeutet: 'ergvek' ist ein pointer (=zeigt auf) die Startadresse des Vektors vom Typ 'double' mit dem Namen 'ergvek'. double **mat bedeutet: 'mat' ist ein pointer (=zeigt auf) die Startadresse der Matrix vom Typ 'double' mit dem Namen 'mat'(im Detail: *mat ist ein Vektor der Startadressen aller Spalten der Matrix, und **mat ist ein Zeiger auf die Startadresse dieses Zeiger-Vektors). Nun zu den Befehlen mat=dmatrix(1,3,1,3); vek=dvector(1,3); ergvek=dvector(1,3); Die Funktionen 'dmatrix' und 'dvector' aus der Bibliothek 'nrutil.c' berechnen nun die Startadressen der Felder 'mat', 'vek' und 'ergvek', wobei die Indexbereiche als Funktionsparameter angegeben werden. D.h.: erst ab diesen Befehlen stehen die Speicherplaetze fuer die drei Felder zur Verfuegung. Diese Speichervereinbarung kann jederzeit widerrufen werden, und zwar durch die 'nrutil'-Funktionen 'free_dmatrix' und 'free_dvector': free_dmatrix(mat,1,3,1,3); free_dvector(vek,1,3); free_dvector(ergvek,1,3); Anmerkung: fuer ein 'fortgeschrittendes' Arbeiten mit C ist es unumgaenglich, dass Sie sich bei den Zeigervariablen gut auskennen. Fuer die Zwecke meiner Lehrveranstaltung genuegen einige grundlegende Kenntnisse. Im obigen einfachen Beispiel ist die Rolle der 'nrutil'-Funktionen nicht sehr beeindruckend. Viel wichtiger ist die Moeglichkeit, dass die Parameter dieser Funktionen nicht nur Konstante, sondern auch Variable sein koennen. D.h.: ES ERGIBT SICH DAMIT EINE BENUTZERFREUNDLICHE MOEGLICHKEIT, EINE DYNAMISCHE FELD-DIMENSIONIERUNG ('DYNAMISCHE ALLOKIERUNG') DURCHZUFUEHREN. Die daraus resultierenden Moeglichkeiten werde ich in den folgenden Beispielen erklaeren. Zuvor jedoch noch eine Vorstellung der 'nrutil'- Bibliothek. **************************************************************** DIE FOLGENDEN ROUTINEN STEHEN IHNEN IN DER NRUTIL-BIBLIOTHEK ZUR VERFUEGUNG, D. H. BEI VERWENDUNG DES BEFEHLS #include "nrutil.c" : =============================================================== Die Indizes: first, last, ... sollten vom Typ long (int) sein, viele Compiler akzeptieren aber auch den "normalen" Typ int. float *yourname; yourname=vector(first,last); // Allokierung eines float-Vektors, Indexbereich: first, ..., last int *yourname; yourname=ivector(first,last); // Allokierung eines int-Vektors, Indexbereich: first, ..., last unsigned char *yourname; yourname=cvector(first,last); // Allokierung eines char-Vektors, Indexbereich: first, ..., last unsigned long *yourname; yourname=lvector(first,last); // Allokierung eines unsigned long (int)-Vektors, // Indexbereich: first, ..., last double *yourname; yourname=dvector(first,last); // Allokierung eines double-Vektors, Indexbereich: first, ..., last float **yourname; yourname=matrix(first_r,last_r,first_c,last_c); // Allokierung einer float-Matrix, // Indexbereich: 1.Index(rows): first_r, ...,last_r // 2.Index(columns): first_c, ...,last_c double **yourname; yourname=dmatrix(first_r,last_r,first_c,last_c); // Allokierung einer double-Matrix, // Indexbereich: 1.Index(rows): first_r, ...,last_r // 2.Index(columns): first_c, ...,last_c int **yourname; yourname=imatrix(first_r,last_r,first_c,last_c); // Allokierung einer int-Matrix, // Indexbereich: 1.Index(rows): first_r, ...,last_r // 2.Index(columns): first_c, ...,last_c float ***yourname; yourname=f3tensor(first_a,last_a,first_b,last_b,first_c,last_c); // Allokierung eines 3D float-Tensors, // Indexbereich: 1.Index: first_a, ...,last_a // 2.Index: first_b, ...,last_b // 3.Index: first_c, ...,last_c ================================================================== Mit den folgenden Befehlen koennen Sie die allokierten Felder freigeben: free_vector(yourname, first,last); free_ivector(yourname, first,last); free_cvector(yourname, first,last); free_lvector(yourname, first,last); free_dvector(yourname, first,last); free_matrix(yourname, first_r,last_r,first_c,last_c); free_dmatrix(yourname, first_r,last_r,first_c,last_c); free_imatrix(yourname, first_r,last_r,first_c,last_c); free_f3tensor(yourname, first_a,last_a,first_b,last_b,first_c,last_c); ************************************************************************ ************************************************************************ (7) DYNAMISCHE ALLOKIERUNG: =========================== Die wichtigste Rolle der in 'nrutil' enthaltenen Funktionen besteht darin, dass sie Ihnen bei der in C moeglichen DYNAMISCHEN ALLOKIERUNG von Speicherplatz fuer ein-, zwei- und dreidimensionale Datenfelder (also fuer Vektoren, Matrizen, Tensoren 3. Ordnung) helfen. Der Vorteil einer DYNAMISCHEN Allokierung liegt darin, dass diese Speicherplatz-Reservierung JEDERZEIT WAEHREND DER PROGRAMM-EXEKUTION IN JEDER AKTUELLEN GROESSE vorgenommen und widerrufen werden kann. D. h., es muessen die Groessen der Felder nicht bei Programmbeginn fixiert werden. Das Prinzip der dynamischen Speicherplatz-Vereinbarung und die Verwendung von "nrutil.c" verstehen Sie sofort an Hand der folgenden kleinen Beispiele: BEISPIEL 1: Berechnung des inneren Produktes zweier Vektoren: ============================================================== // C-Programm: Inneres Produkt der Vektoren a und b // NRUTIL - Demonstration // H. Sormann September 2001 #include #include "nrutil.c" // Zuordnung der NRUTIL-Programme. int main() // Beginn der (Haupt-)Programmes. { int na,nb,i; float inprodukt; float *vec_a, *vec_b; // POINTER vec_a und vec_b. printf("Zahl der Komponenten des Vektors a = "); scanf("%d", &na); printf("Zahl der Komponenten des Vektors b = "); scanf("%d", &nb); if(na==nb) { vec_a=vector(1,na); // Dynamische Speicherplatzvereinbarung: vec_b=vector(1,nb); // Indexbereich: 1...na bzw. 1...nb printf("Eingabe Vektor a: a1 a2 a3 ...\n"); for(i=1;i<=na;i++) scanf("%f", &vec_a[i]); printf("Eingabe Vektor b: b1 b2 b3 ...\n"); for(i=1;i<=nb;i++) scanf("%f", &vec_b[i]); inprodukt=0.0; for(i=1;i<=na;i++) inprodukt+=vec_a[i]*vec_b[i]; printf("Inprodukt der Vektoren = %9.6f\n", inprodukt); free_vector(vec_a,1,na); // Freigabe der Speicherbereiche fuer free_vector(vec_b,1,nb); // vec_a und vec_b. } else printf("Vektoren sind nicht gleich lang!\n"); return (0); } BEISPIEL 2: Multiplikation zweier Matrizen: ============================================ // C-Programm: Produkt der Matrizen amat und bmat // NRUTIL - Demonstration // H. Sormann September 2001 #include #include "nrutil.c" // Zuordnung der NRUTIL-Programme. int main() // Beginn der (Haupt-)Programmes. { int z_a,sp_a,z_b,sp_b,i,j,k; float summe; float **amat, **bmat, **cmat; // POINTER fuer die Matrizen. printf("Zahl der Zeilen und Spalten der Matrix amat = \n"); scanf("%d %d", &z_a, &sp_a); printf("Zahl der Zeilen und Spalten der Matrix bmat = \n"); scanf("%d %d", &z_b, &sp_b); if(sp_a==z_b) { amat=matrix(1,z_a,1,sp_a); // Dynamische Speicherplatzvereinbarung bmat=matrix(1,z_b,1,sp_b); // fuer die Matrizen cmat=matrix(1,z_a,1,sp_b); // amat, bmat und cmat. printf("Eingabe Matrix amat:\n"); for(i=1;i<=z_a;i++) { printf("%2i-te Zeile: amat(x,1) amat(x,2) ...\n",i); for(j=1;j<=sp_a;j++) scanf("%f", &amat[i][j]); } printf("Eingabe Matrix bmat:\n"); for(i=1;i<=z_b;i++) { printf("%2i-te Zeile: bmat(x,1) bmat(x,2) ...\n",i); for(j=1;j<=sp_b;j++) scanf("%f", &bmat[i][j]); } // Ausfuehrung der Matrix-Multiplikation: for(i=1;i<=z_a;i++) { for(j=1;j<=sp_b;j++) { summe=0.0; for(k=1;k<=sp_a;k++) summe+= amat[i][k]*bmat[k][j]; cmat[i][j]=summe; } } printf("Matrix cmat = amat x bmat:\n"); for(i=1;1<=z_a;i++) { for(j=1;j<=sp_b;j++) printf("%9.6f ", cmat[i][j]); printf("\n"); } free_matrix(amat,1,z_a,1,sp_a); free_matrix(bmat,1,z_b,1,sp_b); free_matrix(cmat,1,z_a,1,sp_b); } else printf("Die Matrizen amat und bmat passen nicht zusammen!\n"); return (0); } ********************************************************************* (8) DATENTRANSFER ZWISCHEN PROGRAMM-TEILEN: =========================================== Nur in einfachen Faellen bestehen C-Programme nur aus dem Element 'main'. In der Regel liegt eine ganze Reihe von Elementen (Funktionen) vor, die einander aufrufen bzw. von einander aufgerufen werden und zwischen denen Daten transferiert werden. Diese Datenuebertragung zwischen Programmelementen ist ebenfalls eine typische Anfangs-Schwierigkeit. Auch hier kann ich Ihnen das Studium eines guten C-Lehrbuches nicht ersparen, aber zumindest koennen die folgenden kleinen Beispiele Ihnen die Grundprinzipien zeigen. VERSUCHEN SIE, AM BESTEN UNTERSTUETZT VON EINEM LEHRBUCH, DIE FOLGENDEN BEISPIELE ZU VERSTEHEN: // Beispiel 1: Datentransfer (nur Skalare) #include double fct(double x) { double y; x=x+1.0; y=x*x; return y; } int main () { double x,ergeb; x=3.0; printf(" x vor Aufruf von fct = %lf\n", x); ergeb=fct(x); printf(" x nach Aufruf von fct = %lf\n", x); printf(" ergeb = %lf\n", ergeb); return (0); } /* ERGEBNIS: x vor Aufruf von fct = 3.000000 x nach Aufruf von fct = 3.000000 ergeb = 16.000000 DISKUSSION: der aktuelle Wert fuer x (3.0) wird von "main" in die Routine "fct" uebergeben; dort wird x um Eins (also auf 4.0) erhoeht, und dieser Wert wird quadriert. Das Ergebnis (16.0) wird nach "main" zurueckgegeben. Die Veraenderung von x in "fct" wird von "main" NICHT beibehalten! */ // Beispiel 2: Datentransfer (Skalare als Adressen) #include double fct(double *x) { double y; *x=(*x)+1.0; y=(*x)*(*x); return y; } int main() { double x,ergeb; x=3.0; printf(" x vor Aufruf von fct = %lf\n", x); ergeb=fct(&x); printf(" x nach Aufruf von fct = %lf\n", x); printf(" ergeb = %lf\n", ergeb); return (0); } /* ERGEBNIS: x vor Aufruf von fct = 3.000000 x nach Aufruf von fct = 4.000000 ergeb = 16.000000 DISKUSSION: der aktuelle Werte fuer x (3.0) wird von "main" ALS ADRESSE in die Routine "fct" uebergeben, d.h. im Aufruf steht nicht der Wert x, sondern 'der Zeiger (pointer) auf x': &x; im aufgerufenen Programm (fct) wird DER WERT VON x ("*x") um Eins (also auf 4.0) erhoeht, und dieser Wert wird quadriert. Das Ergebnis (16.0) wird nach "main" zurueckgegeben. Die Veraenderung von x in "fct" wird auch von "main" ERKANNT! */ // Beispiel 3: Transfer von Vektoren inkl. dynamische Allokierung: #include #include "nrutil.c" double summe(double vec[], int n) { int i; double sum; sum=0.0; for(i=1;i<=n;i++) sum=sum+vec[i]; vec[n]=2*vec[n-1]; return sum; } int main() { int n,i; double *vec; printf("Wieviele Komponenten hat der Vektor?\n"); scanf("%d", &n); vec=dvector(1,n); // dyn. Allokierung mittels NRUTIL for(i=1;i<=n;i++) vec[i]=2.0*i; printf("Der Vektor VOR dem Aufruf von SUMME:\n"); for(i=1;i<=n;i++) printf(" %lf", vec[i]); printf("\nSumme der Vektorkomponenten = %lf\n", summe(vec,n)); printf("Der Vektor NACH dem Aufruf von SUMME:\n"); for(i=1;i<=n;i++) printf(" %lf", vec[i]); printf("\n"); free_dvector(vec,1,n); return (0); } /* ERGEBNIS: Wieviele Komponenten hat der Vektor? (Input: 3) Der Vektor VOR dem Aufruf von SUMME: 2.000000 4.000000 6.000000 Summe der Vektorkomponenten = 12.000000 Der Vektor NACH dem Aufruf von SUMME: 2.000000 4.000000 8.000000 DISKUSSION: Felder (also auch Vektoren) werden in C grundsaetzlich ueber die Startadressen ihrer Speicherbereiche uebergeben. (Im konkreten Fall wird die Startadresse des Feldes VEC von der NRUTIL-Routine "dvector" berechnet.) Aus diesem Grund werden alle Veraenderungen von Feld- komponenten, die in der Routine "summe" erfolgen, auch im aufrufenden Programm "main" erkannt. */ // Beispiel 4: Transfer von Matrizen inkl. dynamischer Allokierung. #include #include "nrutil.c" void summen(double **mat, int n, int m, double *sum1, double *sum2) { int i; *sum1=0.0; for(i=1;i<=m;i++) *sum1=*sum1 + mat[1][i]; *sum2=0.0; for(i=1;i<=n;i++) *sum2=*sum2 + mat[i][1]; } int main() { int n,m,i,j; double **mat, summe1, summe2; printf("Wieviele Zeilen hat die Matrix?\n"); scanf("%d", &n); printf("Wieviele Spalten hat die Matrix?\n"); scanf("%d", &m); mat=dmatrix(1,n,1,m); // NRUTIL-Routine printf("Zeilenweises Einlesen der %d Matrixelemente:\n", n*m); for(i=1;i<=n;i++) for(j=1;j<=m;j++) scanf("%lf", &mat[i][j]); for(i=1;i<=n;i++) { for(j=1;j<=m;j++) printf(" %lf", mat[i][j]); printf("\n"); } summen(mat,n,m,&summe1,&summe2); printf("Summe der Elemente der 1. Zeile = %lf\n", summe1); printf("Summe der Elemente der 1. Spalte = %lf\n", summe2); free_dmatrix(mat,1,n,1,m); //NRUTIL-Routine return (0); } /* ERGEBNIS: Wieviele Zeilen hat die Matrix? ( 2 <===) Wieviele Spalten hat die Matrix? ( 3 <===) Zeilenweises Einlesen der 6 Matrixelemente: (1. 2. 3. 4. 5. 6. <==) 1.000000 2.000000 3.000000 4.000000 5.000000 6.000000 Summe der Elemente der 1. Zeile = 6.000000 Summe der Elemente der 1. Spalte = 5.000000 DISKUSSION: Von "main" in die Routine "summen" werden die STARTADRESSE der Matrix und die WERTE der Variablen "n" und "m" (Zahl der Zeilen und Spalten der Matrix) uebergeben. Von der Routine "summen" werden die ADRESSEN der Ergebnisse ins aufrufende Programm zurueckgegeben. */ ***************************************************************** (9) FUNKTIONS-PROTOTYPEN: ========================= Ich moechte Ihnen auch dieses 'Ding' an Hand eines einfachen Beispiels erklaeren, naemlich unter Verwendung eines der im Punkt (7) besprochenen Testbeispiele: #include int main() { double x,ergeb; x=3.0; printf(" x vor Aufruf von fct = %lf\n", x); ergeb=fct(x); printf(" x nach Aufruf von fct = %lf\n", x); printf(" ergeb = %lf\n", ergeb); return (0); } double fct(double x) { double y; x=x+1.0; y=x*x; return y; } Beachten Sie: der einzige Unterschied gegenueber oben besteht darin, dass die beiden Funktionen 'fct' und 'main', welche das Programm bilden, in vertauschter Reihenfolge vorliegen, also zuerst die Hauptroutine 'main' uns danach die Funktion 'fct'. Wenn Sie dieses Programm mit g++ etc. kompilieren und linken, bekommen Sie eine Fehlermitteilung des Inhaltes, dass beim Aufruf von 'fct' aus dem Element 'main' die Routine 'fct' unbekannt ist! Die Ursache fuer diesen Fehler ist klar: die Abarbeitung des C-Programms erfolgt streng sequentiell 'von oben nach unten', d.h.: 'main' braucht die Routine 'fct', also muss 'fct' in Programm-Listing vor 'main' stehen! Ich rate Ihnen, diese 'logische Programmfolge' in C genau zu beachten. Wenn Sie dies einmal nicht tun wollen (in sehr umfangreichen Programmen ist dies nicht immer einfach), gibt es die Moeglichkeit, dem aufrufenden Programm mittels einer 'Funktions-Deklaration' den Funktionsnamen plus Parameterliste inkl. Typus von Funktion und Parametern mitzuteilen. Eine solche 'Funktions-Deklaration' heisst in C auch 'prototype of a function'. Im folgenden sehen Sie noch einmal das Testbeispiel: trotz der Reihenfolge 'main' VOR 'fct' funktioniert die Sache nun klaglos: #include int main() { double fct(double x); // Deklaration der Funktion fct // (Funktions-Prototyp) double x,ergeb; x=3.0; printf(" x vor Aufruf von fct = %lf\n", x); ergeb=fct(x); printf(" x nach Aufruf von fct = %lf\n", x); printf(" ergeb = %lf\n", ergeb); return (0); } double fct(double x) { double y; x=x+1.0; y=x*x; return y; }