結構體基礎
聚合類型——能夠同時存儲超過一個單獨的數據,C語言提供兩種聚合數據類型數組和結構。
數組和結構的區別:
- 數組是相同類型的數據的聚合,結構是不同類型數據的聚合;
- 因為數組的元素長度相同可以采用下標方式訪問,結構成員的長度不同,只能通過名字訪問,不能通過下標來訪問。
- 數組名在表達式中使用時被當做一個指針,而結構體變量在表達式中使用時不能被當做一個指針。
我們可以聲明指向結構的指針,取一個結構變量的地址以及聲明結構數組。
結構體變量的聲明
struct tag
{
number_list;
} variable_list;
其中tag可以省略,但這樣的話只能聲明一個結構體變量,tag用來聲明多個相同的結構體變量。
標簽允許多個聲明使用同一個成員列表,并且創建同一種類型的結構,標簽只標識了一種模式,用于聲明未來標變量。
struct stInfo
{
int age ;
char name[];
int classNo;
}
/* 這個聲明只是把stInfo和這個成員列表關聯起來,但是沒有創建任何結構變量*/
struct stInfo info; //聲明結構體變量info
struct stInfo *info1; //聲明指向結構體變量的指針
struct stInfo inf2[]; //聲明指向結構體的數組
/*info info1 info2都是同一種類型的結構體*/
聲明結構可以使用另外一種良好的技巧是用type創建一種新的類型如下面的例子所示:
type struct
{
int a;
char b;
float c;
} stSimple; //注意最后是有分號(;)的
/*stSimple是一個類型名,而不是一個變量名*/
stSimple x;
stSimple y[];
stSimple *z;
結構體成員
結構體里面可以聲明任何類型的變量作為結構體的成員,像結構,體聯合體等;
struct COMPLEX
{
float f;
int a [20];
long *lp;
struct stSimple s;
struct stSimple sa[20];
struct stSimple *sp;
}
一個結構體的成員名字和其他結構的成員名字相同,所以這個結構的成員a并不會與struct stSimple s的成員a沖突。
結構體成員變量的直接訪問
結構體變量的成員是通過點操作符(.),點操作符接受兩個操作數,左操作數是結構體變量的名字,右操作數是結構體變量的成員,點操作符是自左向右*的結合性。
struct COMPLEX comp;
comp.f; //訪問成員f
comp.s.a //訪問嵌套結構體成員a
comp.a[4] //訪問結構體數組成員
comp sa[4].c //訪問結構體數組成員
結構體成員的間接訪問
當擁有一個指向結構體變量的指針,當使用這個指針訪問結構體成員時就是結構體成員的間接訪問。
??結構體成員的間接訪問使用箭頭操作符(——>),該操作符的左操作數是一個指向結構體的指針,右操作數是結構體成員。
struct COMPLEX *compp;
compp->f;
compp->a[1];
compp->s.a;
compp->sa[4].c;
compp->sp->b;
結構體變量的自引用
先看一個結構體違法自引用的例子:
struct SELF_RIF1
{
int a;
struct SELF_RIF1 b; //嵌套一個結構體
int c;
}
這種引用是違法的,因為成員b是另一個完整的結構體,其內部還包括自己的成員b,如此往復循環無窮盡。
??再看合法的結構體自引用
struct SELF_RIF1
{
int a;
struct SELF_RIF1 *b; //嵌套一個結構體
int c;
}
這個 聲明和前面那個的區別在于b現在是一個指針而不是一個結構。編譯器在結構的長度確定之前就已經知道指針的長度,所以這種類型的自引用是合法的。
事實上一個結構內部包含一個指向該結構本身的指針,它事實上所指向的是同一種類型的不同結構,鏈表和樹都是用這種技巧實現的,每個結構指向鏈表的下一個元素或樹的下一個分支。
結構的初始化
結構的初始化方式和數組的初始化很類似——一個位于一對花括號內部,由逗號分隔的初始列表可用于結構各個成員的初始化,這些值根據結構成員的順序寫出。如果初始列表的值不夠,剩余的結構成員將使用缺省值進行初始化。
??結構中如果包含數組和結構成員,其初始化方式類似于多維數組的初始化。
struct INIT_EX
{
int a;
short b[10];
Simple c;
} x = {
10,
{1,2,3,4,5},
{ 25,'x',1.9}
};
作為函數參數的結構
結構是一個標量,它可以用于其它標量可以使用的任何場合,因此把結構作為一個參數傳遞給一個函數是合法的。
給函數傳遞指向結構的指針的效率要遠大于向函數傳遞整個結構。
typedef struct
{
char product[PRODUCT_SIZE];
int quantity;
float unit_price;
float total_amount;
} Transaction;
/函數參數為整個結構的版本/
void print_receipt(Transaction trans)
{
...
}
/函數參數為指向結構的指針版本/
void print_receipt(Transaction *trans)
{
...
}
/分別定義結構和指向結構的指針并調用函數/
Transaction current_trans;
print_receipt(current_trans); //調用函數參數為整個結構的版本
print_receipt(¤t_trans); //調用函數參數為指向結構指針的版本
向函數傳遞指向結構的指針和向函數傳遞整個結構相比效率會很高,且結構越大這種優勢越明顯。
聯合
聯合和結構相似,但它的行為和結構不一樣。聯合的所有的成員引用的是內存中的相同位置。
union
{
float f;
int i;
} fi;
在一個浮點型和整型都是32位的機器上,變量fi只占用內存中一個32位的字,如果成員f被使用,這個字就被作為浮點值訪問,如果成員i被使用,這個字就被當作整型值訪問。
聯合在某一時刻只能有一個成員被訪問,如果聯合成員的長度不一樣,聯合的長度就是它最長成員的長度,若長度懸殊特別大,可以在聯合中存儲指向不同成員的指針而不是直接存儲成員本身,所有指針的長度都相同。
聯合的初始化
聯合變量可以被初始化,但這個值必須是聯合的第一個成員的類型,而且它必須位于一堆花括號里面,例如:
union
{
int a;
float f;
char c[4];
} x = {5};
我們不能把這個類變量初始化為一個浮點數或字符值,如果給出的初始值是任何其它類型,它就會轉換為一個整型值并賦值給x.a;