C语言:随笔8--结构体
在學習時我們要知道為什么要用到結構體?
因為有時需要將不同類型的數據組合(封裝)成一個有機的整體,以便于引用。(比如類就是將方法和屬性封裝起來,這里結構就是將不同的變量東西給封裝起來)
例如一個學生有學號/姓名/年齡/地址等屬性
int num;
char name[20];
char sex;
int age;
char addr[30];
一、結構體
定義一個結構的一般形式為:
struct 結構名
{成員列表
};
//成員列表由若干個成員組成,每個成員都是該結構的一個組成部分。對每個成員也必須作類型說明,其形式為:
類型說明符 成員名;//int num;//比如:定義的student學生類他有6個屬性
struct stucent
{int num;char name[20];char sex;int age;float score;char addr[30];};
可以采用以下三種方法定義結構體類型變量:
(1)先聲明結構體類型再定義變量名:
//比如:定義的student學生類他有6個屬性
struct stucent//struct 結構體名
{int num;char name[20];char sex;int age;float score;char addr[30];//成員表列};
struct student student1,student2;//類型名 結構體 變量名,變量名
//定義了student1和student2為struct student類型的變量,即他們具有struct student類型的結構。
(2)再聲明類型的同時定義變量
/比如:定義的student學生類他有6個屬性
struct stucent
{int num;char name[20];char sex;int age;float score;char addr[30];}student1,student2//變量名列表
//在定義結構體變量后,系統會為之分配內存單元。例如studet1和student2在內存中各占?子節
(4+20+1+4+4+30=67)
(3)直接定義結構體類型變量
//即不出現結構體名
struct
{成員表列
}變量名表列;
結構的嵌套定義(結構里面的結構)
| Num | name | sex | age | birthday | addr | ||
| Month | day | year | |||||
首先應該定義結構data,由Month(月),day(日),year(年),三個成員組成。
在定義并說明變量boy1和boy2時,其中的成員birthday被說明為data結構類型。成員名可與程序中其它變量同名,互不干擾。(因為他們的作用域不同)
//結構的嵌套
struct data
{int month;int day;int year;
};struct
{
int num;
char name[20];
char sex;
struct data birthday;
float score;
}boy1,boy2={102,“jane”,'M',98.5};//結構體變量初始化,對變量賦值(有點像數組)
boy1=boy2;//將boy2的值給了boy1,全體成員copy//如果對boy1使用點"."運算符進行了賦值操作的話,那么boy2=boy1;(將boy1的全體成員賦值給了boy2)這個語句就可以把boy1里邊成員變量的值直接都賦值給boy2的成員變量,因此Boy2不用再一個個的進行賦值。
再定義了結構體變量以后,當然可以引用這個變量。應遵守:
(1)不能將一個結構體變量作為一個整體進行輸入輸出。
應該用點”.“成員運算符。
//比如打印student1中的各個變量的值時,我們不可以直接
printf("%d,%s,%c,%d,%f,%\n",student1);//正確引用結構體變量中成員的方式為:
//結構體變量名.成員名//點操作符來進行引用
//student1.num//表示student1變量中的num成員。
//student1.num=100;//可以對變量的成員賦值
“.”是成員(分量)運算符,它在所有運算符中優先級最高,因此可以把student1.num作為一個整體看待。上面的賦值語句是將整數100付給student1變量中的成員num。
也不能使用以下語句整體讀入結構體變量:(要精確到它最下邊的每一個級別)
scanf("%d,%s,%c,%d,%f,%s",&student1);
(2)如果成員本身又屬一個結構體類型,則要用若干個成員運算符,一級一級地找到最低的以及的成員。只能對最低級的成員賦值或者存取以及運算。
對上面定義的結構體變量student1,可以這樣訪問各成員:
student1.num
student1.birthday.month//因為birthday也是一個結構的變量名,他不能代表全體成員,我要再點進去,訪問到最下級,最下級才有操作的權力。
(3)結構體變量的成員可以向普通變量一樣進行各種運算(根據類型決定可以進行的運算)。
(4)可以引用結構體變量成員的地址(&boy1.num),也可以引用結構體變量(&boy1)的地址。(&boy1和&boy1.num他們兩個地址是一樣,跟數組類似)
結構體變量的地址主要用做函數參數,傳遞結構體變量的地址。(其實第一個元素的地址也行,但是一般使用的是結構體變量的地址。名稱比較有代表性)
二、結構體數組
一個結構體變量中可以存放一組數據(如一個學生的學號、姓名、成績等數據)。如果有10個學生的數據需要運算,顯然應該用數組,這就是結構體數組。
結構體數組與以前介紹過的數值型數組不同指出在于每個數組元素都是一個結構體類型的數據,他們都分別包括各個成員(分量)項。
下面以一個通訊錄的例子做說明:
#include<stdio.h>
#include<stdlib.h>
#define NUM 3struct person//定義person結構
{char name[20];char phone[10];
};
void main()
{struct person man[NUM];//定義man數組,結構體數組,有三個元素int I;for(I=0;i<NUM;i++)//一個元素包含了兩個項分別是name和phone。{printf("input name:\n");gets(man[i].name);printf("input phone:\n");gets(man[i].phone);}printf("\tname\t\t\t\t\tphone\n\n");for(i=0;i<NUM;i++){printf("%20s\t\t\t%20s\n",man[i],name,man[i].phone);}system("pause")
}
結構體數組的定義:
//第一種定義方法
struct stucent
{int num;char name[20];char sex;int age;float score;char addr[30];};
struct student student[3];//定義了一個數組有三個元素,這個數組名稱叫student,有三個元素,每一個元素都有上述6個項//第2種定義方法
struct stucent
{int num;char name[20];char sex;int age;float score;char addr[30];}studet[3];
結構體數組的初始化:
struct stucent
{int num;char name[20];char sex;int age;float score;char addr[30];}stu[2]={{101,“Li”,'M',18,87.5,"Beijing"},{102,“zhang",'F',19,99,"Shanghai"}};//或者也可以用以下形式初始化:
struct student
{int num;...
};
student student str[]{{...},{...},{...}};
//即先聲明結構體類型,然后定義數組為該結構體類型,再定一數組是初始化。
例題:對候選人得票的統計程序。設有3個候選人,每次輸入一個得票的候選人的名字,要求最后輸出個各人的票結果。并將最高的得票者輸出。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#define NUM 5
struct person
{char name[20];int count;
}candidate[NUM] = {{"A", 0},{"B", 0}, {"C", 0},{"D", 0}, };//定義結構體數組char *winner();int main()
{int i, j;char candidate_name[20];printf("歡迎進入2020年火爆網站評選系統:\n\n");printf("選擇項目:A,B,C,D \n\n");for( i=1; i <= 6; i++ ){printf("第 %2d 位投票, 請選擇支持的候選人: ", i);scanf("%s", candidate_name);for( j=0; j < NUM; j++ )//j小于5,所以j只能到4,因為有4個A,B,C,D{if( 0 == strcmp(candidate_name, candidate[j].name) ){candidate[j].count++;}}}printf("\n");for( i=0; i < 5; i++ )//打印ABCD分別的得票數{printf("%s 得票數為: %d\n", candidate[i].name, candidate[i].count );}printf("\n");printf("本次評選活動的勝利者的: %s\n", winner() );//調用winner()函數,比較得票最高的是誰printf("\n");system("pause");
}char *winner()
{int i =0 , winner = i;//相當于winner=0for( i=1; i < NUM; i++ ){if( candidate[winner].count < candidate[i].count )//結構體數組的第一個元素的count項依次與后邊的元素的count項比較比較{winner = i;}}return candidate[winner].name;
}
----指向結構體類型數據的指針---
一個結構體變量的指針就是該結構體變量所占據的內存段的其實地址。(指針就是一個存放地址的變量)
可以設一個指針變量,用來指向一個結構體變量,此時該指針變量的值(也就是一個地址)是結構體變量的起始地址。(指針真的很偉大,指啥都行)
指針變量也可以用來指向結構體數組中的元素。
結構體指針變量的一般形式:
struct 結構名 *結構指針變量名//*表示他是指針
//例如前面定義的stu這個結構,說明一個指向stu的指針變量名pstu,可以寫為:
struct stu *pstu;//*表示它是一個指針,stu表示他是哪一個結構的指針。
//當然也可以在定義stu結構時同時說明pstu。與前面討論的各類指針變量相同,結構指針變量也必須要先賦值后才能使用。(不然就是個無頭案)
//賦值是把結構變量的首地址賦予該指針變量,不能把結構名賦予該指針變量。
結構名和結構變量名不是一個東西(一個是student,一個是boy1)。例如
//如果boy是被說明為stu類型的結構變量,則:
struct stu//結構為stu
{...
}boy={...,...,...,};//結構變量為boy,并同時賦初值
//(還可以結構變量名再有一個girl,不止一個boy了這次,所以再聲明一個指針指向girl就好了,訪問方式跟下邊雷同)
struct stu *pstu;//*表示它是一個指針,stu表示他是哪一個結構的指針。//定義結構變量指針
pstu=&boy;//是正確的//將boy的初始值地址給了它
(*pstu).name//正確;
(*pstu)->name//正確;
boy.name//正確//通過三種形式可以輸出
pstu=&stu;//是錯誤的
//為什么呢???
//因為結構名stu和結構變量boy是兩個不同的概念,不能混淆,結構名只能表示一個結構形式,編譯系統并不對它分配內存空間。
//只有當某變量被說明定義為這種類型的結構時,才對該變量分配存儲空間。在定義時操作系統才會為它分配空間。只是說明沒定義不分配空間。
//因此上面&stu這種寫法是錯誤的,不可能去取一個結構名的首地址。(因為結構名本來是有地址的,他只是讓編譯器知道,編譯器在編譯時會申請空間)有了結構指針變量,就能更方便的訪問結構變量的各個成員。//其訪問的一般形式為:
(*結構指針變量).成員名
//或者:
結構指針變量->成員名
//例如:
(*pstu).num
//或者:
pstu->num
例子:指向結構體變量的指針變量應用(如上的三種形式輸出)。
三、結構指針變量做函數參數
將一個結構體變量的值傳遞給另外一個函數,有三個方法:
(1)用結構體變量的成員做參數。
(2)用結構體變量作實參。(將結構體變量的名稱作為實參)
(3)用指向結構體變量(或數組)的指針作實參,將結構體變量(或數組)的地址傳遞給形參。
例子:有一個結構體變量stu,內含學生學號、姓名和三門課程的成績。通過調用函數print中將他們輸出。
(要求是先用結構體變量做函數參數(作實參傳遞給它)(2))
這個例子是將整個結構體復制過去,因為void print(struct student);這里的參數是整個結構體變量。(傳值)
#include<stdio.h>
#include<string.h>
struct student//定義結構
{int num;char name[20];//字符串,(name[20]出現在棧里面,實實在在的空間,而下面的“zhangsan字符串出現在數據常量區”//改變為char *name;的話下邊的語句stu.name="zhangsan";是將數據區常量的地址傳遞給name這個指針而已。float score[3];//浮點型數組
};
void print(struct student);//函數的參數是這個結構體,一個結構體作為他的形參
void main()
{struct student stu;//定義這個結構體變量的名稱為stustu.num=8;strcpy(syu.name,"zhangsan");//如果用stu.name="zhangsan";這個語句會錯誤(復制號的左邊必須是一個左值l-value(可以簡單的理解l-value就是一個地址,必選是一個指針所以上邊就必須改一下定義為char *name;這樣的話這句話就沒有錯誤了)),stu.score[0]=98.2;stu.score[1]=93.2;stu.score[2]=98.7;//分別賦值print(stu);//把結構體變量作為實參傳遞給他
}
void print(struct student stu)//print這個函數知道了結構體變量的位置就能夠索引到他的名稱學號成績并分別打印
{printf("\tnum :%d\n",stu.num);printf("\tname :%s\n",stu.name);printf("\tscore_1 :%5.2f\n",stu.score[0]);printf("\tscore_2 :%5.2f\n",stu.score[1]);printf("\tscore_3 :%5.2f\n",stu.score[2]);printf("\n")
}
(要求用指向結構體變量的指針作實參(3))
第二個方法復制過去的只是一個地址,那么從效率上來說,傳遞的比較少的還是第二種方法。(傳址)
#include<stdio.h>
#include<string.h>
struct student//定義結構
{int num;char name[20];//字符串,(name[20]出現在棧里面,實實在在的空間,而下面的“zhangsan字符串出現在數據常量區”//改變為char *name;的話下邊的語句stu.name="zhangsan";是將數據區常量的地址傳遞給name這個指針而已。float score[3];//浮點型數組
};
void print(struct student *);//函數的參數是指針
void main()
{struct student stu;//定義這個結構體變量的名稱為stustu.num=8;strcpy(syu.name,"zhangsan");stu.score[0]=98.2;stu.score[1]=93.2;stu.score[2]=98.7;//分別賦值print(&stu);//因為上邊是指針,指針必須接收一個地址嘛,所以這里傳遞給他的那就是這個結構體的地址了
}
void print(struct student *p)//用指針來接收,p指向的還是這個結構,用p來打印
{printf("\tnum :%d\n",p->num);printf("\tname :%s\n",p->name);printf("\tscore_1 :%5.2f\n",p->score[0]);printf("\tscore_2 :%5.2f\n",p->score[1]);printf("\tscore_3 :%5.2f\n",p->score[2]);printf("\n")
}
---動態存儲分配----
在數組一章中,曾介紹過數組的長度是預先定義好的(不是直接說明的話,但是也要隱身間接告訴編譯器這個數組多長),在整個程序中固定不變。C語言中不允許動態數組類型。
int a[10]={0,1,2,3,4,5,6,7,8,9};//告訴他長度
char a[ ];//編譯器會自動計算字符串的長度,然后把[ ]自動的給填充上。(所以數組必須是預先定義好的,規定好有多長的)
int a[n];//這樣是不可以的。因為C語言中不允許動態數組類型。(用變量表示長度,想對數組的大小作動態說明,這是錯誤的。因為編譯器不知道你的數組是多大,那他定義的時候就不知道怎么分配空間,但是呢在實際的編程中,往往會發生這種情況,即所需的內存空間取決于實際輸入的數據,而無法預先確定;所以對于這種問題,用數組的辦法很難解決(因為數組在定義時就必須規定有多長的數據))
為了解決上述問題,C語言提供了一些內存管理函數,這些內存管理函數可以按需要動態的分配內存空間,也可把不再使用的空間回收待用,為有效地利用內存資源提供了手段。
常用的內存管理函數有以下三個:
(1)分配內存空間的函數malloc、calloc
(2)釋放內存空間函數free
----malloc函數
//函數原型為
void *malloc(unsigned int size)//返回一個空指針,參數是要申請的一個尺寸,尺寸是無符號整型
//其作用是在內存的動態存儲區中分配一個長度為size的連續空間(size是一個無符號數)。
//此函數的返回值是一個指向分配域的(要分配的這個空間的)起始地址的指針(類型為void)。
//如果此函數未能成功的執行(例如存儲空間不足),則返回空指針(NULL)表示0。
----calloc函數
//函數原型為:
void *calloc(unsigned n,unsigned size);
//其作用是在內存的動態存儲區中分配n個長度為size的連續空間。
//函數返回一個指向分配域起始地址的指針。
//如果分配不成功,返回NULL。
//用calloc函數可以為一維數組開辟動態存儲空間,n為數組元素個數,每個元素長度為size。
(,這種用法用的少,上個用法用的多,因為這種用法是結合數組來使用的)
----free函數
//函數原型為void free(void *p);
//無返回值,將一個指針(void *p),這個指針就是你將要釋放的,剛才申請的那個內存的地址
//起作用是釋放由p指向的內存區,使這部分內存區能被其他變量使用
//p是最近一次調用calloc或malloc函數時返回的值
//free函數無返回值
----鏈表(這是一個數據結構)
什么是鏈表?
答:鏈表是一種常見的重要的數據結構,是動態的進行存儲分配的一種結構。(所以要說剛才的三個函數)
鏈表的組成:
頭指針:存放一個地址,該地址指向第一個元素。
結點:用戶(元素)需要的實際數據和鏈接節點的指針。
凸
上邊ABCD就是四個結點,頭指針第一個指針,那么我們發現一個特點,第一個指針存放的是一個地址,地址指的是第一個數據,第一個數據有兩部分,第一部分是數據的內容,另一部分是指向下一個數據的地址;然后下一個數據又有兩個部分,一個是內容,另一個是再指向下一個數據的地址;依次到最后沒有就指向Null,表示這個鏈表結束。那我們會發現這個鏈表說白了跟數組相似,數組的數據也是一個挨著一個構成的,而鏈表呢也是一個挨著一個,但是鏈表跟數組有一個不同的點,鏈表一個元素是放兩個東西,一個是元素的內容,另一個是下一個元素的地址。那這樣的話最簡單的一個好處就是鏈表可以動態的分配。怎么動態的分配呢?1、臨時加多一個E,將這個Null改為E的地址,然后將E指向下一個,沒有的話就是Null,有的話就指向下一個;2、我們臨時去掉B也是可以的,將A存放的地址指向C(直接把B人間蒸發);3、想在A和C之間添插B可以嗎?可以的。我將B的地址指向了C,將A的地址指向了B,給B賦值元素,那就實現了在A和C中間插入B;所以說鏈表這個數據結構使得我們整個分配空間變得更加的靈活。比數組的好處就是體現在這幾個方面。
嘗試根據下圖建立鏈表
鏈表有三個數據,三個結點。源代碼如下:
//由上圖可以看到他有三個結點,每個結點都有元素(一個是number學號、還有score分數、還有地址(存放下一個的地址))
#include<stdio.h>
struct student
{long num;//長型存放學號float score;//分數struct student *next;//(相當于結構體里邊有個結構體指針)//因為我們下一個指向的是跟他相同結構的也是student這個結構的一個數據,那我們這里就指向了一個指向結構的指針,指向student這個結構//這是一個指針,存放的地址就是下一個這樣的結構,大家結構都是一樣的只是位置不同//next這個指針變量,是一個結構體類型的所以它需要指向的是一個結構體(類比于 int *p;相當于int型的這個指針變量,需要指向的是int型的數據)
};
void main()
{struct student a,b,c,*head;//定義abc三個結點和一個head是一個結構體指針。a.num=10101;a.score=89.5;b.num=10103;b.score=90;c.num=10107;c.score=85;//賦值head=&a;//head是鏈表的頭指針它必指向鏈表的第一元素,我們只需要知道這個鏈表的第一個元素在哪里,我們接著的第幾個元素都可以索引到,因為我們接著的元素都會存放下一個元素的地址。//head里邊存放的地址是指向第一個元素a.next=&b;//a指向b//因為是指針指向下一個地址b.next=&c;//b指向cc.next=null//c最后的地址就存放Null,表示鏈表結束do//使用do while依次打印出來,只要head不等于0{printf("%ld %5.1f\n",head->num,head->score);head=head->next;//第一次循環相當于head=a.next;而a.next=&b;//為什么每次執行的都是head,不是就一個head嗎?head->a->b->c->Null。(因為我們這里并沒有規定head必須指向頭結點,所以head的指向是可以變化的)(它這里是把 head 當普通指針使用了,每次改變 head 的值,這樣 head 就不指向頭結點了,而是每次指向下一個節點)}while(head);//這個的意思就是while(head!=0);//while(head!=Null);//NUll就是0//他的地址的下一個值是有指向一個結構的,那么他就可以繼續把他打印出來,直到最后一個,因為最后一個他是指向Null,所以這里head=head->next;這個head 是空的,那么整個程序結束。}//通過鏈表索引分別將a元素,b元素,c元素打印出來。
---建立動態鏈表
所謂建立動態鏈表是指在程序執行過程中從無到有地建立起一個鏈表,即一個一個地開辟結點和輸入各結點數據(即根據需要動態的開辟和消除結點,這叫做動態建立鏈表,這體現了鏈表比數組的優越性和時效性),并建立起前后相鏈的關系(鏈表是一個連著一個)。
根據下面的分析寫一個程序建立一個含有學生(學號,成績)數據的單向動態鏈表。
(約定:如果學號不為0的話就把他錄入;如果輸入學號為0,(就表示建立鏈表完成了)則表示建立鏈表的過程完成了,該結點不應連接到鏈表中)
分析如下:
我們把他寫成一個最簡單的盒子流程圖,這個圖怎么分析呢?要畫畫,畫圓和三角形來分析。按照順序來做(三角形表示結點)
head=Null,n=0表示初始化為0,照抄就好了。
算法的實現:
(1)如果輸入的p1->num不等于0,則輸入的是第一個結點(n=1),令head=p1,即把p1的值賦給head,也就是使head也指向新開辟的結點,p1所指向的新開辟的結點就成為鏈表中第一個結點。(那么這么說我們必須把三個結點都指向第一個數據,因為只有這一個數據能讓我們指向。)
(2)再開辟另一個結點并使P1指向它,接著輸入該結點的數據。
(3)如果輸入的p1->num不等于0,則應連入第二個結點(n=2),將新結點的地址賦給第一個結點的next成員。
(4)接著使得p2=p1,也就是使p2指向剛才建立的結點。
圖(圖說明)
剛才建立第一個結點的時候head ,p1,p2都是指向的第一個結點。(圖a)當出現第二個結點的時候p1首當其沖就沖了上去,然后又把p2給拉了過去(圖b),這時候第一個結點只有head指向它,并且引導第一個結點的next指針指向了第二個結點,這時候完成這一步的時候三個指針都指向了第二個結點(圖c)。這樣的話第三個第4個也是以此類推。有一個過程的。
(5)再開辟一個結點并使p1指向它,并輸入該結點的數據。
(6)在第三次循環中,由于n=3(n不等于1),又將p1的值賦給p2->next,也就是將第3個結點連接到第2 個結點之后,并使p2=p1,使p2指向最后一個結點(也就是第三個結點)。
在第三次循環我們就把他結束掉,如下圖:(怎么結束呢,就使它最后的next指向Null)(第三步跟第二步是重復的,先使p1指向它,接著使p2指向它,他的next也指向它)
(7)再開辟一個新結點,并使p1指向它,輸入該結點的數據。由于p1->num的值為0,不再執行循環,此新結點不應被連接到鏈表中。
(8)將Null賦給p2->next.
(9)建立鏈表過程至此結束,p1最后所指的結點未鏈入鏈表中,第三個結點的next成員的值為Null,他不指向任何結點。
下面程序還實現了鏈表的輸出,怎么實現呢?
首先要知道鏈表第一個結點的地址,也就是要知道head值。(只要知道了地址我們相當于把握了整個鏈表)
設一個指針變量p,先指向第一個結點(也就是head結點),輸出p所指的結點,然后使p后移一個結點(p這時候移向了head->next指針,移向了下一個結點),再輸出,直到鏈表的未結點。如此就可以得到下面流程圖
下面程序就是動態輸入動態停止
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>#define LENsizeof(struct student)//結構的大小
struct student *creat();//創建鏈表
void print(struct student *head)//打印鏈表(的函數)
struct student
{int num;float score;//分數struct student *next;
};
int n;//全局變量,用來記錄存放了多少數據void main()
{struct student *stu;stu=create();//12這個stu就是他的頭結點,指針指向他的頭結點print(stu);//然后我把這個頭當成參數來print(是自定義的函數,實現的是打印這個鏈表)接收他的頭printf("\n\n");system("pause");
}struct student *creat()//creat創建函數
{struct student *head;struct student *p1,*p2;//在這里只是開辟了四個字節,說明這些指針是指向這個結構的,在內存并沒有真正的創建整個結構那么大小的空出來。只是創建了一個指針,說它指向這么一個結構所以在2處就必須動態的給他初始化,給他獲取相應的空間。LEN在上邊通過宏定義,定義為這個student這個結構的大小p1=p2=(struct student *)malloc(LEN);//先定義要給他賦值,我們是動態創建鏈表,我們在上邊聲明的都是指針結構,都是指針,指針結構的話他只是開辟了一個四個字節,放一個指針地址//LEN是student結構的大小。3//讓p1,p2同時指向我們剛申請的這個大小printf("Please enter the num:");scanf("%d",&p1->num);printf("Please enter the score:");scanf("%f",&p1->score);//4接著我們輸入,輸入都是指向的p1,p1首當其沖head=NULL;//head指向NULLn=0;//0個節點,還沒有任何結點//5開始初始化while(p1->num)//6實現循環,當p1的num不等于0的時候,就是沒有退出(while(0!=p1->num)){n++;//7不等于0就n++,表示出現了第一個結點(因為當p1的num成員為0時,就標志著我們整個程序結束,鏈表創建完成)if(1==n)//{head=p1;//8當n=1時head必須指向它p1,我們整個數據鏈的頭結點指向它}else{p2->next=p1;}p2=p1//p2是跟在p1后邊的p1=(struct student *)malloc(LEN);//9然后接著p1再次創建一個新的結點printf("\nPlease enter the num:");scanf("%d",&p1->num);printf("\nPlease enter the score:");scanf("%f",&p1->score); //10這里要求輸入第二個結點//只要輸入的值不為0他就在while循環里邊慢慢的循環,依次循環,當他在while處判斷輸入的值為0,到11}p2->next=NULL;//p2->next就指向NULL,我們剛申請的9到11之間的這個地方就廢掉了,他就停留在內存的某個地方return head;//然后我們返回的值是head,head是我們整個鏈表的一個頭結點的一個指針(也就是整個鏈表的領頭羊,相當于我們數組的首地址,我們知道了這個數組的首地址,我們知道了數組依次索引,鏈表是同樣的。)(我們之所以發明鏈表這個數據結構主要就是彌補數組的不足)//return返回的是指向我們創建完的鏈表的頭
}
void print(struct student *head)//11定義接收頭
{struct student *p;//12我們又定義了p指針,printf("\nThere are %d records!\n\n",n);p=head;//將頭先指向了pif(head)//13如果head不等于0的話(if(NULL!=head))(什么都沒有也就是一個空指針因為head是一個指針嘛){do//14然后用while的形式把p輸出出來{ printf("學號為%d的成績是:%f\n",p->num,p->score);p=p->next;}while(p);//這里也是P不等于NULL的時候(while(NULL!=p))他就實現循環}}
結果展示:
-----對鏈表的刪除操作:
比如下邊鏈表想刪除B。
(A->B->C->D->E->H)
其實很簡單直接把它去掉,把A的指向,指向C就可以了。
刪除B之后還想再把B加回來的話,就直接把A和C之間的鏈斷掉,把B加進來就好了(把A指向B,B又指向C就好了)。
(如果是一個數組ABCDE,想要把B刪去怎么辦?直接刪去B,然后C走到B的位置,D走到C的位置,E走到D的位置;那如果想把B再插入進來的話,那就得E再跑回原來的位置,D再走回來,C再走回來,之后把B放進去。所以這就是數組相當于鏈表不方便的地方)
從一個動態鏈表中刪去一個結點,并不是真正從內存中把它抹掉,(鏈表我們要鏈向下一個只是用地址來鏈,因為鏈表的next指針指向的是一個結點的指針,我們只需要把那個指針改變一下,那我們感覺他是消失了(那消失的那個結點去哪里了就像宇航員在太空中拋棄一樣,遲早會被隕石給撞到的,那他也一樣,在內存中的某個地方遲早會被數據給覆蓋掉不用管他,并不是真正把他從內存中抹掉,而是))而是把它從鏈表中分離開來,只要撤銷原來的鏈接關系即可。
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的C语言:随笔8--结构体的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言:随笔7--预处理命令
- 下一篇: C语言:随笔9--链表