@TOC


📝前言

本小節,阿森繼續和你一起學習什麼是結構體?結構體類型的聲明和創建,然後就是結構體的初始化,隨即學習結構成員的訪問操作符來更好的打印結構體的數據,當然還有匿名結構體類型,和結構的自引用。文章乾貨滿滿,接下來我們就學習一下結構體吧 😃!


🌠 什麼是結構體?

結構體是一種用户定義的數據類型,它允許用户根據需要組合不同類型的變量。

struct Student 
{
  char name[20];
  int age;
  float score;
};

結構體通過struct關鍵字來定義,它允許將多個不同類型的數據元素組合在一起,這些數據元素稱為結構體的成員。

🌅 結構體類型的聲明和創建

  1. 結構體類型的聲明
struct 結構體類型標籤名 
{
  成員聲明1;
  成員聲明2;
  ...
};//分號不能丟

例如:

struct Student//此時只是聲明瞭 Student 類型,
{				//但還沒有創建任何 Student 類型的變量。
  int id;
  char name[20];
  float score;
};//分號不能丟
  1. 創建結構體類型的變量的語法如下:
struct 結構體類型名 變量名;

例如:

創建一個名為student1的學生結構體變量:
struct Student student1;
也可以在聲明結構體類型的同時創建變量:
struct Stu
{
	char name[20];//名字
	int age;//年齡
	char sex[5];//性別
	char id[20];//學號
};
struct Stu s1;
struct Stu s2;

3.typedef關鍵字來為結構體類型定義別名,然後創建結構體變量:

// 首先定義一個結構體類型
struct Student 
{
  int id;
  char name[20];
};

int main() 
{

  // 使用typedef為Student結構體類型定義一個別名StudentType
  typedef struct Student StudentType;  

  // 使用原結構體類型定義變量
  struct Student st1;

  // 使用新的類型別名定義變量 
  StudentType st2;

  // 訪問結構體成員
  st1.id = 1001;
  st2.id = 1002;

  return 0;
}

typedef struct Student StudentType可以理解使用typedef把 struct Student重新取了一個名字 StudentType ,此時這個 StudentTye 就是一個類型,比如:int a;這個StudentTye就相當於int, StudentType st2 ;當然,這個 struct Student 也可以理解為 int ,所以也可以這麼用 struct Student st1 ;

自定義類型:結構體深入解析_操作符

🌠 結構體變量的初始化

結構體變量的初始化主要有兩種方式:

  1. 按照默認順序初始化:

默認情況下,結構體成員的初始化順序與它們在結構體定義中的順序相同。

例如:

struct Stu
{
	char name[20];//名字
	int age;//年齡
	char sex[5];//性別
	char id[20];//學號
}p1;
struct Stu s1 = { "asenyaozixin",11,"男","2023012018" };
//定義結構體變量s2
  1. 指定順序初始化:

可以通過在列表中指定成員名來指定成員的初始化順序:

自定義類型:結構體深入解析_操作符_02

例如:

struct Stu
{
	char name[20];//名字
	int age;//年齡
	char sex[5];//性別
	char id[20];//學號
};
struct Stu s2 = { .age = 66,.id = "2023001001",.name = "ahuibuyiban",.sex = "nv" };

完整示例且打印:

struct Stu
{
	char name[20];//名字
	int age;//年齡
	char sex[5];//性別
	char id[20];//學號
};
int main()
{
	struct Stu s1 = { "asenyaozixin",11,"nan","2023012018" };//按照默認順序初始化
	struct Stu s2 = { .age = 66,.id = "2024001001",.name = "ahuibuyiban",.sex = "nv" };//指定順序初始化
	printf("%s %d %s %s\n", s1.name, s1.age, s1.sex, s1.id);
	printf("%s %d %s %s\n", s2.name, s2.age, s2.sex, s2.id);

	return 0;
}

打印結果:

自定義類型:結構體深入解析_操作符_03

🌅 結構成員訪問操作符

結構成員訪問操作符用於訪問結構體中的成員變量。

結構體成員的直接訪問

  1. 結構體成員的直接訪問----點操作符(.) 使⽤⽅式:結構體變量.成員名

使用點操作符可以訪問結構的普通成員,例如:

struct Stu
{
	char name[20];
	int age;
	float score;
} s3 = { "熊大", 33, 66.0f }, s4 = {"熊二", 18, 100.0f};//全局變量

int main()
{
	struct Stu s1 = {"zhangsan", 20, 95.5f};//局部變量
	struct Stu s2 = {"lisi", 18, 87.5f};
	struct Stu s5 = {.score= 98.5f, .name="hehe", .age = 18};

	//. 結構成員訪問操作符
	//結構體變量.成員名
	//
	printf("%s %d %f\n", s1.name, s1.age, s1.score);
	printf("%s %d %f\n", s2.name, s2.age, s2.score);
	printf("%s %d %f\n", s3.name, s3.age, s3.score);
	printf("%s %d %f\n", s4.name, s4.age, s4.score);
}

輸出:

自定義類型:結構體深入解析_初始化_04

  1. 結構體成員的間接訪問----箭頭操作符(->) 使⽤⽅式:結構體指針->成員名

結構體變量聲明為結構體指針時,使用箭頭操作符訪問其成員:

struct Stu
{
	char name[20];
	int age;
	float score;
} s3 = { "熊大", 33, 66.0f }, s4 = {"熊二", 18, 100.0f};//全局變量

int main()
{
	struct Stu s1 = {"zhangsan", 20, 95.5f};//局部變量
	struct Stu s2 = {"lisi", 18, 87.5f};
	struct Stu s5 = {.score= 98.5f, .name="hehe", .age = 18};

	//結構體指針
	struct Stu* p1 = &s1;//取出s1的地址
	struct Stu* p2 = &s2;//取出s2的地址
	struct Stu* p3 = &s3;//取出s3的地址
	struct Stu* p4 = &s4;//取出s4的地址
	struct Stu* p5 = &s5;//取出s5的地址

	printf("%s %d %f\n", p1->name, p1->age, p1->score);
	printf("%s %d %f\n", p2->name, p2->age, p2->score);
	printf("%s %d %f\n", p3->name, p3->age, p3->score);
	printf("%s %d %f\n", p4->name, p4->age, p4->score);
	printf("%s %d %f\n", p5->name, p5->age, p5->score);

	//結構體指針->成員名
	
	return 0;
}

輸出:

自定義類型:結構體深入解析_初始化_05


🌠 匿名結構體類型

匿名結構體類型就是沒有給結構體類型起名字的結構體類型。

匿名結構體的定義方式:

struct 
{
  成員1 數據類型;
  成員2 數據類型;
  ...
} 變量名1, 變量名2, ...;

例如:

struct
{
    int a;
    char b;
    float c;
} x;

匿名結構體的特點是:

  • 不需要給結構體起名字,定義時不指定結構體名稱。
  • 只能在定義它的代碼塊內使用,不能在其他地方再次使用這個匿名結構體類型。

思考:下⾯的兩個結構在聲明的時候省略掉了結構體標籤(tag),然後主函數裏的p = &x的代碼合法嗎?

struct
{
	int a;
	char b;
	float c;
} x;

struct
{
	int a;
	char b;
	float c;
} *p;


int main()
{
	p = &x;//?代碼合法嗎?
	return 0;
}

輸出沒問題但有警告:

自定義類型:結構體深入解析_操作符_06

警告:
編譯器會把上⾯的兩個聲明當成完全不同的兩個類型,所以是⾮法的。
匿名的結構體類型,如果沒有對結構體類型重命名的話,基本上只能使⽤⼀次。

自定義類型:結構體深入解析_結構體指針_07



🌅 結構的⾃引⽤

結構的自引用指的是結構體內部包含自己類型的指針成員,通過這個指針可以實現結構體之間的引用關係。

⾃引⽤⽅式:

struct Node
 {
  int data;
  struct Node *next; 
};

// Node結構體包含一個指向Node結構體的指針next
// 通過next可以實現鏈表節點之間的引用關係

自定義類型:結構體深入解析_初始化_08

  • 思考1: 在結構中包含⼀個類型為該結構本⾝的成員是否可以呢? ⽐如,定義⼀個鏈表的節點:
struct Node
{
 int data;
 struct Node next;
};

上述代碼正確嗎?如果正確,那 sizeof(struct Node) 是多少? 仔細分析,其實是不⾏的,因為⼀個結構體中再包含⼀個同類型的結構體變量,這樣結構體變量的⼤⼩就會⽆窮的⼤,是不合理的。

代碼運行:

自定義類型:結構體深入解析_操作符_09

圖解分析:

自定義類型:結構體深入解析_結構體指針_10

  • 思考2: 在結構體⾃引⽤使⽤的過程中,夾雜了 typedef 對匿名結構體類型重命名,也容易引⼊問題,看看下⾯的代碼,可⾏嗎?
typedef struct 
{
	int data;//存放數據
	Node* next;//存放寫一個節點的地址
}Node;
int main()
{

	return 0;
}

運行:

自定義類型:結構體深入解析_操作符_11

分析: 首先使用typedef給前面匿名結構體起了別名Node還不是類型,但是在typedef語句內,struct定義部分還沒有結束,所以在struct內部使用Node聲明next時,Node類型還未通typedef獲得定義,僅僅是對匿名結構體的一個重命名,就提前使⽤Node類型來創建成員變量。

解決⽅案如下:定義結構體不要使⽤匿名結構體了 如下: 先定義結構體:

struct Node
 {
  int data;
  struct Node* next; 
}

再使用typedef給它起別名:

typedef struct Node Node;

或者一步完成:

typedef struct Node
{
  int data;
  struct Node* next;
} Node;

🚩總結

這次阿森和你一起學習結構體的結構體類型的聲明和創建,初始化,訪問操作符,這是結構體基礎知識,但阿森會慢慢和你一起學習,從基礎到進階。感謝你的收看,如果文章有錯誤,可以指出,我不勝感激,讓我們一起學習交流,如果文章可以給你一個小小幫助,可以給博主點一個小小的贊😘