1. 程式人生 > >結構體基礎知識總結以及在高階資料結構中的寫法

結構體基礎知識總結以及在高階資料結構中的寫法

在寫了幾種資料結構之後覺得結構體非常重要,但自己掌握得並不好,需要一點小總結。

以下基礎知識大多來自網站菜鳥教程:
http://www.runoob.com/cprogramming/c-structures.html

格式:

struct tag {                      ----------結構體名字
    member-list                ----------成員列表
    member-list 
    member-list  
    ...
} variable-list ;            ----------
變數列表

例如:

1.
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book;

幾種形式:
原則:結構體名、成員名、變數名,三者至少出現其二

沒有結構體名、聲明瞭一個變數s1
2.
struct 
{
    int a;
    char b;
    double c;
} s1;
 
3.
struct SIMPLE
{
    int a;
    char b;
    double c;
};
有結構體的名字了就可以用它來宣告變量了   
這種情況要在前面帶上struct
struct SIMPLE t1, t2[20], *t3;
4.
typedef是用來給型別取一個新的名字的,使用之後新名字和原來的名字有一樣的作用,宣告出來的變數和原來的型別一致
注意這種形式非常常見
這種和上面那個差不多,但是有了一個typedef,上面沒有結構體名的時候是沒辦法再去宣告變數的,但現在可以了
typedef struct
{
    int a;
    char b;
    double c; 
} Simple2;
現在可以用Simple2作為型別宣告新的結構體變數:Simple2 u1, u2[20], *u3;

注意,Simple2的位置如果沒有typdef的時候只是一個變數,有了typdef了就可以用他來宣告結構體變量了,相當於把Simple2 作為一個結構體的名字了。

這種與上面的同理,也更為常見
typedef
struct s { int a; char b; double c; } Simple2;
此結構體的宣告包含了指向自己型別的指標
struct NODE
{
    char string[100];
    struct NODE *next_node;
};
如果兩個結構體**互相包含**,則需要對其中一個結構體進行不完整宣告,如下所示:

struct B;    //對結構體B進行不完整宣告
 
//結構體A中包含指向結構體B的指標
struct A
{
    struct B *partner;
    //other members;
};

結構體指標
struct Books *struct_pointer;
指標變數中儲存結構變數的地址:
struct_pointer = &Book1;
訪問結構體變數
struct_pointer->title;


例項:注意是函式的引數列表是結構體指標時,呼叫函式的時候是對變數取地址(變數的地址也就是指標存的值)struct Books Book1;        /* 宣告 Book1,型別為 Books */

printBook( &Book1 );

void printBook( struct Books *book )

--------------------------------------------------------------------------------------------------------------------分割線,以下是個人感想
結構體的基礎知識看起來並不多,總結一下也比較清晰,但是寫了幾種資料結構之後,會發現也許並不是那麼回事…
甚至在宣告結構體的時候就出現了問題導致程式到後來各種型別不匹配根本執行不了。
以下從寫過的幾種資料結構經驗角度寫一點總結:
注意:下面的結構體並不絕對,只是個人選擇問題,但可以放在一起總結。
(不知道為什麼不用程式碼塊寫的星號*有時候顯示不出來…可能因為我程式碼是複製貼過來的?有時候還莫名其妙的斜體,講究看一下)

1.這是在寫二項堆的時候的結構體

typedef struct _BNode *BinomialNode;
typedef struct _BNode                              //結點的結構體
{
	Type key;
	int degree;
    BinomialNode child;
    BinomialNode parent;
    BinomialNode sibling;
}Node;

typedef struct Heap *BinomialHeap;
typedef struct Heap                                   //每個二項堆的堆頂連成一個連結串列
{
	BinomialNode head;
}Binheap;

高階資料結構上,結構體一般都要宣告一個結構體變數和一個結構體指標,還有一個特點是結構體的成員需要宣告成該結構體型別的指標變數(比如結構體是一個node,成員一般都是一些child,parent等,他們本身就是node).
一種情況就是這樣,先把指標變數給宣告出來,typedef struct _BNode *BinomialNode; 之後再成員定義指標變數的時候就直接用這個指標變數定義BinomialNode child;(沒有星號)

以下為對應的分配空間以及引用: BinomialNode x = (BinomialNode)malloc(sizeof(Node));

先講一下我們的malloc函式, 專門用來分配空間的,對應標頭檔案<stdlib.h>或者<malloc.h>.
函式原型extern void星malloc(unsigned int num_bytes);
void星 表示未確定型別的指標,void星 可以指向任何型別的資料,申請記憶體空間時還不知道使用者是用這段空間來儲存什麼型別的資料 例如: char星a = NULL;//宣告一個char型別的指標 a = (char)malloc(100sizeof(char));//使用malloc分配記憶體的首地址,然後賦值給a (這個形式就和上面那個BinomialNode很對應了,請對起來看。malloc前面其實是一種強制轉化(因為本身是void未定型別),轉化成(char*)型別,也就是a的型別,malloc後面是他的真正的引數,分配100個單位char大小的空間)

寫資料結構的時候我們也發現,有時候一個節點要用到malloc,有時候又不用,大概是這麼個意思:

沒有用到malloc的指標是沒有指向有效的記憶體地址的,是不能對這樣的指標指向的地址進行賦值的。如果用了malloc,就可以對它指向的地址裡面賦值或者其他操作了。也就是隻需要一個指標做指向性的操作就不需要給他malloc,如果要進行值的操作就需要malloc.

對結構體的引用,還有引數列表也需要單獨提出來。
寫成上面那種形式看起來就很方便很好理解了,引數列表一般都是這種情況:
void DELETE(BinomialHeap H, BinomialNode x)
直接用指標引用堆頂元素的連結串列或者用指標引用結點,後續基本不怎麼會用到取值取地址等操作。

2.這是在寫紅黑樹的時候寫的結構體(想起被紅黑樹支配三天的恐懼)

typedef struct rbtree     //定義紅黑樹的結構
{
	Color color;
	type key;
	struct rbtree *left;
	struct rbtree *right;
	struct rbtree *parent;

}node, *tree;

同樣的慣例,宣告一個結構體變數和一個結構體指標。
不同的是成員是用struct rbtree *left;來宣告的,如果沒有像上面一樣,先用typedef struct _BNode *BinomialNode;是不能再結構體成員宣告的時候用到這個指標的,於是就直接用了結構體的最原始的名字。宣告指標就加星號了。
下面的malloc和引數:
p = (node星 )malloc(sizeof(node));
創造結點要用node星去強制轉換。
node星 create(type key, node *left, node *right, node *parent) //建立結點
void left_rotate(tree &t, node *x)
呼叫結點需要每次都加上星號,呼叫樹要用他的地址。

3.B樹用到的結構體


typedef struct TreeNode *PtrBTNode;      //定義一個結點和一個指向結點的指標
typedef struct TreeNode BTNode;

typedef struct TreeNode {                        //結點的結構體,包含位置,判斷是否為葉子結點,關鍵字,孩子結點的指標
	int Num;                                      //Num是當前結點的關鍵字的個數
	bool IsLeaf;
	PtrElementType Key;
	PtrBTNode *Child;    
};          


typedef struct Tree *PtrBT;     //定義指向樹根的指標
typedef struct Tree {             
	PtrBTNode Root;           
};  

這個B樹的宣告和二項堆類似,也是先就把指標變數typedef了,然後就可以在結構體裡面用了。
malloc和引數也類似
PtrBTNode NewNode = (PtrBTNode)malloc(sizeof(BTNode)); //新建一個結點,分配空間
void BTDelete(PtrBT T, PtrBTNode CurrentNode, ElementType Val)
值得注意的是,child用了PtrBTNode *Child; 是和二項堆不一樣的地方。
其實二項堆的child指的是父節點的最左邊的孩子,B樹的child就是所有的child有很多個,我們是用一個數組來存放這些child的結點的。宣告的時候用了一個二級指標,指標child裡面存放的地址是另一個指向BTNode型別的指標。陣列和指標的關係,*child就是陣列的首地址,指標移動陣列也能移動,就相當於有一個指向陣列的指標了,跟前面的情況就相似了。
就像這樣:
for (j = 0; j < MinDegree; j++) { //z的子女就等於FullNode的後一半的子女
z->Child[j] = FullNode->Child[MinDegree + j];
}

就寫這麼多吧