[C]第七章--結構體
阿新 • • 發佈:2018-12-22
結構體
什麼是結構體?
- 當單獨的幾個資料型別不足以表達使用者需求時,這時候就需要構建比較複雜的結構體變數.
- 結構體(struct)指的是一種資料結構,是C語言中聚合資料型別的一類。結構體可以被宣告為變數、指標或陣列等,用以實現較複雜的資料結構。
結構體同時也是一些元素的集合,這些元素稱為結構體的成員(member),且這些成員可以為不同的型別,成員一般用名字訪問。 - 結構,是一些值的集合,這些值稱為成員變數,結構的每個成員可以是不同的型別.
結構的宣告
struct tag{
member-list;
}variable-list;
struct
是自定製的型別,用這個型別來抽象資訊.
什麼又是抽象?
- 是在具體事物的基礎上,提取出一些需要的核心資訊,再表達出來
- 抽象相對於具體,少了很多"資訊"
這裡定義好了結構體變數Student
,但是在實際使用中都是以struct Student xx
的 形式出現,十分麻煩,所以我們通常在定義時加上關鍵詞typedef
:
例如描述一個學生,他有名字,年齡,性別,學號:
typedef struct Student{
char name[20]; //名字
int age; //年齡
char sex[5]; //性別
char id[20]; //學號
}Stu;
//注意 這裡最後的分號不能省略
通過typedef
關鍵詞的使用,之後結構體變數的使用就簡化為Student xx
最後一行的Stu
,與typedef
相結合就可以理解為重新命名,所以之後就可以使用Stu xx
,並且將變數擴充套件為全域性變數.
同樣這一全域性變數的設定也可以單獨語句實現:
typedef struct Student Stu;
-
全域性變數的設定會少寫很多
struct
結構的成員可以是標量,陣列,指標,甚至是其他結構體.
結構體變數的定義和初始化
定義
struct Point{
int x;
int y;
}p1; //宣告型別的同時定義變數p1
struct Point p2; //定義結構體變數p2
初始化
struct Point p3 = { x , y };
struct Stu{ //型別宣告
char name[15];
int age;
};
struct Stu s = {"zhangsan" , 20}; //初始化
struct Node{
int data;
struct Point p;
struct Node* next;
}n1 = {10,{4,5},NULL}; //結構體巢狀初始化
結構體成員的訪問
.
操作符: 訪問結構體成員變數->
操作符: 結構體指標訪問指向變數的成員
struct S s;
strcpy(s.name , "zhangsan"); //使用 . 訪問name成員
s.age = 20; //使用 . 訪問age成員
struct Stu{
char name[20];
int age;
};
void print(struct Stu* ps){
printf("name = %s age = %d\n",(*ps).name,(*ps).age);
printf("name = %s age = %d\n",ps->name,ps->age);
}
int main(){
struct Stu s = {"zhangsan",20};
print(&s); //結構體地址傳參
return 0;
}
從二者相同的結果中分析我們可以得到結論:
a -> b
等效於(*a).b
結構體傳參
struct S{
int data[1000];
int num;
};
struct S s = {{1,2,3,4},1000};
//結構體傳參
void print1(struct S s){
printf("%d\n",s.num);
}
//結構體地址傳參
void print2(struct S* ps){
printf("%d\n",ps->num);
}
int main(){
print1(s); //傳結構體
print2(&s); //傳地址
return 0;
}
我們可以想想上面程式碼中print1
和print2
那個函式更好?
答案是: print2
函式
原因比較複雜,簡單總結為一下一段話:
講解函式棧幀的時候,我們講過函式傳參的時候,引數是需要壓棧的.如果傳遞一個結構體物件的時候,結構體過大,引數壓棧的系統開銷比較大,所以會導致效能的下降.
- 結論:
結構體傳參的時候,推薦傳遞結構體地址,這樣可以降低開銷.
結構體的巢狀
typedef struct School{
Student students[1000]; //之前定義過的結構體變數
int size;
}School;
- 這樣的話不能直接巢狀自身這個結構體,如果嵌套了執行結果為
使用了未定義的結構體
. - 而且也無法對這個結構體函式的變數進行
sizeof
操作,因為不知道結構體的大小(記憶體佔存大小)
如果想要巢狀,可以用指標來實現:
typedef struct Student{
char name[1024];
int score;
struct Student* s; //指標 ,嵌套了自身結構體
}student;
- 這樣的話,如果執行
sizeof
這個結構體函式定義的變數,就會有明確的佔存大小
例如上面這個程式:
sizeof的結果為: 1024 + 4 + 4 = 1032;
巢狀也可能很複雜,多個結構體變數可以互相傳參,這就會產生佔存動輒上G的結構體,所以謹慎使用巢狀.