1. Überblick
Die Sprache C ermöglicht es Benutzern, eine Datenstruktur anzugeben, die aus verschiedenen Datentypen besteht, die zu einem Ganzen zusammengefasst sind, um darauf zu verweisen. Die zu diesem Ganzen zusammengefassten Daten stehen in Beziehung zueinander. Eine solche Datenstruktur wird als Struktur bezeichnet, was einem Datensatz in anderen höheren Programmiersprachen entspricht.
Die allgemeine Form der Deklaration eines Strukturtyps ist wie folgt:
struct 结构体名
{成员列表};
Der Strukturname dient als Kennzeichnung des Strukturtyps, auch Struktur-Tag genannt. Innerhalb der geschweiften Klammern stehen die Mitglieder der Struktur, die zusammen die Struktur bilden. Jedes Mitglied sollte eine Typdeklaration haben, zum Beispiel:
Type name member name;
Die Mitgliederliste kann auch Feldliste genannt werden, und das erste Mitglied wird in der Struktur auch als Feld bezeichnet. Die Regeln für die Benennung von Mitgliedern sind dieselben wie die Regeln für die Benennung von Variablen.
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
2. Methoden zum Definieren von Variablen des Strukturtyps
Was zuvor angegeben wurde, ist nur ein Strukturtyp, der wie ein Modell ist, aber keine konkreten Daten enthält, und das System weist ihm keine tatsächlichen Speichereinheiten zu. Um Strukturtyp-Daten in einem Programm zu verwenden, sollten Variablen des Strukturtyps definiert und konkrete Daten darin gespeichert werden. Die folgenden drei Methoden können verwendet werden, um Variablen des Strukturtyps zu definieren.
(1) Zuerst den Strukturtyp deklarieren, dann Variablennamen definieren
Wie oben wurde ein Strukturtyp struct student definiert, und er kann verwendet werden, um Variablen zu definieren. Zum Beispiel:
struct student{ //结构体类型名
...
...
...
}student1, student2 //结构体变量名
student1 und student2 werden als Variablen vom Typ struct student definiert.
Nach dem Definieren von Strukturvariablen weist das System ihnen Speichereinheiten zu. Zum Beispiel belegen student1 und student2 jeweils 59 bytes im Speicher.
Beachten Sie, dass der Unterschied zwischen dem Definieren einer Variablen als Standardtyp (Basisdatentyp) und als Strukturtyp darin besteht, dass Letzteres nicht nur erfordert, die Variable als Strukturtyp anzugeben, sondern auch einen bestimmten Strukturtyp (zum Beispiel den Typ struct student), weil viele spezifische Strukturtypen definiert werden können. Wenn Sie eine Variable als Ganzzahl definieren, müssen Sie sie nur als int angeben.
(2) Variablen definieren, während der Typ deklariert wird
Zum Beispiel:
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1, student2;
Die Wirkung ist dieselbe wie bei der ersten Methode, das heißt, es werden zwei Variablen vom Typ struct student definiert, student1 und student2. Die allgemeine Form dieser Definition ist:
struct 结构体名
{
成员表列
}变量名表列;
(3) Variablen vom Strukturtyp direkt definieren
Die allgemeine Form ist:
struct
{
成员表列
}变量名表列;
Das heißt, der Strukturname erscheint nicht.
Zu Strukturtypen sollten einige Punkte erläutert werden:
- a. Typ und Variable sind unterschiedliche Konzepte und sollten nicht verwechselt werden. Sie können nur Variablen Werte zuweisen, auf sie zugreifen oder Operationen mit ihnen durchführen, nicht aber mit einem Typ. Zur Kompilierzeit wird für Typen kein Speicherplatz zugewiesen; Speicherplatz wird nur für Variablen zugewiesen.
- b. Mitglieder (d. h. Felder) einer Struktur können unabhängig verwendet werden, und ihre Rolle und Stellung entsprechen denen gewöhnlicher Variablen.
-
c. Ein Mitglied kann auch eine Strukturvariable sein. Zum Beispiel:
struct date // 声明一个结构体类型 { int month; int day; int year; } struct student { int num; char name[20]; char sex; int age; struct date birthday; char addr[30]; }student1, student2;Zuerst wird ein Typ struct date deklariert, der „date“ repräsentiert und drei Mitglieder enthält: month, day, year. Wenn dann der Typ struct student deklariert wird, wird das Mitglied birthday als Typ struct date angegeben.
- d. Mitgliedsnamen können mit Variablennamen im Programm übereinstimmen, aber sie repräsentieren nicht dasselbe Objekt.
3. Referenzierung von Strukturvariablen
(1) Eine Strukturvariable kann nicht als Ganzes eingegeben oder ausgegeben werden.
Nur jedes Mitglied in einer Strukturvariablen kann separat eingegeben oder ausgegeben werden. Die Art und Weise, auf ein Mitglied einer Strukturvariablen zu verweisen, ist:
Struct variable name.member name
Zum Beispiel repräsentiert student1.num das Mitglied num in der Variablen student1, das heißt das Element num von student1. Sie können einem Mitglied einer Variablen einen Wert zuweisen. Zum Beispiel: student1.num = 10010;
.Der Punkt ist der Mitglieds-(Komponenten-)Operator. Er hat die höchste Priorität unter allen Operatoren, daher kann student1.num als Ganzes behandelt werden. Die Wirkung der obigen Zuweisungsanweisung besteht darin, dem Mitglied num der Variablen student1 die Ganzzahl 10010 zuzuweisen.
(2) Wenn ein Mitglied selbst ein Strukturtyp ist, sind mehrere Mitgliedsoperatoren erforderlich, um das Mitglied der untersten Ebene Ebene für Ebene zu finden. Nur dem Mitglied der untersten Ebene kann ein Wert zugewiesen werden, auf es kann zugegriffen werden, oder es kann damit operiert werden.
Zum Beispiel: Auf die Mitglieder der Strukturvariablen student1 kann wie folgt zugegriffen werden:
student1.num student1.birthday.month
Beachten Sie, dass student1.birthday nicht verwendet werden kann, um auf das Mitglied birthday in der Variablen student1 zuzugreifen, da birthday selbst eine Strukturvariable ist.
((3) Mitglieder einer Strukturvariablen können verschiedene Operationen wie gewöhnliche Variablen ausführen (die erlaubten Operationen werden durch ihren Typ bestimmt).
student2.score = student1.score; sum = student1.score + student2.score; student1.age ++; ++ student1.age;
Da.der .-Operator die höchste Priorität hat, führt student1.age++ eine Inkrementoperation an student1.age aus, anstatt zuerst eine Inkrementoperation an age auszuführen.
(4) Sie können auf die Adresse eines Mitglieds einer Strukturvariablen verweisen. Sie können auch auf die Adresse einer Strukturvariablen verweisen. Zum Beispiel:
scanf("%d", &student1.num);// 输入 student1.num 的值
printf("%o", &student1);// 输出 student1 的首地址
Aber Sie können die folgende Anweisung nicht verwenden, um eine gesamte Strukturvariable zu lesen, zum Beispiel:
scanf("%d,%s,%c,%d,%f,%s", &student1);
Die Adresse einer Strukturvariable wird hauptsächlich als Funktionsparameter verwendet, um die Adresse der Struktur zu übergeben.
4. Initialisierung von Strukturvariablen
Wie Variablen anderer Typen können Strukturvariablen zum Zeitpunkt der Definition mit Anfangswerten versehen werden.Beispiel
5. Struktur-Arrays
Eine Strukturvariable kann einen Satz von Daten speichern (wie z. B. die Studentennummer, den Namen, die Noten usw. eines Studenten). Wenn in Berechnungen Daten von 10 Studenten verarbeitet werden sollen, sollte offensichtlich ein Array verwendet werden, und zwar ein Struktur-Array. Der Unterschied zwischen einem Struktur-Array und den zuvor eingeführten numerischen Arrays besteht darin, dass jedes Array-Element ein Datenelement vom Typ Struktur ist und sie jeweils verschiedene Member-(Komponenten-)Elemente enthalten.
5.1 Definieren von Struktur-Arrays
Ähnlich wie beim Definieren von Strukturvariablen muss sie nur als Array angegeben werden.
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
struct student stu[3];
Das Obige definiert ein Array stu, dessen Elemente vom Typ struct student sind, und das Array hat 3 Elemente. Sie können auch direkt ein Strukturarray definieren. Zum Beispiel:
struct student
{
int num;
....
}stu[3];
oder
struct
{
int num;
...
}stu[3];
5.2 Initialisierung von Strukturarrays
Wie andere Arten von Arrays können Strukturarrays initialisiert werden, zum Beispiel:
struct student
{
int mum;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu[3] = {{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"},
{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"},
{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"}};
Beim Definieren des Arrays stu kann die Anzahl der Elemente weggelassen werden, das heißt, sie wird in der folgenden Form geschrieben:
stu[] = {{...},{...},{...}};
Zur Kompilierzeit bestimmt das System die Anzahl der Array-Elemente anhand der Anzahl der als Anfangswerte angegebenen Strukturkonstanten.
Natürlich kann die Array-Initialisierung auch die folgende Form verwenden:
struct student
{
int num;
...
};
struct student stu[] = {{...},{...},{...}};
Das heißt, zuerst wird der Strukturtyp deklariert, dann wird das Array als dieser Strukturtyp definiert und beim Definieren des Arrays initialisiert.
Aus dem Obigen ist ersichtlich, dass die allgemeine Form der Initialisierung eines Strukturarrays darin besteht, nach dem Definieren des Arrays Folgendes hinzuzufügen:
5.3 Anwendungsbeispiel für Strukturarrays
Das folgende Beispiel veranschaulicht die Definition und Referenzierung von Strukturarrays.
Beispiel
Das Ausführungsergebnis ist wie folgt:
LI Li Fun Zhang Zhang Fun Li Fun Zhang Li Li: 3 Zhang: 3 Fun: 3
6. Zeiger auf Daten des Strukturtyps
Ein Zeiger auf eine Strukturvariable ist die Startadresse des von dieser Variable belegten Speichersegments. Eine Zeigervariable kann so gesetzt werden, dass sie auf eine Strukturvariable zeigt; in diesem Fall ist der Wert der Zeigervariable die Startadresse der Strukturvariable. Eine Zeigervariable kann auch verwendet werden, um auf ein Element in einem Struktur-Array zu zeigen.
6.1 Zeiger auf eine Strukturvariable
Anwendung eines Zeigers auf eine Strukturvariable:
Beispiel
.num oder stu
.name als tatsächliches Funktionsargument, um den Wert des tatsächlichen Arguments an den formalen Parameter zu übergeben. Die Verwendung ist dieselbe wie bei einer gewöhnlichen Variable als tatsächliches Argument, und sie gehört zur Wertübergabemethode. Es ist zu beachten, dass die Typen des tatsächlichen Parameters und des formalen Parameters übereinstimmen müssen.
NO. :89101 name: Li Lin sex:M score:89.500000 NO. :89101 name: Li Lin sex:M score:89.500000
Man kann sehen, dass die Ausgabe der beiden printf-Anweisungen dieselbe ist.
In der Sprache C kann aus Gründen der Bequemlichkeit und Anschaulichkeit (*p).num durch p->num ersetzt werden. Es repräsentiert das Member num in der Strukturvariablen *p (d. h. die Strukturvariable, auf die p zeigt). Ähnlich ist (*p).name äquivalent zu p->name.
Das heißt, die folgenden drei Formen sind äquivalent:
- a. Strukturvariable.Membername
- b. (*p).Membername
- c. p-> Membername
Die Ausgabe-Elemente der letzten printf-Funktion oben können wie folgt umgeschrieben werden:
printf("NO. :%ld\nname: %s\nsex:%c\nscore:%f\n",p->num, p->name, p->sex, p->score);
wobei->als Pfeiloperator bezeichnet wird.
Analysieren Sie die folgenden Operatoren:
- p -> n liefert den Wert des Members n in der Strukturvariablen, auf die p zeigt.
- p -> n ++ liefert den Wert des Members n in der Strukturvariablen, auf die p zeigt, und erhöht ihn nach Verwendung des Werts um 1.
- ++p -> n liefert den Wert des Members n in der Strukturvariablen, auf die p zeigt, und erhöht ihn um 1 (zuerst inkrementieren).
6.2 Zeiger auf ein Struktur-Array
Wie bereits eingeführt, können Zeiger und Zeigervariablen verwendet werden, um auf Arrays oder Array-Elemente zu zeigen. Ebenso können Zeigervariablen auch dazu verwendet werden, auf Arrays von Strukturen und deren Elemente zu zeigen.
Anwendung eines Zeigers auf ein Array von Strukturen.
Beispiel
Das Ausführungsergebnis ist wie folgt:
No. name sex age 10101 Li Lin M 18 10102 Zhang Fun M 19 10103 Wang Min F 20
Beachten Sie die folgenden zwei Punkte:
(1) Wenn der Anfangswert von p stu ist, d. h., er zeigt auf das erste Element, dann zeigt er nach p + 1 auf die Startadresse des nächsten Elements. Zum Beispiel:
(++p) -> num erhöht zuerst p um 1 und ruft dann den Wert des Members num in dem Element ab, auf das er zeigt (d. h. 10102).
(p++) ->num ruft zuerst den Wert von p->num ab (d. h. 10101) und erhöht dann p um 1, sodass er auf stu zeigt
Beachten Sie den Unterschied zwischen den beiden obigen.
(2) Das Programm hat den Zeiger p als eine Variable definiert, die auf Daten vom Typ struct student zeigt. Er kann nur auf Daten vom Typ struct student zeigen (der Wert von p ist die Startadresse eines Elements des stu-Arrays) und kann nicht auf ein Member innerhalb eines Elements des stu-Arrays zeigen (d. h., die Adresse von p kann keine Member-Adresse sein). Zum Beispiel ist Folgendes falsch:
p = &stu[1].name
Zur Kompilierzeit tritt ein Fehler auf. Denken Sie niemals, dass, nur weil p eine Adresse speichert, ihm jede beliebige Adresse zugewiesen werden kann. Wenn die Adresstypen unterschiedlich sind, kann eine erzwungene Typkonvertierung verwendet werden. Zum Beispiel:
p = (struct student *)&stu[1].name;
An diesem Punkt die Startadresse des name-Members von stu
6.3 Verwendung von Strukturvariablen und Zeigern auf Strukturen als Funktionsparameter
Es gibt drei Methoden, den Wert einer Strukturvariablen an eine andere Funktion zu übergeben:
- (1) Verwenden Sie ein Member einer Strukturvariablen als Argument. Zum Beispiel verwenden Sie stu
- (2) Verwenden Sie eine Strukturvariable als Parameter. Ältere Versionen des C-Systems erlaubten nicht, dass eine Strukturvariable als tatsächliches Argument verwendet wird; ANSI C hob diese Einschränkung auf. Wenn jedoch eine Strukturvariable als tatsächliches Argument verwendet wird, wird die Methode der Wertübergabe (pass-by-value) angewandt, und alle von der Strukturvariablen belegten Speichereinheiten werden der Reihe nach an den formalen Parameter übergeben. Der formale Parameter muss ebenfalls eine Strukturvariable desselben Typs sein. Während des Funktionsaufrufs belegt der formale Parameter ebenfalls Speichereinheiten. Diese Übergabemethode hat einen großen Overhead sowohl hinsichtlich Speicherplatz als auch Zeit. Wenn die Struktur sehr groß ist, ist der Overhead beträchtlich. Da außerdem die Wertübergabemethode verwendet wird, kann, wenn der Wert des formalen Parameters (ebenfalls eine Strukturvariable) während der Ausführung der aufgerufenen Funktion geändert wird, dieser Wert nicht an die aufrufende Funktion zurückgegeben werden, was häufig Unannehmlichkeiten verursacht. Daher wird diese Methode im Allgemeinen seltener verwendet.
- (3) Verwenden Sie einen Zeiger auf eine Strukturvariable (oder ein Array) als tatsächliches Argument und übergeben Sie die Adresse der Strukturvariablen (oder des Arrays) an den formalen Parameter.
Verwendung einer Strukturvariablen als Funktionsparameter.
Beispiel
Ändern Sie das Obige so, dass ein Zeiger auf eine Strukturvariable als aktuelles Argument verwendet wird.
Beispiel
7. Umgang mit verketteten Listen mit Zeigern
7.1 Überblick über verkettete Listen
Eine verkettete Liste ist eine häufige und wichtige Datenstruktur. Sie ist eine Struktur, die dynamische Speicherzuweisung durchführt.
Eine verkettete Liste hat eine Kopfzeiger-Variable, die eine Adresse speichert, die auf ein Element zeigt. Jedes Element in der verketteten Liste wird als Knoten bezeichnet. Jeder Knoten sollte zwei Teile enthalten: einen sind die tatsächlichen vom Benutzer benötigten Daten, und der andere ist die Adresse des nächsten Knotens. Man sieht, dass der Kopfzeiger head auf das erste Element zeigt und das erste Element auf das zweite Element, ... bis zum letzten Element. Dieses Element zeigt nicht mehr auf andere Elemente; es wird das Ende der Liste genannt, und sein Adressteil enthält NULL (bedeutet eine leere Adresse), und die verkettete Liste endet hier.
Man sieht, dass die Elemente in einer verketteten Liste nicht zusammenhängend im Speicher gespeichert werden müssen. Um ein bestimmtes Element zu finden, muss man zuerst das vorherige Element finden, und erst dann kann man das nächste Element anhand der Adresse des nächsten Elements finden, die es bereitstellt. Ohne den Kopfzeiger head kann nicht auf die gesamte verkettete Liste zugegriffen werden.
Man sieht, dass diese Art von Datenstruktur einer verketteten Liste unter Verwendung von Zeigervariablen implementiert werden muss. Das heißt, ein Knoten sollte eine Zeigervariable enthalten, um die Adresse des nächsten Knotens zu speichern.
Früher haben wir Strukturvariablen eingeführt. Sie als Knoten in einer verketteten Liste zu verwenden, ist am besten geeignet. Eine Strukturvariable enthält mehrere Mitglieder; diese Mitglieder können numerische Typen, Zeichentypen, Array-Typen oder Zeigertypen sein. Wir verwenden dieses Mitglied vom Zeigertyp, um die Adresse des nächsten Knotens zu speichern. Zum Beispiel können wir einen solchen Strukturtyp entwerfen:
struct student
{
int num;
float score;
struct student *next;
};
Unter ihnen werden die Member num und score verwendet, um nützliche Daten im Knoten zu speichern (die vom Benutzer benötigten Daten). next ist ein Member vom Zeigertyp, das auf Daten vom Typ struct student zeigt (dies ist der Strukturtyp, der next enthält). Ein Member vom Zeigertyp kann auf Strukturdaten anderer Typen zeigen und auch auf Daten des Strukturtyps, in dem es sich befindet. Nun ist next ein Member vom Typ struct student, und es zeigt auf Daten vom Typ struct student. Auf diese Weise kann eine verkettete Liste erstellt werden.
Bitte beachten Sie: Das bloße Definieren eines Typs struct student weist noch keinen Speicherplatz zu; Speichereinheiten werden erst zugewiesen, wenn Variablen definiert werden.
7.2 Einfache verkettete Liste
Das folgende Beispiel veranschaulicht, wie eine einfache verkettete Liste erstellt und ausgegeben wird:
Beispiel
Ausführungsergebnis:
99101 89.5 99103 90.0 99107 85.0
7.3 Funktionen, die zur Verarbeitung dynamisch verketteter Listen benötigt werden
(1) malloc-Funktion
void *malloc(unsigned int size);
Ihre Funktion besteht darin, im dynamischen Speicherbereich des Arbeitsspeichers einen zusammenhängenden Bereich der Länge size zu reservieren. Der Wert dieser Funktion (d. h. der Rückgabewert) ist ein Zeiger auf die Startadresse des reservierten Bereichs (Basistyp void). Wenn die Ausführung dieser Funktion fehlschlägt (zum Beispiel bei unzureichendem Speicherplatz), gibt sie den Nullzeiger NULL zurück.
(2) calloc-Funktion
void *calloc(unsigned n, unsigned size);
Ihre Funktion besteht darin, im dynamischen Speicherbereich des Arbeitsspeichers n zusammenhängende Bereiche der Länge size zu reservieren. Die Funktion gibt einen Zeiger auf die Startadresse des reservierten Bereichs zurück; wenn die Reservierung nicht erfolgreich ist, gibt sie NULL zurück. Die calloc-Funktion kann verwendet werden, um dynamischen Speicherplatz für ein eindimensionales Array zu reservieren, wobei n die Anzahl der Array-Elemente ist und jedes Element die Länge size hat.
(3) free-Funktion
void free(void *p);
Ihre Funktion besteht darin, den Speicherbereich freizugeben, auf den p zeigt, damit dieser Teil des Speicherbereichs von anderen Variablen verwendet werden kann. p ist der Wert, der vom letzten Aufruf der Funktion calloc oder malloc zurückgegeben wurde. Die Funktion free hat keinen Rückgabewert. Bitte beachten Sie: Die von früheren C-Versionen bereitgestellten Funktionen malloc und calloc gaben Zeiger auf Zeichendaten zurück. Die von ANSI C bereitgestellten Funktionen malloc und calloc sind als Typ void * festgelegt.
7.4 Erstellen einer dynamischen verketteten Liste
Das sogenannte Erstellen einer dynamischen verketteten Liste bedeutet, eine verkettete Liste während der Programmausführung von Grund auf aufzubauen, d. h. Knoten nacheinander anzulegen, Daten für jeden Knoten einzugeben und die Verknüpfungsbeziehung von vorne nach hinten herzustellen.
Beispiel
Im Folgenden finden sich verschiedene Operationen an der verketteten Liste
Verkettete Liste ausgeben:
void printlist(struct student *head)
{
struct student *p;
p = head;
if(head != NULL)
{
do
{
printf("num=%d score=%5.2f\n", p->num, p->score);
p = p->next;
} while (p != NULL);
}
/* while(p -> next != NULL)
{
printf("num=%d score=%f\n", p->num, p->score);
p = p->next;
}*/
}Einen Knoten löschen:struct student *delNode(struct student *head, int num)
{
printf("delNode.\n");
struct student *p1, *p2;
if(head == NULL)
{
printf("The List is NULL.\n");
}
else
{
p1 = head;
while(p1->next != NULL && p1->num != num)
{
p2 = p1;
p1 = p1->next;
}
if(p1->num == num)
{
if(p1 == head)
head = p1->next;
else
p2->next = p1->next;
}
else
printf("Can not find list num.\n");
}
return head;
}
Einen Knoten aktualisieren:
struct student *update(struct student *head, int index, int num, float score)
{
printf("update.\n");
struct student *p;
if(head == NULL)
{
printf("The List is NULL.\n");
}
else
{
p = head;
while(p->next != NULL && p->num != index)
{
p = p->next;
}
if(p->num == index)
{
p->num = num;
p->score = score;
}
else
printf("Can not find list index.\n");
}
return head;
}
Einen Knoten hinzufügen:
struct student *add(struct student *head, int index, int num, float score)
{
printf("add.\n");
struct student *p1, *p2, *p3;
if(head == NULL)
{
printf("The List is NULL.\n");
}
else
{
p1 = p2 = head;
while(p1->next != NULL && p1->num != index)
{
p1 = p1->next;
p2 = p1;
}
if(p1->num == index)
{
p3 = (struct student *)malloc(LEN);
p3->num = num;
p3->score = score;
if(p2->next == NULL)
{
p2->next = p3;
p3->next = NULL;
}
else
{
p3->next = p2->next;
p2->next = p3;
}
}
else
printf("Can not find list index.\n");
}
return head;
}
Originallink: http://www.cnblogs.com/qkhhxkj/archive/2011/06/28/2091818.html
Referenzbuch: Das Kapitel über Strukturtypen in dem Buch *C Programming* von Herrn Tan Haoqiang.