7 Zeiger und Vektoren


7.1 Vektoren
7.1.1 Eindimensionale Vektoren
7.1.2 Zeichenketten
7.1.3 Mehrdimensionale Vektoren
7.2 Zeiger
7.2.1 Zeiger und Adressen
7.2.2 Zeiger und Vektoren
7.2.3 Zeigerarithmetik
7.2.4 Zeiger auf Funktionen
7.3 Anwendungen
7.3.1 Vektoren von Zeigern und Zeiger auf Zeiger
7.3.2 Zeiger und mehrdimenisonale Vektoren
7.3.3 Argumente der Kommandozeile


7.1 Vektoren

7.1.1 Eindimensionale Vektoren

Zur Zusammenfassung mehrerer Objekte gleichen Typs zu einer Einheit verwendet man Vektoren, oft auch als Felder (arrays) bezeichnet. Sie bilden eine lineare Anordnung ihrer Grundobjekte im Speicher. Sie werden durch die Verwendung von eckigen Klammern ([]) nach dem Variablennamen erzeugt. Innerhalb der Klammern wird die Größe des Vektors (Feldes) in Anzahl Elementen des definierten Typs angegeben:


  char s;         /* 1 Zeichen */
  char sf[20];    /* 20 Zeichen */
  int i;          /* 1 ganze Zahl */
  int iff[100];   /* 100 ganze Zahlen (= 400 Bytes bei 32 Bit) */
  double f;       /* 1 Fließkommazahl */
  double ff[100]; /* 100 Fließkommazahlen (= 800 Bytes) */

Der Zugriff auf die einzelnen Vektorelemente erfolgt durch Angabe des Vektornamens gefolgt vom Index in eckigen Klammern:


  int j = 5, k;

  sf[8] = 'a';
  sf[j] = sf[19];
  k     = iff[j];

Das 1.Element hat immer den festgelegten Index 0, das N-te den Index N-1. Vektorelemente können L-Werte sein, Vektornamen alleine nicht, da sie feste Adressen (nämlich die Adresse des 1. Vektorelementes) darstellen. Als dafür zu große Einheit können Vektoren nicht in der Speicherklasse register sein. Vektoren in der Speicherklassse auto können nicht initialisiert werden, dies ist nur bei Vektoren in den Speicherklassen static und extern möglich. Werden diese nicht initialisiert, so haben alle Elemente den Wert 0. Die Initialisierungswerte werden nach einem Gleichheitszeichen in geschweiften Klammern angegeben. Nicht initialisierte Elemente erhalten ebenfalls den Wert 0. Beispiele für Vektorinitialisierungen:


  static int iff[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  static int jf[5] = { 0, 1, 3};   /* restl. Elemente = 0 */
  static int kf[] = { 1, 3, 5, 7, 11, 13};  /* Laenge aus Anzahl */
					    /* Init-Elemente     */

Nachfolgend einige Beispiele für Vektorverarbeitung:


  /* Feld mit der Summe der natuerlichen Zahlen bis zu
     diesem Feldelement belegen */
     
  #define MAX 100
  
  int sum[MAX], i, j;
  
  for (i = 0, j = 0; i < MAX; i++) {
    j = j + i + 1;
    sum[i] = j;
  }


  /* nochmals unter Auslassung des 1.Elementes */
  
  #define MAX 101
  
  int sum[MAX], i, j;
  
  for (i = 1,j = 0; i < MAX; i++) {
    j = j + i;
    sum[i] = j;
  }


  /* Feld mit den Quadratzahlen belegen, 1.Version */
  
  #define MAX 100
  
  int mul[MAX], i;
  
  for (i = 0; i < MAX; i++)
    mul[i] = (i + 1) * (i + 1);


  /* Feld mit den Quadratzahlen belegen, 2.Version */
  #define MAX 100
  
  int mul[MAX], i, j;
  
  for (i = 0, j = 1; i < MAX; i++, j++) {
    mul[i] = j * j;

7.1.2 Zeichenketten

Zeichenketten sind eindimensionale Vektoren oder Felder von Zeichen. Sie zeichnen sich durch einige Besonderheiten aus: Sie können auch in der Speicherklasse auto initialisiert werden. Als Zeichenkette müssen sie mit '\0' abgeschlossen sein. Dies läßt sich aber auch automatisch mit Hilfe der vereinfachten Initialisierung erreichen:


  char s[] = { 's', 't', 'r', 'i', 'n', 'g', '\0'};
  char s[] = "string";     /* \0 intern angehaengt */
  char s[10] = "string";   /* restl. Elemente mit 0 init. */

Ansonsten werden Zeichenketten wie normale Vektoren behandelt. Insbesondere ist der Zugriff auf Vektorelemente, d.h. auf einzelne Zeichen in einer Zeichenkette gleich:


  char s = "string";
  
  s[0] hat den Wert 's'
  s[3] hat den Wert 'i'
  s[6] hat den Wert '\0'

7.1.3 Mehrdimensionale Vektoren

Mehrdimenisonale Vektoren werden durch 2 oder mehr Paare eckiger Klammern nach dem Objektnamen definiert (Dabei stehen zwischen den Klammern keine Kommata.). Ein 2-dimensionaler Vektor ist ein Vektor, dessen Komponenten Vektoren sind, für höhere Dimensionen gilt Entsprechendes. Die Angabe der Elementanzahl der ersten Dimension kann (in Funktionen) fehlen, die der anderen Dimensionen nicht, da sonst die Indexberechnung nicht richtig arbeiten kann.


  /* Deklaration extern oder in der aufrufenden Funktion */
  int md[5][10];
  
  funct(md, 5);
  
  /* in der Funktion, anzahl gibt die Groesse der 1. Dimension an */
  void funct(int mdf[][10], anzahl)
  {
    int i, j;
    
    for (i = 0; i < anzahl; i++)
      for (j = 0; j < 10; j++)
	mdf[i][j] = i + j;
  }

Die gleiche Vektorbelegung wie mit diesem kurzen Programm kann man auch durch Initialisierung erreichen. Diese müßte dann wie folgt aussehen:


  static int md[5][10] = {
    { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9 },
    { 1,  2,  3,  4,  5,  6,  7,  8,  9, 10 },
    { 2,  3,  4,  5,  6,  7,  8,  9, 10, 11 },
    { 3,  4,  5,  6,  7,  8,  9, 10, 11, 12 },
    { 4,  5,  6,  7,  8,  9, 10, 11, 12, 13 }
  };

Die Angaben für die 2. (und weitere) Dimension werden also jeweils in eigene geschweifte Klammern geschrieben. Innerhalb der geschweiften Klammern gilt wiederum, daß evtl. nicht initialisierte Elemente auf 0 gesetzt werden. Man kann die inneren geschweiften Klammern auch weglassen, dann wird der ganze Vektor Element für Element initialisiert.

Beispielprogramm p7-1.c


  /*
   *      p7-1.c
   *      Beispielprogramm 1, Abschnitt 7
   */

  static int day_tab[2][13] = {
    { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
    { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
  };

  /* Tag im Jahr aus Monat und Tag bestimmen */
  int  day_of_year(int year, int month, int day)
  {
    int i, leap;
  
    leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
    for (i = 1; i < month; i++)
      day += day_tab[leap][i];
    return (day);

  } /* day_of_year() */

  /* Monat und Tag aus Tag im Jahr */
  void month_day(int year, int yearday, int *pmonth, int *pday)
  {
    int i, leap;
  
    leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
    for (i = 1; yearday > day_tab[leap][i]; i++)
      yearday -= day_tab[leap][i];
    *pmonth = i;
    *pday = yearday;

  } /* month_day() */


7.2 Zeiger

7.2.1 Zeiger und Adressen

Vektoren werden über ihre Anfangsadresse angesprochen und auch mittels dieser Anfangsadresse an Funktionen übergeben. Die Anfangsadresse eines Vektors steht fest, ist also eine Konstante. Im Gegensatz dazu ist ein Zeiger eine Variable, die die Adresse eines (beliebigen) anderen Objektes enthält. Man kann auf dieses Objekt indirekt über einen Zeiger zugreifen.

Die Adresse eines Objektes erhält man durch Anwendung des Adreßoperators &. & kann auf Variablen und Vektorelemente angewendet werden, nicht aber auf Vektornamen selbst (Warum? Ein Vektorname hat keine Adresse, er ist eine Adresse!). Ebenso haben natürlich Variablen in der Speicherklasse register keine Adressen.

Beispiele für die Anwendung des Adreßoperators und das Speichern der Adresse in einem Zeiger:


  px = &x;     /* px erhält als Wert die Adresse von x */
  pf = &f[5];  /* pf erhält als Wert die Adresse des
		  6. Elementes von f */

Der Zugriff auf ein Objekt, dessen Adresse in einer Variablen (einem Zeiger) steht, geschieht mittels des Operators * (Inhalt von):


  y = *px     /* y erhält den Wert des Objektes, dessen
		 Adresse in px steht */
  px = &x;
  y = *px;    /* y = x; */

Zeiger zeigen immer auf Objekte eines bestimmten Typs. Sie müssen daher deklariert werden. Auch in der Zeigerdefinition wird der Operator * verwendet. Zeigerdefinitionen kann man als Muster verstehen:


  int  *pk;     /* pk ist Zeiger auf int */
  char *zs;     /* zs ist Zeiger auf char */
  int  x, y, *px;
  
  px = &x;      /* Adressen */
  y = *px;      /* Werte */

Die Kombination *Zeiger kann in Ausdrücken überall dort auftreten, wo auch das Objekt, auf das der Zeiger zeigt, selbst stehen könnte:


  y = *px + 10;
  y = *px + *px;
  printf("%d\n", *px);
  *px = 0;
  py = px;    /* falls py auch Zeiger auf int */

Bei der Verwendung des Operators * muß man die Operatorrangfolge und -assoziativität genau beachten. Dies erscheint zunächst etwas schwierig, da dieser Operator ungewohnt ist. Hier einige Beispiele mit dem * Operator und anderen Operatoren:


  y = *px + 1;    /* Inhalt von px plus 1 */
  y = *(px+1);    /* Inhalt der Adresse px+1 */
  *px += 1;       /* Inhalt von px = Inhalt von px plus 1 */
  (*px)++;        /* Inhalt von px inkrementieren */
  *px++;          /* wie *(px++); (Assoziativität) 
		     Inhalt der Adresse px; px = px plus 1*/
  *++px;          /* Inhalt der Adresse px+1; px = px plus 1 */

Zeiger haben nur dann sinnvolle Werte, wenn sie die Adresse eines Objektes oder 0 enthalten. Für den Zeigerwert 0 ist garantiert, daß er nirgends hinzeigt (NULL bzw. NIL(in PASCAL)).

Beispielprogramm p7-2.c


  /*
   *      p7-2.c
   *      Beispielprogramm 2, Abschnitt 7
   *      Vertauschen von Parametern
   */

  #include <stdio.h>

  void no_swap(int, int);
  void do_swap(int *, int *);

  void main(void)
  {
    int i, j;
  
    i = 5; j = 10;
    printf("i=%2d j=%2d\n", i, j);
    no_swap(i, j);
    printf("i=%2d j=%2d\n", i, j);
    do_swap(&i, &j);
    printf("i=%2d j=%2d\n", i, j);
  
  } /* main() */
  
  /* Funktioniert nicht, da Werte uebergeben werden */
  void no_swap(int x, int y)
  {
    int temp;
  
    temp = x;
    x = y;
    y = temp;

  } /* no_swap() */ 

  /* Funktioniert, da Adressen uebergeben werden */
  void do_swap(int *px, int *py)
  {
    int temp;
  
    temp = *px;
    *px = *py;
    *py = temp;

  } /* do_swap() */

7.2.2 Zeiger und Vektoren

Zeiger und Vektoren hängen sehr eng zusammen. Das sieht man schon daran, daß ein Vektorname genau wie ein Zeiger auch eine Adresse ist (allerdings eine Adreßkonstante). Als Konsequenz aus dem engen Zusammenhang zwischen Vektoren und Zeigern ergibt sich die für den Anfänger zunächst etwas verwirrende Tatsache, daß alle Operationen mit Vektorindizes auch mit Zeigern formuliert werden können. Dies ist meist effizienter, aber für Anfänger schwerer zu verstehen. Ausgehend von den Definitionen und Anweisungen:


  int a[10];       /* int Vektor mit 10 Elementen */
  int *pa, y;      /* Zeiger auf int Objekte */
  
  pa = &a[0];      /* pa hat Adresse 1. Vektorelement */
  pa = a;          /* pa hat ebenfalls Adresse 1. Element */
  y = *pa;         /* y = 1. Element von Vektor a */

sind die folgenden Angaben äquivalent:


  a[i]        *(a+i)     /* i + 1tes Element des Vektors a */
  &a[i]        (a+i)     /* Adresse des i + 1ten Elementes */
  pa[i]       *(pa+i)

Statt Vektorname und Indexausdruck kann immer auch ein Zeiger mit Abstand stehen. Als Konsequenz aus Vektorname (= Adresskonstante) und Zeiger (= Adressvariable) ergeben sich folgende erlaubten und nicht erlaubten Zusweisungen:


  pa = a;   /* erlaubt */         a = pa;   /* falsch */
  pa++;     /* erlaubt */         a++;      /* falsch */

Beispielprogramm p7-3.c


  /*
   *      p7-3.c
   *      Beispielprogramm 3, Abschnitt 7
   *      Laenge einer Zeichenkette ermitteln
   */

  /* 1. Version mit Vektoren */
  int strlen1(char s[])
  {
    int n;
  
    for (n = 0; s[n] != '\0'; n++)
      n++;
    return(n);

  } /* strlen1() */
  
  /* 2. Version mit Zeiger */
  int strlen2(char *s)
  {
    int n;
  
    for (n = 0; *s != '\0'; s++)
      n++;
    return(n);

  } /* strlen2() */

7.2.3 Zeigerarithmetik

Mit Zeigern können bestimmte arithmetische Operationen und Vergleiche durchgeführt werden. Es sind natürlich nur die Operationen erlaubt, die zu sinnvollen Ergebnissen führen. Zu Zeigern dürfen ganzzahlige Werte addiert und es dürfen ganzzahlige Werte subtrahiert werden. Zeiger dürfen in- und dekrementiert werden und sie dürfen voneinander subtrahiert werden (Dies ist i.a. nur sinnvoll, wenn beide Zeiger auf Elemente des gleichen Objektes, z.B. einen Vektor, zeigen.).

Man kann Zeiger mittels der Operatoren >, >=, <, <=, != und == miteinander vergleichen. Wie bei der Zeigersubtraktion ist das aber i.a. nur dann sinnvoll, wenn beide Zeiger auf Elemente des gleichen Vektors zeigen. Eine Ausnahme bildet hier der Zeigerwert NULL, der sog. NULL-Pointer, der i.a. einen ungültigen Zeigerwert bezeichnet.

Alle anderen denkbaren arithmetischen und logischen Operationen (Addition von 2 Zeigern, Multiplikation, Division, Shifts oder Verwendung von logischen Operatoren, sowie Addition und Subtraktion von float oder double Werten) sind mit Zeigern nicht erlaubt.

Wie funktioniert nun aber die Zeigerarithmetik? Sei p ein Zeiger und n eine ganze Zahl, dann bezeichnet p + n das n-te Objekt im Anschluß an das Objekt, auf das p gerade zeigt. Es wird also nicht der Wert n zu p direkt addiert, sondern n wird vorher mit der Typlänge des Typs, auf den p zeigt, multipliziert. Dieser Typ wird aus der Deklaration von p bestimmt.

Beispielprogramm p7-4.c


  /*
   *      p7-4.c
   *      Beispielprogramm 4, Abschnitt 7
   *      Laenge einer Zeichenkette ermitteln
   */

  int strlen(char *s)
  {
    char *p = s;
  
    while (*p != '\0')
      p++;
    return(p - s);

  } /* strlen() */

Beispielprogramm p7-5.c


  /*
   *      p7-5.c
   *      Beispielprogramm 5, Abschnitt 7
   *      Einfache Speicherverwaltung
   */

  #define NULL 0          /* Zeigerwert fuer Fehleranzeige */
  #define ALLOCSIZE 1000  /* verfuegbarer Platz */

  static char allocbuf[ALLOSIZE];
  static char *allocp = allocbuf; /* naechste freie Position */

  /* liefert Zeiger auf Platz fuer n Zeichen */
  char *alloc(int n)
  {
    if (allocp + n <= allocbuf + ALLOCSIZE) {     /* reicht */
      allocp += n;
      return (allocp - n);        /* alter Zeiger */
    } 
    else                          /* nicht genug Platz */
      return (NULL);

  } /* alloc() */

  /* Speicher ab p freigeben */
  void free(char *p) 
  {
    if (p >= allocbuf && p <= allocbuf + ALLOCSIZE)
      allocp = p;
 
  } /* free() */

Beispielprogramm p7-6.c


  /*
   *      p7-6.c
   *      Beispielprogramm 6, Abschnitt 7
   *      4 Versionen einer Stringkopierfunktion
   */

  /* 1. t nach s kopieren Version mit Vektoren */
  void strcpy1(char s[],char t[])
  {
    int i;
    
    i = 0;
    while ((s[i] = t[i]) != '\0')
      i++;
    
  } /* strcpy1() */

  /* 2. t nach s  kopieren, 1. Version mit Zeigern */
  void strcpy(char *s, char *t)
  {
    while ((*s = *t) != '\0') {
      s++;
      t++;
    }

  } /* strcpy2() */

  /* 3. t nach s kopieren, 2. Version mit Zeigern */
  void strcpy3(char *s, char *t)
  {
    while ((*s++ = *t++) != '\0')
      ;

  } /* strcpy3() */

  /* 4. t nach s kopieren, 3. Version mit Zeigern */
  void strcpy(char *s, char *t)
  {
    while (*s++ = *t++)
      ;

  } /* strcpy4() */

Beispielprogramm p7-7.c


  /*
   *      p7-7.c
   *      Beispielprogramm 7, Abschnitt 7
   *      2 Versionen einer Stringvergleichsfunktion
   *      liefert 0, wenn Strings gleich sind
   */

  /* 1. Version mit Vektoren */
  int strcmp1(char s[], char t[])
  {
    int i;
  
    i = 0;
    while (s[i] == t[i])
      if (s[i++] == '\0')
	return (0);
    return (s[i] - t[i]);

  } /* strcmp1() */

  /* 2. Version mit Zeigern */
  void strcmp2(char *s, char *t)
  {
    for ( ; *s == *t; s++, t++)
      if (*s == '\0')
	return (0);
    return (*s - *t);

  } /* strcmp2() */

7.2.4 Zeiger auf Funktionen

In C ist eine Funktion keine Variable, sondern der Funktionsname ist (wie ein Vektorname) eine Adreßkonstante. Es können daher Zeiger auf Funktionen definiert werden, die man dann wie Variablen verwenden kann. Damit wird es möglich, Funktionen an Funktionen zu übergeben.

Ein Zeiger auf eine Funktion wird so vereinbart:


  (*Funktionsname)()   /* Zeiger auf eine Funktion */

Im Gegensatz zu:


  *Funktionsname()     /* Funktion, die einen Zeigerwert liefert */

Die Möglichkeit und der Gebrauch von Zeigern auf Funktionen wird am besten an einem Beispiel klar:


  /* in der aufrufenden Funktion */
  int iv[];
  char *sv[];
  int strcmp(), numcmp(), swap(), turn();
  
  sort(iv, numcmp, swap);  /* sortiere mittels der Funktionen */
			   /* numcmp und swap */
  sort(sv, strcmp, swap);  /* sortiere mittels der Funktionen */
			   /* strcmp und swap */
  sort(iv, numcmp, turn);  /* sortiere mittels der Funktionen */
			   /* numcmp und turn */

  /* in der gerufenen Funktion */
  sort(void v[], int (*comp)(), int (*exch)())
  {
    int j, gap;
    ...
    
    if ((*comp)(v[j], v[j+gap]) <= 0)
      break;
    (*exch)(&v[j], &[j+gap]);
    ...
    
  } /* sort() */


7.3 Anwendungen

7.3.1 Vektoren von Zeigern und Zeiger auf Zeiger

Genauso wie man Vektoren aus den Grunddatentypen (char, int, float und double) bilden kann, kann man dies auch mit Zeigern tun. Ein Vektor von Zeigern wird so definiert:


  * Vektorname []

gelesen von rechts nach links (wegen des Vorranges von []): Vektor von Zeigern. Dagegen ist


  (* Vektorname) []

ein Zeiger auf einen Vektor.

Zeiger auf Zeiger sind eine äquivalente Formulierung für Vektoren von Zeigern


  int *iff[];       /* sind äquivalent, da iff die
  int **iff;           Adresse des Vektors enthält */

Das folgende Beispiel zeigt, wie man einen Vektor von Zeigern initialisieren kann:

Beispielprogramm p7-8.c


  /*
   *      p7-8.c
   *      Beispielprogramm 8, Abschnitt 7
   *      Name des Monats n liefern
   */

  char *month_name(int n)
  {
    static char *name[] = {       /* Vektor von Zeigern */
      "falscher Monat",           /* String ist ein char-Vektor */
      "Januar",                   /* und daher durch seine */
      "Februar",                  /* Anfangsadresse charakterisiert */
      "Maerz",
      "April",
      "Mai",
      "Juni",
      "Juli",
      "August",
      "September",
      "Oktober",
      "November",
      "Dezember"
    };
   
    return ((n < 1 || n > 12) ? name[0] : name[n]);

  } /* month_name() */

Beispielprogramm p7-9.c


  /*
   *      p7-9.c
   *      Beispielprogramm 9, Abschnitt 7
   *      Programm zum Einlesen, alphabetischen Sortieren
   *      und Ausgeben von Eingabezeilen
   */

  #include <stdio.h>

  #define NULL 0
  #define LINES 100       /* maximale Anzahl Zeilen */

  void main(void)
  {
    char *lineptr[LINES]; /* Zeiger auf Textzeilen */
    int nlines;           /* Anzahl gelesener Zeilen */
  
    if ((nlines = readlines(lineptr, LINES)) >= 0) {
      sort(lineptr, nlines);
      writelines(lineptr, nlines);
    } 
    else        
      printf ("input too big to sort\n");
    
  } /* main() */
 
  #define MAXLEN 1000     /* Maximallaenge einer Zeile */

  /* Zeile einlesen */
  int getline(char line[], int lim)
  {
    int c, i;
  
    i = 0;
    while (--lim > 0 && (c = getchar()) != EOF && c != '\n')
      s[i++] = c;
    if (c == '\n')
      s[i++] = c;
    s[i] = '\0';
    return(i);

  } /* getline() */

  /* Eingabezeilen einlesen um sie zu sortieren */
  int readlines(char *lineptr[], int maxlines)
  {
    int len, nlines;
    char *p, *malloc(), line[MAXLEN];
  
    nlines = 0;
    while ((len = getline(line, MAXLEN)) > 0)
      if (nlines >= maxlines)
	return(-1);
      else if ((p = malloc(len)) == NULL)
	return(-1);
      else {
	line[len - 1] = '\0'; /* Zeilentrenner entfernen */
	strcpy(p, line);
	lineptr[nlines++] = p;
      }
    return(nlines);

  } /* readlines() */

  /* Zeilen ausgeben */
  void writelines(char *lineptr[], int nlines)
  {
   int i;
 
    for (i = 0; i < nlines; i++)
      printf("%s\n", lineptr[i]);

  } /* writelines() */

  /* Zeichenketten v[0],...,[n-1] in aufsteigender Reihen- */
  /* folge sortieren, es werden die Zeichenketten */
  /* verglichen und ihre Zeiger entsprechend sortiert */
  sort(char *v[], int n)
  {
    int gap, i, j;
    char *temp;
  
    for (gap = n/2; gap > 0; gap /= 2)
      for (i = gap; i < n; i++)
	for (j = i - gap; j >= 0; j -= gap) {
	  if (strcmp(v[j], v[j + gap]) <= 0)
	    break;
	  temp = v[j];
	  v[j] = v[j + gap];
	  v[j + gap] = temp;
	}

  } /* sort() */

7.3.2 Zeiger und mehrdimensionale Vektoren

Ein mehrdimensionaler Vektor ist ja ein Vektor, dessen Elemente selbst wieder Vektoren sind (siehe Abschnitt 7.1.3). Man kann solche Vektoren auch mit Zeigern realisieren. Betrachten wir dazu folgendes Beispiel:


  int md[10][10];   /* 2 dimensionaler Vektor mit insgesamt
		       100 int-Elementen */
  int *mp[10];      /* Vektor mit 10 Zeigern auf int-Objekte */

Die Ausdrücke


  md[5][5]     und     mp[5][5]

sind beide möglich und bezeichnen das gleiche Element.

md ist ein Vektor mit 10 Elementen, die selbst wiederum Vektoren mit je 10 int Elementen sind. Insgesamt enthält md also 100 Elemente, für die auch der Speicherplatz bereitgestellt wird.

mp ist ein Vektor mit 10 Elementen, die Zeiger auf int Objekte sind (Speziell könnten diese natürlich auch auf je einen int Vektor mit je 10 Elementen zeigen.). Im Falle von mp wurden aber nur 10 Speicherplätze für die Zeiger angelegt, die noch nicht initialisiert sind. Im Vergleich zu md wird hier für das gleiche Feld insgesamt mehr Speicherplatz und evtl. eine Initialisierung benötigt. Dafür hat man aber 2 Vorteile:

  1. wegen einfacherer Adressierung (meist) schnellere Programmausführung
  2. Zeiger können auf verschieden lange Vektoren zeigen (höhere Dimensionen der Vektoren haben immer eine feste Länge)

7.3.3 Argumente der Kommandozeile

Bisher haben wir die Funktion main als parameterlos behandelt. Im allgemeinen hat diese Funktion aber 2 Parameter argc und argv, die Angaben über die Kommandoparameter beim Programmaufruf enthalten.

argc ist ein int Parameter und enthält die Anzahl der Paramter der Kommandozeile einschließlich des Programmaufrufes selbst (hat also immer mindestens den Wert 1).

argv ist ein Vektor mit Zeigern auf Zeichenketten. Diese Zeichenketten enthalten die Aufrufparameter der Kommandozeile, wobei der 1. Parameter der Programmaufruf selbst ist. Letztes Argument ist die Vektorkomponente argv[argc - 1].

Beispiel:


  # Programmaufruf auf Betriebssystemebene 
  $ echo Hier ist Echo!
  
  # Werte von argc und argv im aufgerufenen Programm 
  argc = 4
  argv[0] = "echo"
  argv[1] = "Hier"
  argv[2] = "ist"
  argv[3] = "Echo!"

Beispielprogramm p7-10.c


  /*
   *      p7-10.c
   *      Beispielprogramm 10, Abschnitt 7
   *      3 Versionen des echo-Programms
   */

  /* Echo der Aufrufargumente 1.Version */
  void main1(int argc, char *argv[])
  {
    int i;

    for (i = 1; i < argc; i++)
      printf("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n');

  } /* main1() */

  /* Echo der Aufrufargumente 2.Version */
  void main2(int argc, char *argv[])
  {
    while (--argc > 0)
      printf("%s%c", *++argv, (argc > 1) ? ' ' : '\n');

  } /* main2() */

  /* Echo der Aufrufargumente 3.Version */
  void main3(int argc, char *argv[])
  {
    while (--argc > 0)
      printf((argc > 1) ? "%s " : "%s\n", *++argv);

} /* main3() */

Beispielprogramm p7-11.c


  /*
   *      p7-11.c
   *      Beispielprogramm 11, Abschnitt 7
   *      Programm zum Finden von Zeilen, die ein anzugebendes
   *      Suchmuster enthalten bzw. nicht enthalten
   *      Aufruf: find [-n] [-x] suchmuster 
   */
 
  #include <stdio.h>

  #define MAXLINE 1000
  
  int getline(char [], int);
  int index(char [], char []);

  void main(int argc, char *argv[])
  {
    char line[MAXLINE], *s;
    long lineno = 0;
    int except = 0, number = 0;
  
    while (--argc > 0 && (*++argv)[0] == '-')
      for (s = argv[0]+1; *s != '\0'; s++)
	switch (*s) {
	  case 'x':
	    except = 1;
	    break;
	  case 'n':
	    number = 1;
	    break;
	  default:
	    printf("find: illegal option %c\n",*s);
	    argc = 0;
	    break;
	}

    if (argc != 1)
      printf("Usage: find [-x] [-n] pattern\n");
    else
      while (getline(line, MAXLINE) > 0) {
	lineno++;
	if ((index(line, *argv) >= 0) != except) {
	  if (number)
	    printf("%ld: ",lineno);
	  printf("%s",line);
	}
      }

  } /* main() */
  
  /* Zeile in s ablegen, Laenge liefern */
  int getline(char s[], int lim)
  { 
    int c, i;
  
    i = 0;
    while (--lim > 0 && (c = getchar()) != EOF && c != '\n')
      s[i++] = c;
    if (c == '\n')
      s[i++] = c;
    s[i] = '\0';
    return(i);

  } /* getline() */
 
  /* Position von t in s liefern, -1 falls nicht da */
  int index (char s[], char t[])    
  { 
    int i, j, k;
  
    for (i = 0; s[i] != '\0'; i++) {
      for (j = i, k = 0; t[k] != '\0' && s[j] == t[k]; j++, k++)
	;
      if (t[k] == '\0')
	return(i);
    }
    return(-1);
  
  } /* index() */


zurück zum Inhaltsverzeichnis

 

11. November 1999, Peter Klingebiel, DVZ