![]() |
Programmieren in Cvon Prof. Dr. Rainer Thomas |
--> Ein Zeiger z e i g t auf andere Variable oder auf Funktionen
Ausnahme (ANSI-C) :
generic pointer
void *
kann auf beliebige Objekt-Typen zeigen.
Adressen können Zeigern zugewiesen werden. z.B.
Achtung : nicht anwendbar auf Register-Variable !
Zeigerobjekte können wie unter Namen referenzierte Variable
verwendet werden
z.B.
| --> | y = *px; *px = y+1; |
mittels Verwendung des Objektoperators.
z.B.:
int *px; /* px wird als Zeiger auf int-Werte vereinbart */
/* Interpretation : *px ist vom Typ int,
1 --> px ist ein Zeiger auf int */
/*--------------------------------------------------------------------*/
/* Programm POINDEM1 */
/*--------------------------------------------------------------------*/
/* Einfaches Demonstrationsprogramm zu Zeigern */
/*--------------------------------------------------------------------*/
#include <stdio.h>
void main(void)
{
int x=1, y, *px, *py;
px=&x; /* Wertzuweisung an Zeiger mittels
Adressoperator */
*px=5; /* Modifikation des Zeigerobjekts */
(*px)++; /* Klammern notwendig, wenn Zeigerobjekt */
/* modifiziert werden soll */
y=*px+1; /* Zeigerobjekt als Operand */
printf("\nx = %d\n",x);
printf("y = %d\n",y);
py=px; /* Zuweisung des Werts eines anderen Zeigers */
*py+=3; /* Modifikation des Zeigerobjekts */
printf("\nWert des Zeigerobjekts von px : %d\n",*px);
printf("Wert des Zeigerobjekts von py : %d\n",*py);
}
/*--------------------------------------------------------------------*/
Bildschirmausgabe :
E:\RT\C\VORL>poindem1
x = 6
y = 7
Wert des Zeigerobjekts von px : 9
Wert des Zeigerobjekts von py : 9
/*-------------------------------------------------------------------*/
/* Programm POINDEM2 */
/*-------------------------------------------------------------------*/
/* Demonstration der direkten Wertzuweisung an Zeiger */
/* sowie des Einlesens von Zeigerwerten */
/*-------------------------------------------------------------------*/
#include <stdio.h>
void main(void)
{
char x='A',y='F',*px;
int a;
/* --1--------- Direkte Zuweisung eines Integerwertes ------------ */
/* - Compiler gibt Warnung aus : Different levels of indirection - */
px=0xffd8;
printf("\n1) Wert des Zeigerobjekts von px : %02x\n",*px);
/* --2--------- Zuweisung eines mittels Cast-Operator ------------ */
/* ------------ umgewandelten Integerwertes ------------ */
px=(char *)0xffd8;
printf("\n2) Wert des Zeigerobjekts von px : %02x\n",*px);
/* --3----- Direkte Eingabe einer Adresse als Integerwert -------- */
printf("\n3) Adresse (Offset) ? ");
scanf("%x",&px);
printf(" Inhalt dieser Zelle : %02x\n",*px);
/* --4-------- Mittels Cast-Operator typgewandelte als ----------- */
/* ----------- Integerwert eingelesene Adresse ----------- */
printf("\n4) Adresse (Offset) ? ");
scanf("%x",&a);
px=(char *) a;
printf(" Inhalt dieser Zelle : %02x\n",*px);
/* --5------ Direkte Eingabe einer Adresse als Zeigerwert -------- */
/* --------- (ANSI-C !!!) -------- */
printf("\n5) Adresse (Offset) ? ");
scanf("%p",&px);
printf(" Inhalt dieser Zelle : %02x\n",*px);
}
Bilschirmausgabe :
E:\RT\C\VORL>poindem2
1) Wert des Zeigerobjekts von px : 41
2) Wert des Zeigerobjekts von px : 41
3) Adresse (Offset) ? ffd9
Inhalt dieser Zelle : 46
4) Adresse (Offset) ? ffd9
Inhalt dieser Zelle : 46
5) Adresse (Offset) ? ffd8
Inhalt dieser Zelle : 41
/*-------------------------------------------------------------------*/
/* Programm POINDEM3 */
/*-------------------------------------------------------------------*/
/* Demonstration der Ausgabe von Zeigerwerten */
/*-------------------------------------------------------------------*/
#include <stdio.h>
void main(void)
{
char x='A',y='F',*ptr;
int a;
/* --1----- Ausgabe einer Adresse direkt als Integerwert --------- */
/* -------- (sedezimal) --------- */
/* --------------------------------------------------------------- */
printf("\n1) Adresse (Offset) von x : %04x\n",&x);
ptr=&x;
printf(" Adresse (Offset) von x : %04x\n",ptr);
/* --2----- Ausgabe einer mittels Cast-Operator in einen --------- */
/* -------- Integerwert (sedezimal) gewandelten Adresse --------- */
/* --------------------------------------------------------------- */
printf("\n2) Adresse (Offset) von y : %04x\n",(int)&y);
ptr=&y;
printf(" Adresse (Offset) von y : %04x\n",(int)ptr);
/* --3----- Ausgabe einer Adresse direkt als Zeigerwert ---------- */
/* -------- (ANSI-C !!!) ---------- */
/* --------------------------------------------------------------- */
printf("\n3) Adresse (Offset) von x : %04p\n",&x);
ptr=&x;
printf(" Adresse (Offset) von x : %04p\n",ptr);
}
/*-------------------------------------------------------------------*/
Bildschirmausgabe :
E:\RT\C\VORL>poindem3
1) Adresse (Offset) von x : ffd8
Adresse (Offset) von x : ffd8
2) Adresse (Offset) von y : ffd9
Adresse (Offset) von y : ffd9
3) Adresse (Offset) von x : FFD8
Adresse (Offset) von x : FFD8
8.2 Zeiger als Parameter und Funktionswert
| --> | Ausgabeparameter sind erforderlich. Diese lassen sich z.B. in PASCAL durch VAR-Parameter realisieren |
Da C nur Wert-Parameter kennt, müssen in solchen Fällen die Adressen
der Variablen, die die erzeugten Werte aufnehmen sollen, als Werte
übergeben werden.
Durch De-Referenzierung dieser Adressen kann die Funktion dann den
entsprechenden Variablen Werte zuweisen.
/* ---------------------------------------------------------------- */
/* Funktion tausch() */
/* ---------------------------------------------------------------- */
/* Beispiel für Zeiger als Parameter in Funktionen */
/* Vertausch zweier int-Werte */
/* ---------------------------------------------------------------- */
/* Beispiel fuer Aufruf : tausch(&x,&y) */
/* ---------------------------------------------------------------- */
void tausch(int *a, int *b)
{
int hilf;
hilf=*a;
*a=*b;
*b=hilf;
}
/* ---------------------------------------------------------------- */
/* ---------------------------------------------------------------- */
/* Funktion maxelement() */
/* ---------------------------------------------------------------- */
/* Beispiel für Zeiger als Funktionswert */
/* Ausgabe eines Zeigers auf das größte Element eines double-Arrays */
/* ---------------------------------------------------------------- */
double *maxelement(double a[], int n)
{
int i,m;
double maxwert;
for (maxwert=a[0], m=0, i=1; i<=n; i++)
if (a[i]>maxwert)
{ maxwert=a[i];
m=i;
}
return &a[m];
}
/* ---------------------------------------------------------------- */
8.3 Dynamische Speicherallokation
Derartige - dynamisch allokierte - Objekte können nicht wie statische
Objekte definiert werden.
Sie werden auch nicht über einen Namen, sondern nur über ihre Adresse
angesprochen. Diese Adresse kann ihrerseits einer statischen Pointer-
Variablen, die wie üblich zu definieren ist, zugewiesen werden.
| malloc() | Allokation eines Speicherblocks bestimmter Größe
|
| calloc() | Allokation eines Speicherblocks für eine bestimmte
Anzahl von Objekten einer bestimmten Größe,
Initialisierung aller Bytes des Speicherblocks mit 0
|
| realloc() | Veränderung der Größe eines allokierten Speicherblocks |
Diese Funktionen liefern die Anfangsadresse des allokierten Blocks
| --> | nicht mehr benötigter dynamisch allokierter Speicher ist explizit freizugeben. Hierfür dient die Bibliotheksfunktion free() |
/* -------------------------------------------------------------------- */ /* malloc - Allokation eines Speicherblocks */ /* -------------------------------------------------------------------- */ #include <stdlib.h> /* enthält Funktionsdeklaration */ void *malloc(size_t size); /* size Laenge des zu allokierenden Blocks in Bytes */
malloc allokiert einen Speicherblock von wenigstens der Länge size Bytes (der tatsächlich allokierte Speicherblock kann infolge evtl. notwendiger Speicherplatzausrichtung größer sein)
/* -------------------------------------------------------------------- */ /* calloc - Allokation und Initialisierung eines Speicherblocks */ /* -------------------------------------------------------------------- */ #include <stdlib.h> /* enthält Funktionsdeklaration */ void *calloc(size_t nobj, size_t size); /* nobj Anz. Objekte, für die Speicherplatz allokiert werden soll */ /* size Laenge eines Objekts in Bytes */
calloc allokiert einen Speicherblock von wenigstens der Länge nobj size Bytes (der tatsächlich allokierte Speicherblock kann infolge evtl. notwendiger Speicherplatzausrichtung größer sein) und initialisiert alle Bytes mit 0.
/* -------------------------------------------------------------------- */
/* free - Freigabe von Speicherplatz */
/* -------------------------------------------------------------------- */
#include <stdlib.h> /* enthält Funktionsdeklaration */
void free(void *ptr);
/* ptr Zeiger auf allokierten Speicherblock */
free gibt den - zuvor allokierten - Speicherblock, auf den ptr zeigt, wieder frei.
/* ------------------------------------------------------------------- */
/* Programm MALDEM */
/* ------------------------------------------------------------------- */
/* Demonstrationsprogramm zur Verwendung der Bibliotheksfunktionen zur */
/* dynamischen Speicherverwaltung */
/* */
/* Einlesen, Sortieren und Ausgeben einer beliebigen Anzahl von */
/* double-Werten */
/* */
/* Die Funktionen zum Einlesen, Sortieren und Ausgaben eines Feldes */
/* von double-Werten stehen in einem getrennt zu übersetzenden Modul */
/* zur Verfügung */
/* ------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#define DBL_SIZ 8 /* Länge eines Double-Wertes in Bytes */
extern void dfeld_ein(double *, unsigned);
extern void dfeld_sort(double *, unsigned);
extern void dfeld_aus(double *, unsigned);
void main(void)
{
int anz;
double *dfeld;
printf("\nAnzahl der double-Werte ? ");
scanf("%d", &anz);
if (anz>0)
if ((dfeld=malloc(anz*DBL_SIZ)) == NULL)
printf("\nSpeicherplatz reicht nicht aus !\n");
else
{ dfeld_ein(dfeld, anz);
dfeld_sort(dfeld, anz);
dfeld_aus(dfeld, anz);
free(dfeld);
}
else
printf("\nAnzahl muß >0 sein !\n");
}
Neben der Zuweisung und der Objektbildung lassen sich auf Zeiger in C die folgenden Operationen anwenden:
Addition von n zu einem Zeiger liefert als neuen Zeigerwert die Adresse,
die um den Speicherplatz für n Objekte seines Objekttyps größer als
sein ursprünglicher Wert ist.
| ---> | Compiler nimmt Skalierung von n entsprechend der Größe des Objekttyps vor (Portabilität !). |
int *px;
px=px+4; /* jeweils Erhöhung von px um den Speicherplatz */
px+=4; /* von 4 int-Werten */
px++; /* Erhöhung von px um den Speicherplatz für */
/* einen int-Wert */
Wirkung analog zur Addition.
z.B.
double *pd;
pd=pd-2; /* Erniedrigung von pd um den Speicherplatz für */
/* 2 double-Werte */
Die Operation liefert die Anzahl der Objekte, um die die Werte der beiden Zeiger voneinander entfernt sind.
Sinnvoll vor allem auf Zeiger anzuwenden, die auf Elemente des gleichen
Arrays zeigen.
---> Anzahl der zwischen den Zeigerobjekten liegenden Elemente + 1
z.B. alternative Formulierung der Funktion strlen
int strlen(char kette[]) /* liefert Länge des Strings kette */
{
char *anf = &kette[0]; /* anf --> 'h' */
char *end = anf; /* 'a' */
while (*end) /* 'l' */
end++; /* 'l' */
return(end-anf); /* 'o' */
} /* end --> '\0' */
Sinnvoll nur für Zeiger, die auf Elemente des gleichen Arrays zeigen.
/* ------------------------------------------------------------------ */
/* Modul ALLOCMEM */
/* ------------------------------------------------------------------ */
/* Einfache Funktionen zur dynamischen Speicherverwaltung : */
/* Allokation und Freigabe von Speicherplatz */
/* */
/* Der dynamisch verwaltete Speicher steht als "privates" char-Array */
/* (Speicherklasse static !) zur Verfügung */
/* Verwaltung erfolgt stack-ähnlich : */
/* Freigabe der allokierten Speicherblöcke muß in umgekehrter Reihen- */
/* folge ihrer Allokation erfolgen */
/* ------------------------------------------------------------------ */
#define NULLPOINT 0 /* Null-Pointer */
#define LAENGE 4096
static char allocbuf[LAENGE]; /* verfuegbarer Speicherplatz */
static char *allocp=&allocbuf[0]; /* Zeiger auf jeweils naechste
verfuegbare Speicherzelle */
char *alloc(int n) /* liefert Zeiger auf Bereich fuer n Zeichen */
{
if (allocp+n <= &allocbuf[0]+LAENGE)
{ allocp+=n; /* Speicher vorhanden */
return(allocp-n);
}
else return(NULLPOINT); /* Speicher nicht vorhanden */
}
void back(char *p) /* Rueckgabe des Speicherplatzes ab der Adresse p */
{
if (p>=&allocbuf[0] && p<&allocbuf[0]+LAENGE)
allocp=p;
}
8.5 Verwandschaft zwischen Zeigern und Arrays
int a[10]; /* Definition eines int-arrays mit 10 Elementen */
| a[0] | --> | 1.Element von a |
| a[1] | --> | 2.Element von a |
| a[i] | --> | Element, das i Positionen vom Anfang entfernt ist |
int *pa; /* Definition eines Pointers auf int-Variable */
pa=&a[0];
| pa | --> | zeigt auf das 1.Element von a |
| pa+1 | --> | zeigt auf das 2.Element von a |
| pa+i | --> | zeigt auf das Element, das i Positionen von dem Element, auf das pa zeigt, entfernt ist |
| ==> | *pa | ist gleich mit a[0] |
| *(pa+1) | ist gleich mit a[1] | |
| *(pa+i) | ist gleich mit a[i] |
| --> | Ein Array-Name ist äquivalent mit einem Pointer auf das 1.Element des Arrays: |
==> statt pa=&a[0] kann man auch pa=a schreiben
--> Für den Zugriff zum i-ten Element von a gilt:
statt *(pa+i) kann man auch pa[i] schreiben
| ==> |
|
| Anm. : | Pointer-Arithmetik kann effizienter (schneller) als Array- Indizierung sein. |
pa[-i] (i>0) bezeichnet das Element, das i Positionen (Objekte !) vor
dem Element liegt, auf das pa zeigt.
d.h. pa[-i] ist äquivalent mit *(pa-i)
Dies ist i.a. nur sinnvoll, wenn pa auf ein Element innerhalb eines Arrays zeigt und das dadurch referierte davorliegende Array-Element auch tatsächlich existiert.
z.B.
int x,*px;
px=&x; /* ist jederzeit zulässig */
z.B.
int z,y[5];
y=&z; /* ist u n z u l ä s s i g */
int a1[10], a2[10]; ...
| a2=a1; | --> | würde bedeuten, daß die Anfangsadresse von a1
der (Adreß-) Konstanten a2 zugewiesen wird. --> u n z u l ä s s i g !!! |
int a1[10], *pa2; ...
| pa2=a1; | --> | zulässig !!! |
Anwendung derartiger Konstruktionen vor allem bei char-Arrays (Strings), z.B. :
| char *message; | ||
| message="Vorsicht !"; | --> | der Pointervariablen message wird die Anfangsadresse des
- im ASp irgendwo als Konstante abgelegten - Strings
zugewiesen. Kein Kopieren von Strings !!! |
z.B.
void strcopy(char str1[], char str2[]);
void strcopy(char *str1, char *str2);
In beiden Fällen werden beim Funktionsaufruf den jeweiligen formalen Parametern - sowohl den als String, als auch den als Pointer deklarierten - Adressen als aktuelle Parameter übergeben.
| --> | als formale Funktionsparameter sind char s[] und
char *s vollkommen äquivalent. Arrays und Pointer sind sowohl als aktuelle als auch als formale Parameter vollkommen austauschbar. |
/* -------------------------------------------------------------------- */
/* Aequivalente Formulierungen der Funktion strcopy */
/* -------------------------------------------------------------------- */
void strcopy(char str1[], char str2[]) /* s. Umdruck VPC623 */
{
int i=0;
while((str2[i]=str1[i]) != '\0')
i++;
}
/* -------------------------------------------------------------------- */
void strcopy(char *str1, char *str2)
{
int i=0;
while ((str2[i]=str1[i]) != '\0')
i++;
}
/* -------------------------------------------------------------------- */
void strcopy(char *str1, char *str2)
{
int i=0;
while ((*(str2+i)=*(str1+i)) != '\0')
i++;
}
/* -------------------------------------------------------------------- */
void strcopy(char *str1, char *str2)
{
while ((*str2=*str1)!='\0')
{ str2++;
str1++;
}
}
/* -------------------------------------------------------------------- */
void strcopy(char *str1, char *str2)
{
while (*str2++=*str1++);
}
/* -------------------------------------------------------------- */
void strcopy(char str1[], char str2[])
{
int i=0;
while((str2[i]=str1[i]) != '\0')
i++;
}
/* -------------------------------------------------------------- */
void strcopy(char *str1, char *str2)
{
int i=0;
while ((str2[i]=str1[i]) != '\0')
i++;
}
/* -------------------------------------------------------------- */
void strcopy(char *str1, char *str2)
{
int i=0;
while ((*(str2+i)=*(str1+i)) != '\0')
i++;
}
/* -------------------------------------------------------------- */
_strcopy proc near
push bp
mov bp,sp
push si
xor si,si ; i=0
jmp short @2
@4:
inc si ; i++
@2:
mov bx,word ptr [bp+4] ; str1 --> BX
mov al,byte ptr [bx+si] ; str1[i] --> AL
mov bx,word ptr [bp+6] ; str2 --> BX
mov byte ptr [bx+si],al ; AL --> str2[i]
or al,al
jne @4 ; str1[i] == 0 ?
pop si
pop bp
ret
_strcopy endp
/* -------------------------------------------------------------- */
void strcopy(char *str1, char *str2)
{
while ((*str2=*str1)!='\0')
{ str2++;
str1++;
}
}
/* -------------------------------------------------------------- */
_strcopy proc near
push bp
mov bp,sp
jmp short @2
@4:
inc word ptr [bp+6] ; str2++
inc word ptr [bp+4] ; str1++
@2:
mov bx,word ptr [bp+4] ; str1 --> BX
mov al,byte ptr [bx] ; *str1 --> AL
mov bx,word ptr [bp+6] ; str2 --> BX
mov byte ptr [bx],al ; AL --> *str2
or al,al
jne @4 ; *str1 == 0 ?
pop bp
ret
_strcopy endp
/* -------------------------------------------------------------- */
void strcopy(char *str1, char *str2)
{
while (*str2++=*str1++);
}
/* -------------------------------------------------------------- */
_strcopy proc near
push bp
mov bp,sp
jmp short @2
@4:
@2:
mov bx,word ptr [bp+4] ; str1 --> BX
inc word ptr [bp+4] ; str1++
mov al,byte ptr [bx] ; *str1 --> AL
mov bx,word ptr [bp+6] ; str2 --> BX
inc word ptr [bp+6] ; str2++
mov byte ptr [bx],al ; AL --> *str2
or al,al
jne @4 ; *str1 == 0 ?
pop bp
ret
_strcopy endp
int proc_var_array(int n)
{
double var_ar[n];
...
}
#define DBL_SIZ 8
#include <stdlib.h>
int proc_var_array(int n)
{
double *var_ar;
if ((var_ar=calloc(n, DBL_SIZ)) != NULL)
{ ...
var_ar[1] = ... /* Zugriff zu Array-Komponenten */
...
}
else
...
}
| Achtung : | Die Definition
Ein Zugriff zu Array-Komponenten darf erst nach Alloka- tion des entsprechenden Speicherplatzes erfolgen. |
#include <stdlib.h>
#include <string.h>
char *add_string(char *s1, char *s2)
{
char *as;
if ((as=malloc(strlen(s1)+strlen(s2)+1)) != NULL)
{ strcpy(as, s1);
strcat(as, s2);
}
return as;
}
8.6 Mehrdimensionale Arrays und Pointer-Arrays
Mehrdimensionale Arrays koennen als "Pointer auf Arrays" aufgefasst werden.
char cf[3][4]; /* Definition und damit Speicherplatzreser-
vierung eines Arrays von 3 * 4 char-
Werten */
/* cf ist ein Pointer auf ein Array von
3 char-Arrays je der Laenge 4 */

| cf+i | ist ein Pointer auf das (i+1)-te Zeilen-Array
(der immer auf das 1.Element dieses Arrays zeigt)
|
| *(cf+i) = cf[i] | ist das (i+1)-te Zeilen-Array,
d.h. genauer ein Pointer auf das 1.Element des
(i+1)-ten Zeilen-Arrays
|
| ---> (cf+i) und *(cf+i) | liefern immer die gleiche Adresse
|
| *(cf+i)+j = cf[i]+j | ist ein Pointer auf das (j+1)-te Element des
(i+1)-ten Zeilen-Arrays
|
| *(*(cf+i)+j) = *(cf[i]+j) = cf[i][j] = (*(cf+i))[j] |
ist das (j+1)-te Element des (i+1)-ten Zeilen-Arrays |
Parameterdeklaration mehrdimensionaler Felder:
Pointer-Arrays (Arrays von Pointern), die wiederum auf Arrays zeigen, sind von mehrdimensionalen Arrays zu unterscheiden, obwohl sie gleichartig verwendet werden koennen.
char *pa[3]; /* Definition und damit Speicherplatzreser-
vierung eines Arrays von 3 Pointern, die
jeweils auf char-Werte zeigen */
/* pa ist ein Zeiger auf ein Array von
3 char-Pointern */
char ca1[4], /* Definition und damit Speicherplatzreser-
ca2[4], /* vierung von 3 char-Arrays je der
ca3[4]; /* Laenge 4 */
pa[0] = ca1; /* Wertzuweisung an die char-Pointer */
pa[1] = ca2;
pa[2] = ca3;

| pa+i | ist ein Pointer auf die (i+1)-te Komponente des Pointer-Arrays pa |
| *(pa+i) = pa[i] | ist das Array, auf das die (i+1)-te Komponente des Pointer-Arrays pa zeigt, d.h. genauer ein Pointer auf das 1.Element dieses Arrays |
| *(pa+i)+j = pa[i]+j | ist ein Pointer auf das (j+1)-te Element des Arrays, auf das die (i+1)-te Komponente des Pointer-Arrays pa zeigt |
| *(*(pa+i)+j) = *(pa[i]+j) = pa[i][j] = (*(pa+i))[j] |
ist das (j+1)-te Element des Arrays, auf das die (i+1)-te Kompon. d. Point.-Arrays pa zeigt |
char str0[20];
...
char str9[20];
char *strp_feld[10];
...
strp_feld[0]=str0;
...
Mit der Definition char *strp_feld[10] wird Speicherplatz
lediglich für 10 Pointer auf char reserviert.
Der Speicherplatz für die Objekte der einzelnen Komponenten des
Arrays - den eigentlichen Strings - wird hierdurch nicht reserviert.
Er muß durch gesonderte Definitionen bzw Allokationen beschafft
werden. (für 10 Strings zu je 20 Zeichen --> 200 Bytes).
Der Speicherplatz für die Pointer wird zusätzlich zum
Speicherplatz für die Zeichen benötigt.
| --> | Die Zeilenlänge muß sich nach der längsten Zeile (String) richten. --> bei kürzeren Zeilen (Strings) Platzverschwendung. |
| --> | Zur Speicherung unterschiedlich langer Objekte (Strings) wird nur soviel Platz belegt, wie tatsächlich benötigt wird. |
| 2-dimensionales Array | : | z.B. oder |
int fkt(char sf[][MSP]); int fkt(char (*sf)[MSP]); |
| Pointer-Array | : | z.B. bzw. |
int fkt(char *sfp[]); int fkt(char **sfp); |
/* ------------------------------------------------------------------- */
/* Programm POINDEM4 */
/* ------------------------------------------------------------------- */
/* Demonstration von Ähnlichkeit und Unterschied mehrdimensionaler */
/* Arrays und Pointer-Arrays */
/* ------------------------------------------------------------------- */
#include <stdio.h>
char aacWochTagFeld[][11] = {"ungueltig", "Montag",
"Dienstag", "Mittwoch",
"Donnerstag", "Freitag",
"Samstag", "Sonntag" };
char *apcWochTagZeig[] = {"ungueltig", "Montag",
"Dienstag", "Mittwoch",
"Donnerstag", "Freitag",
"Samstag", "Sonntag" };
void main(void)
{ int i,j;
char cZeichF,cZeichZ;
int indein(int *, int *);
while (indein(&i,&j)>=0)
{ cZeichF=aacWochTagFeld[i][j];
cZeichZ=apcWochTagZeig[i][j];
printf("aacWochTagFeld[%2d][%2d] = %02x\n",i,j,cZeichF);
printf("apcWochTagZeig[%2d][%2d] = %02x\n",i,j,cZeichZ);
}
}
int indein(int *iA, int *iB)
{ printf("\nZeile ? "); scanf("%d",iA);
if (*iA<0) return (-1);
else
{ printf("Spalte ? "); scanf("%d",iB);
if (*iB<0) return (-1);
else return 1 ;
}
}
/* ------------------------------------------------------------------- */
F:\RT\C\VORL>poindem4
Zeile ? 0
Spalte ? 8
aacWochTagFeld[ 0][ 8] = 67
apcWochTagZeig[ 0][ 8] = 67
Zeile ? 1
Spalte ? 7
aacWochTagFeld[ 1][ 7] = 00
apcWochTagZeig[ 1][ 7] = 44
Zeile ? -1
/* -------------------------------------------------------------------- */
/* Programm POINDEM5 */
/* -------------------------------------------------------------------- */
/* Einlesen einer unbekannten Anzahl von Zeilen (max. Anzahl 100) */
/* Nach Abschluss der Eingabe (mit CTRL_Z) Ausgabe der Zeilen in umge- */
/* kehrter Reihenfolge (letzte Zeile zuerst) */
/* -------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#define MAXANZ 100
#define MAXLEN 136
#define WRITELN putchar('\n')
void strcopy(char *pcStr1, char *pcStr2)
{ while (*pcStr2++ = *pcStr1++);
}
int ZeilenEin(char *apcZeilFeld[], int iMax)
{ extern int GetLine(char *, int); /* s. Umdruck V-PC-722 */
char *pcNext, acZeile[MAXLEN];
int iLaenge, iZeilZahl=0;
while (((iLaenge=GetLine(acZeile,MAXLEN)) >= 0)
&& (iZeilZahl < iMax) && (pcNext=malloc(iLaenge+1)) != NULL)
{ strcopy(acZeile,pcNext);
apcZeilFeld[iZeilZahl++]=pcNext;
}
return iZeilZahl;
}
void ZeilenAus(char *apcZeilFeld[], int iAnz)
{ int i;
WRITELN;
for (i=iAnz;i>0; )
{ printf("%s\n",apcZeilFeld[--i]);
free(apcZeilFeld[i]);
}
}
void main(void)
{ char *apcZeilen[MAXANZ];
int iAnz;
WRITELN;
iAnz=ZeilenEin(apcZeilen,MAXANZ);
ZeilenAus(apcZeilen,iAnz);
}
/* -------------------------------------------------------------------- */
Bildschirmausgabe :
F:\RT\C\VORL>poindem5
hallo
guten
morgen
^Z
morgen
guten
hallo
| argv[0] | ist ein Pointer auf den Programmnamen selbst |
| argv[1] | ist ein Pointer auf den ersten echten Kommandozeilen-Parameter |
| argv[argc-1] | ist ein Pointer auf den letzten Kommandozeilen- Parameter |
| argv[argc] | ist der NULL-Pointer |
| Programm: | echopar
|
| Programmaufruf: | echopar Pascal ist wunderbar |

/* ------------------------------------------------------------------ */
/* Programm ECHOPAR */
/* ------------------------------------------------------------------ */
/* Ausgabe der Kommandozeilen-Parameter untereinander nach stdout */
/* (Bildschirm) */
/* ------------------------------------------------------------------ */
#include <stdio.h>
void main(int argc, char *argv[])
/* argc Anzahl der Kommandozeilen-Parameter */
/* argv Pointer-Array auf Kommandozeilen-Parameter */
{ int i;
putchar('\n');
for (i=1; i<argc; i++)
printf("%s\n",argv[i]);
}
/* ------------------------------------------------------------------ */
Bildschirmausgabe :
E:\RT\C\VORL>echopar pascal ist wunderbar
pascal
ist
wunderbar
/* ------------------------------------------------------------------ */
/* Programm ECHOPAR2 */
/* ------------------------------------------------------------------ */
/* Ausgabe der Kommandozeilen-Parameter untereinander nach stdout */
/* (Bildschirm) */
/* Verwendung des Namens argv selbst wieder als Pointer */
/* ------------------------------------------------------------------ */
#include <stdio.h>
void main(int argc, char **argv)
/* argc Anzahl der Kommandozeilen-Parameter */
/* argv Pointer auf Pointer-array */
{
putchar('\n');
while (--argc>0)
printf("%s\n",*++argv);
}
/* ------------------------------------------------------------------ */
E:\RT\C\VORL>echopar2 c ist noch wunderbarer
c
ist
noch
wunderbarer
Es ist möglich
- Zeiger-Variable, die auf Funktionen zeigen, zu vereinbaren
- Funktions-Zeiger-Variablen Werte zuzuweisen,
- Arrays von Funktionszeigern aufzubauen,
- Zeiger auf Funktionen als Parameter zu übergeben
- Zeiger auf Funktionen als Funktionswert zurückzugeben
Ein Funktionszeiger kann immer nur auf Funktionen zeigen, deren Funktionswert von einem ganz bestimmten Typ ist.
Beispiel :
double (*pf)(int); /* pf ist ein Zeiger auf eine Funk-
tion, die einen double-Wert lie-
fert und einen int-Parameter er-
wartet */
Achtung : die runden Klammern um (*pf) sind zwingend notwendig
Man erhält in C die Adresse einer Funktion durch Angabe des
Funktionsnamens allein - ohne eine Parameterliste.
(Keine Anwendung des &-Operators !, Funktionsname ist Adreßkonstante !)
Beispiel :
extern double f1(int); /* Funktionsdeklaration */
double (*pf)(int); /* Definition einer Funktions-
Zeiger-Variablen pf */
pf = f1; /* Zuweisung der Adresse der
Funktion f1 an pf */
Beispiel :
a=(*pf)(2*i);
Beispiel :
int (*best(char *))(int);
| --> | best ist eine Funktion mit einem char-Pointer-Parameter, die einen Pointer auf eine int-Funktion mit einem int-Parameter liefert. |
/* ----------------------------------------------------------------- */
/* Programm SIMPINT */
/* ----------------------------------------------------------------- */
/* Definition der Funktion "simpint", */
/* Integration einer beliebigen Funktion mit einem double-Parameter, */
/* die einen double-Wert liefert, nach dem Simpson-Verfahren */
/* Beispiel zur Anwendung der Funktion */
/* ----------------------------------------------------------------- */
#include <stdio.h>
double simpint(double a, double b, unsigned n,
double (*fkt)(double))
/* a,b Untere u. obere Integrationsgrenze */
/* n Anz. Intervalle */
/* fkt Pointer auf zu integrierende Funktion */
{
double x,usum,gsum,h,zh;
int i;
h=(b-a)/n;
zh=2*h;
for (usum=0,i=1,x=a+h; i<n; x+=zh,i+=2)
usum=usum+(*fkt)(x);
for (gsum=0,i=2,x=a+zh; i<n-1; x+=zh,i+=2)
gsum=gsum+(*fkt)(x);
return ((*fkt)(a)+(*fkt)(b)+4*usum+2*gsum)/3*h;
}
double rechteck(double x) /* period. Rechteckfunktion */
{ /* Grundperiode : 0 ... 1.0 */
double r; /* x = 0 ) */
r = x-(int)x; /* x = 0.5 > rechteck = 0.5 */
r = r <0 ? r+1.0 : r; /* x = 1 ) */
if( r==0 || r==0.5) /* x < 0.5 rechteck = 1.0 */
return 0.5; /* x > 0.5 rechteck = 0 */
else
if (r<0.5) return 1.0;
else return 0;
}
void main(void)
{
double x1,x2;
unsigned m;
printf("\nUntere Grenze ? "); scanf("%lf",&x1);
printf("Obere Grenze ? "); scanf("%lf",&x2);
printf("Intervallanzahl ? "); scanf("%u",&m);
printf("\nIntegral : %1.4f\n",simpint(x1,x2,m,rechteck));
}
Bildschirmausgabe :
C>simpint
Untere Grenze ? 0
Obere Grenze ? 1
Intervallanzahl ? 10000
Integral : 0.5000
/* ------------------------------------------------------------------- */
/* Programm FKZDEMO */
/* ------------------------------------------------------------------- */
/* Demonstrationsprogramm zu Funktionszeigern */
/* Auswahl einer Funktion über Indizierung eines Arrays von Funktions- */
/* zeigern */
/* ------------------------------------------------------------------- */
#include <stdio.h>
#define FKTZ 4
int add(int a, int b)
{ printf("Summe : ");
return a+b;
}
int sub(int a, int b)
{ printf("Differenz : ");
return a-b;
}
int mul(int a, int b)
{ printf("Produkt : ");
return a*b;
}
int mod(int a, int b)
{ printf("Modulus : ");
return a%b;
}
void main(void)
{ int i;
int x,y;
int (*op[FKTZ])(int, int); /* Array von Funktionszeigern */
op[0]=add;
op[1]=sub;
op[2]=mul;
op[3]=mod;
while((printf("\nZwei int-Zahlen ? "), scanf("%d%d", &x, &y))!=EOF)
{ printf("Operation (0 - ADD, 1 - SUB, 2 - MUL, 3 - MOD) ? ");
scanf("%d",&i);
printf("%d\n",(*op[i])(x,y));
}
}
Bildschirmausgabe :
E:\RT\C\VORL>fkzdemo
Zwei int-Zahlen ? 125 68
Operation (0 - ADD, 1 - SUB, 2 - MUL, 3 - MOD) ? 1
Differenz : 57
Zwei int-Zahlen ? 45 17
Operation (0 - ADD, 1 - SUB, 2 - MUL, 3 - MOD) ? 3
Modulus : 11
Zwei int-Zahlen ? ^Z
| near | (--> Near-Zeiger) und |
| far | (--> Far-Zeiger) |
Diese sind bei Zeigervariablen- und Funktions-Vereinbarungen unmittelbar nach dem Datentyp des Zeigerobjekts bzw unmittelbar vor dem Funktionsnamen anzugeben.
Beispiele :
| int far *p1; | p1 ist Far-Zeiger auf int-Werte |
| int far *f1(); | f1 ist eine Funktion (Near oder Far je nach Speichermodell), die einen Far-Zeiger auf int-Werte liefert |
| int far f2(); | f2 ist eine Far-Funktion, die einen int-Wert liefert |
| int *far f3(); | f3 ist eine Far-Funktion, die einen Zeiger (Near oder Far je nach Speichermodell) auf int-Werte liefert |
| int (far *f4)(); | f4 ist ein Far-Zeiger auf eine Funktion, die einen int-Wert liefert. |
Beispiel :
#include <dos.h>
int far *screen;
screen=MK_FP(0xB800,0);
unsigned FP_SEG(void far *fptr); /* --> Segment */ unsigned FP_OFF(void far *fptr); /* --> Offset */
#include <dos.h>
unsigned seg,offs;
seg=FP_SEG(screen);
offs=FP_OFF(screen);
Beispiel :
int i; /* Speicher-Modell Small ! */
int far *iptr;
iptr=(int far *)&i; /* liefert Adr. von i als Far-Zeiger */
Beispiel :
scanf("%p", &px); /* px sei als Pointer definiert */
printf("%p", &x); /* x sei als int definiert */
Leider ist offensichtlich ein Fehler in der Funktion scanf() enthalten.
Sie arbeitet mit dem Length Modifier F nicht wie beschrieben.
Statt F ist im Controlstring für scanf() der Length Modifier
Beispiel :
char c;
char near *nptr;
char far *fptr;
...
scanf("%Np", &nptr);
scanf("%lp", &fptr);
printf("Offset : %Np", (char near *)&c);
printf("Segment:Offset : %Fp", (char far *)&c);
Die Eingabe von Far-Zeigern erfolgt in der Form :
| SSSS:OOOO | SSSS | Segment | sedezimal |
| OOOO | Offset | sedezimal |
/*---------------------------------------------------------------------*/
/* Programm POINDEM6 */
/*---------------------------------------------------------------------*/
/* Demonstration der Ein- und Ausgabe von Near- und Far-Zeigern in */
/* TURBO-C */
/*---------------------------------------------------------------------*/
#include <stdio.h>
void main(void)
{ char cZx='A', cZy='B';
char *pcZ1, near *npcZ2;
char far *fpcZeig;
/* -------------- Ausgabe eines Default-Zeigers -------------------- */
printf("\nAdresse von cZx (Default-Zeiger) : %p", &cZx);
/* ---------------- Ausgabe eines Near-Zeigers --------------------- */
printf("\nAdresse von cZx (Near-Zeiger) : %Np", (char near *)&cZx);
/* ---------------- Ausgabe eines Far-Zeigers ---------------------- */
printf("\nAdresse von cZx (Far-Zeiger) : %Fp\n",(char far *)&cZx);
/* -------------- Eingabe eines Default-Zeigers -------------------- */
printf("\nAdresse (Offset) ? ");
scanf("%p", &pcZ1);
/* ---------------- Eingabe eines Near-Zeigers --------------------- */
printf("Adresse (Offset) ? ");
scanf("%Np", &npcZ2);
/* ---------------- Eingabe eines Far-Zeigers ---------------------- */
printf("Adresse (Segment:Offset) ? ");
scanf("%Lp", &fpcZeig);
/* -------- Ausgabe des Objekts der eingegebenen Zeiger ------------ */
printf("\nObjekt von pcZ1 : %c", *pcZ1);
printf("\nObjekt von npcZ2 : %c", *npcZ2);
printf("\nObjekt von fpcZeig : %c\n", *fpcZeig);
}
/*---------------------------------------------------------------------*/
Bildschirmausgabe :
F:\RT\C\VORL>poindem6
Adresse von cZx (Default-Zeiger) : FFCA
Adresse von cZx (Near-Zeiger) : FFCA
Adresse von cZx (Far-Zeiger) : 0987:FFCA
Adresse (Offset) ? ffcb
Adresse (Offset) ? ffcb
Adresse (Segment:Offset) ? 0987:ffcb
Objekt von pcZ1 : B
Objekt von npcZ2 : B
Objekt von fpcZeig : B
/*------------------------------------------------------------------*/
/* Programm GIV2 */
/*------------------------------------------------------------------*/
/* Ermittlung des Interrupt-Vektors, der zu einer INT-Nr. gehört */
/* Auslesen des Interrupt-Vektors direkt aus der INT-Vektor-Tabelle */
/*------------------------------------------------------------------*/
#include <stdio.h>
#include <dos.h>
#define INT_SEG 0
void main(void)
{
int int_nr;
void far *int_vec;
int int_nr_anfordern(void);
void far *get_vect(unsigned);
while ((int_nr=int_nr_anfordern()) != EOF)
{ int_vec=get_vect(int_nr);
printf("Interrupt-Vektor : %Fp\n",int_vec);
}
putchar('\n');
}
int int_nr_anfordern(void)
{
unsigned zahl;
printf("\nINT-Nr. (Abbruch CTRL-Z) ? ");
if (scanf("%i", &zahl) != EOF)
return zahl;
else
return EOF;
}
void far *get_vect(unsigned int_nr)
{
void far *ivec;
ivec=*(void far * far *)MK_FP(INT_SEG,4*int_nr);
return ivec;
}
/*------------------------------------------------------------------*/
Bildschirmausgabe :
E:\RT\C\VORL>giv2
INT-Nr. (Abbruch CTRL-Z) ? 0x21
Interrupt-Vektor : 20B2:19C8
INT-Nr. (Abbruch CTRL-Z) ? 0x13
Interrupt-Vektor : 08C2:18C5
INT-Nr. (Abbruch CTRL-Z) ? 13
Interrupt-Vektor : F000:EAA6
INT-Nr. (Abbruch CTRL-Z) ? ^Z
/* -------------------------------------------------------------------- */ /* peekb - Lesen eines Bytes von einer Speicher-Adresse */ /* -------------------------------------------------------------------- */ #include <dos.h> char peekb(unsigned seg, unsigned offs);peekb liest ein einzelnes Byte von der durch seg:offs angegebenen Speicher-Adresse.
/* -------------------------------------------------------------------- */ /* peek - Lesen eines Int-Wertes (2 Bytes) von einer Speicher-Adresse */ /* -------------------------------------------------------------------- */ #include <dos.h> int peek(unsigned seg, unsigned offs);peek liest einene Integerwert (2 Bytes) von der durch seg:offs angegebenen Speicher-Adresse (und der nächsten Adresse).
/* -------------------------------------------------------------------- */ /* pokeb - Schreiben eines Bytes in eine Speicher-Adresse */ /* -------------------------------------------------------------------- */ #include <dos.h> void pokeb(unsigned seg, unsigned offs, char wert);pokeb schreibt das Byte mit dem Wert wert in die durch seg:offs angegebene Speicher-Adresse.
/* -------------------------------------------------------------------- */ /* poke - Schreiben eines Int-Wertes (2 Bytes) in eine Speicher-Adresse */ /* -------------------------------------------------------------------- */ #include <dos.h> void poke(unsigned seg, unsigned offs, int wert);poke schreibt den Integer-Wert wert in die durch seg:offs angegebene Speicher-Adresse (und in die nächste Adresse).
/* -------------------------------------------------------------------- */ /* inportb - Lesen eines Bytes von einer E/A-Adresse */ /* -------------------------------------------------------------------- */ #include <dos.h> unsigned char inportb(int eaadr);inportb liest ein einzelnes Byte von der durch eaadr angegebenen E/A-Adresse.
/* -------------------------------------------------------------------- */ /* inport - Lesen eines Int-Wertes (2 Bytes) von einer E/A-Adresse */ /* -------------------------------------------------------------------- */ #include <dos.h> int inport(int eaadr);inport liest einene Integerwert (2 Bytes) von der durch eaadr angegebenen E/A-Adresse (und der nächsten Adresse).
/* -------------------------------------------------------------------- */ /* outportb - Schreiben eines Bytes in eine E/A-Adresse */ /* -------------------------------------------------------------------- */ #include <dos.h> void outportb(int eaadr, unsigned char wert);outportb schreibt das Byte mit dem Wert wert in die durch eaadr angegebene E/A-Adresse.
/* -------------------------------------------------------------------- */ /* outport - Schreiben eines Int-Wertes (2 Bytes) in eine E/A-Adresse */ /* -------------------------------------------------------------------- */ #include <dos.h> void outport(int eaadr, int wert);outport schreibt den Integer-Wert wert in die durch eaadr angegebene E/A-Adresse (und in die nächste Adresse).
/*---------------------------------------------------------------------*/
/* Programm CMRAM */
/*---------------------------------------------------------------------*/
/* Lesen aus dem CMOS-RAM eines IBM/AT */
/* Beispiel für den direkten Zugriff auf E/A-Adressen in TURBO-C */
/*---------------------------------------------------------------------*/
#include <dos.h>
#include <stdio.h>
#define CM_AR 0x70 /* CMOS-RAM - Adress-Register */
#define CM_DR 0x71 /* CMOS-RAM - Daten-Register */
#define WRITELN putchar('\n')
#define HEADER "\n*** Lesen des CMOS-RAM ***\n"
void main(void)
{
unsigned cm_adr;
unsigned char wert;
printf(HEADER);
while ((printf("\nAdresse ? "),scanf("%i",&cm_adr))!=EOF)
{ outportb(CM_AR, cm_adr);
wert=inportb(CM_DR);
printf("Wert : %02X\n",wert&0x0ff);
}
WRITELN;
}
Bildschirmausgabe :
E:\RT\C>cmram
*** Lesen des CMOS-RAM ***
Adresse ? 0x07
Wert : 29
Adresse ? 0x08
Wert : 04
Adresse ? 0x09
Wert : 91
Adresse ? 0x10
Wert : 40
Adresse ? 0x12
Wert : F0
Adresse ? 0x15
Wert : 80
Adresse ? 0x16
Wert : 02
Adresse ? ^Z
Zum Inhaltsverzeichnis |
Zum nächsten Abschnitt |