我們可以使用C的結構體來表示數據結構元素,比如鏈表或樹的節點,指針是把這些元素聯繫到一起的紐帶 ...
指針與結構體
簡介:我們可以使用C的結構體來表示數據結構元素,比如鏈表或樹的節點,指針是把這些元素聯繫到一起的紐帶。
typedef struct _person{ char* firstName; char* lastName; char* title; unsigned int age; } Person;
/*用點表示法初始化*/
Person person;
person.firstName=(char*)malloc(strlen("Emily")+1);
stcpy(person.firstName,"Emily");
person.age=23;
/*****結構體指針初始化*/
Person *ptrperson;
ptrperson=(Person*)malloc(sizeof(Person));
ptrperson->firstName=(char*)malloc(strlen("Emily")+1);
strcpy(ptrperson->firstName,"Emily");
ptrperson->age=23; // (*ptrperson).age = 23;
為結構體分配記憶體,分配的記憶體大小至少是各個欄位的長度之和。不過,實際長度會大於這個和,結構體的各欄位之間可能會有填充。結構體數組各元素之間會有填充。
結構體釋放問題:
用結構體變數和指向結構體的指針函數參數
1.用結構體變數的成員作參數。(用法和普通變數相同)
2.用結構體變數作實參。形參也必須是同類型的結構體變數。調用期間形參也要占用記憶體。(空間和時間上開銷較大),較少使用該方法。
3.用指向結構體變數(或數組)的指針作實參,將結構體變數(數組)的地址傳給形參。
用指針處理鏈表
鏈表是一種動態地進行存儲分配的一種數據結構。 鏈表是C語言編程中常用的數據結構,比如我們要建一個整數鏈表,一般可能這麼定義:
struct int_node {
int val;
struct int_node *next;
};
假設有五個學生某一門功課的成績分別為A、B、C、D和E,這五個數據在記憶體中的存儲單元地址分別為1248、1488、1366、1022和1520,其鏈表結構如圖所示。
鏈表有一個“頭指針”變數,圖中以 head表示,它存放一個地址,該地址指向鏈表中第一個結點,第一個結點又指向第二個結點……直到最後一個結點。該結點不再指向其他結點,它稱為“表尾”,它的地址部分存放一個“NULL”(表示“空地址”),鏈表到此結束。鏈表中每個結點都包括兩個部分:用戶需要用的實際數據和下一個結點的地址。
鏈表每個結點中只有一個指向後繼結點的指針,該鏈表稱為單鏈表。其實結點中可以有不止一個用於鏈接其他結點的指針。如果每個結點中有兩個用於鏈接其他結點的指針,一個指向前趨結點(稱前趨指針),另一個指向後繼結點(稱後繼指針),則構成雙向鏈表。雙向鏈表如圖所示。
/*單鏈表的例子(靜態鏈表:因為所有節點在程式中定義的,不是臨時開闢的,也不能用完後釋放)*/ #include<stdio.h> //#define NULL 0 struct student { int num; int score; struct student *Next; }; int main() { struct student a,b,*head,*p; a.num=10012; a.score=99; b.num=10013; b.score=81; head=&a; a.Next=&b; b.Next=NULL; p=head; do { printf("%5d %ld\n",p->num,p->score); p=p->Next; }while(p!=NULL); }
問題:結構體的變數名可以當做地址賦給指針嗎?沒有頭指針head行不行?p起了什麼作用?沒它可以嗎?
處理動態鏈表用到的函數 calloc/malloc/free
malloc函數原型: void *malloc(unsigned int size);其作用是在記憶體的動態存儲區分配一個長度為size的連續空間。此函數的值(返回值)是一個分配域的起始地址(void類型);若函數未成功執行,則返回空指針(NULL)。
calloc函數原型: void *calloc(unsigned n,unsigned size);其作用是在記憶體的動態存儲區分配n個長度為size的連續空間。函數返回一個指向分配域起始位置的指針;否則,返回NULL。
free 函數原型: void free(void *p);釋放由p指向的動態存儲區。free無返回值。
建立動態鏈表(知難而進)
/*寫一函數建立一個有3名學生數據的單向動態鏈表*/ #include<stdio.h> #include<malloc.h> //#define NULL 0 #define LEN sizeof(struct student) struct student { long num; float score; struct student *next; }; int n; struct student *creat(void) { struct student *head; struct student *p1,*p2; n=0; p1=p2=(struct student*)malloc(LEN); scanf("%ld,%f",&p1->num,&p1->score); head=NULL; while(p1->num!=0) { n=n+1; if(n==1) head=p1; else p2->next=p1; p2=p1; p1=(struct student*)malloc(LEN); scanf("%ld,%f",&p1->num,&p1->score); } p2->next=NULL; return(head); } int main() { creat(); }
建立鏈表首先要定義一個包含數據域和指針域的的結構類型,然後建立指向表頭節點的頭指針head,最後通過malloc函數動態申請一塊記憶體作為表頭節點。
typedef struct node { int data; /*信息*/ struct node *link; /*指針*/ }NODE; /*定義節點*/ NODE *head; /*定義頭指針head */
定義結構類型和頭節點之後,我們要建立不包含數據的表頭節點,可以按下列語句進行操作。
NODE *p; /*說明一個指向節點的指針變數p */ p=(NODE*) malloc(sizeof(NODE)); /*申請表頭節點*/ p->link = NULL; /*將表頭節點的link置為NULL */ head=p; /*head指向表頭節點p*/
由於此時鏈表中只有一個表頭節點,沒有數據節點,所以稱為空鏈表。
p=(NODE*) malloc(sizeof(NODE)); /*申請一個數據節點*/ 為了在鏈表中保存數據,可以從表頭位置將數據節點插入到鏈表中,例如,插入一個數據節點:
p=(NODE*) malloc(sizeof(NODE)); /*申請一個數據節點*/ gets(p ->data); /*輸入一個新的數據*/ p->link=head->link; /*建立鏈接關係。將表頭節點的link存入p 的link中*/ head->link=p; /*將數據節點插在表頭節點之後成為第一個數據節點*/
插入第一個數據節點後鏈表,然後繼續插入下一個數據節點。
create(NODE *head,int n) 根據上面的鏈表建立過程,可以寫出函數create建立有n個數據節點的鏈表。
create(NODE *head,int n) { NODE *p; for(; n>0;n--) { p=(NODE*) malloc(sizeof(NODE)); if(p==NULL) exit(0); gets(p->data); p->link = head->link; head->link = p; } }