1. 程式人生 > >[C]第七章--結構體

[C]第七章--結構體

結構體

什麼是結構體?

  • 當單獨的幾個資料型別不足以表達使用者需求時,這時候就需要構建比較複雜的結構體變數.
  • 結構體(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;
}

我們可以想想上面程式碼中print1print2那個函式更好?
答案是: 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的結構體,所以謹慎使用巢狀.