結構體筆記(結構體嵌套、自引用,結構體指針)
結構體(struct)
1、基本概念
結構體-----將不同類型的數據成員組織到統一的名字之下,適用於對關係緊密,邏輯相關、具有相同或不同類型的數據進行處理
2、結構體定義格式
定義結構
為了定義結構,您必須使用 struct 語句。struct 語句定義了一個包含多個成員的新的數據類型,struct 語句的格式如下:
struct 標籤名
{
類型 變量名;
類型 變量名;
······
} 結構變量 (結構體名字);
struct tag
{member_list member_list member_list ...
} variable_list ;
tag 是結構體標籤。
member-list 是標準的變量定義,比如 int i; 或者 float f,或者其他有效的變量定義。
variable-list 結構變量,定義在結構的末尾,最後一個分號之前,您可以指定一個或多個結構變量。下面是聲明student結構的方式:
struct student
{
char name[50]; //名字
int id; //學號
} student1;
(聲明結構體類型僅僅是聲明瞭一個類型,系統並不為之分配內存,就如同系統不會為類型 int 分配內存一樣。只有當使用這個類型定義了變量時,系統才會為變量分配內存。所以在聲明結構體類型的時候,不可以對裏面的變量進行初始化。)
定義了一個tag為student的結構體和一個結構變量student1,如果省略變量名(student1),就變成了對結構的聲明,上述結構體聲明也可分開寫
struct student
{
char name[50]; //名字
int id; //學號
} ;
struct student student1;
與上面效果相同,可理解為struct student類似於int,而我們用的是student1類似於變量,如果省略結構名,則稱之為無名結構,這種情況常常出現在函數內部,或者説你只需要student1這一個變量,
後面不需要再使用結構體名定義其他變量,那麼在定義時也可以不給出結構體名
struct
{
char name[50]; //名字
int id; //學號
} student1;
(在聲明結構體時常常與typedef函數配合使用)
2.
3、結構體變量的初始化
和其它類型變量一樣,對結構體變量可以在定義時指定初始值。
例:
struct student
{
char name[50];
int id;
} charon = {"charon", 666};
int main()
{
printf ( "name : %s\nid: %d\n", charon.name, charon.id );
輸出:
4、結構體成員的訪問
訪問結構體變量的成員必須使用成員選擇運算符(也稱圓點運算符),格式為:結構體變量名.成員名
若使用指針對結構體成員進行訪問,格式為:指針->成員名 等價於 (*指針).成員名
相同點:兩個操作符都是二元操作符,且其有操作符是結構體成員的名稱。
不同點:“ . ”操作符左邊的操作數是一個“結構體”的表達式,而“ -> ”操作符左邊的操作數是一個指向結構體的指針。
例:
typedef struct
{
int num;
float score;
char name[10];
} STUDENT;
STUDENT temp;
STUDENT * p = &temp;
在這裏temp.score代表的是結構體temp裏的成員score。
p->score代表指向temp結構體成員score的指針。
為了使用方便和直觀,C語言允許把(*temp).score用p->score來替換。
也就是p->score等價與(*temp).score。
所以在結構體中“ . ”和“ -> ”的用法相似,但是並不等價。
5、typedef函數
為一種數據類型定義一個新名字。這裏的數據類型包括內部數據類型(int,char等)和自定義的數據類型(struct等),
(注意與#define的區別,typedef 是用來定義一種類型的新別名的,它不同於宏#define,宏是簡單的字符串替換)
例:
為int定義了一個新的名字INTEGER,也就是説INTEGER與int是同義詞,也可以為結構體定義一個別名
typedef struct student STUDENT;
或者
typedef struct student
{
int num;
} STUDENT;
上述兩條語句是等價的,二者都是為struct student結構體類型定義了一個新的名字STUDENT,即STUDENT與struct student是同義詞,所以下列兩條語句等價
STUDENT stu1,stu2;
struct student stu1, stu2;
6、結構體嵌套
就是在一個結構體內包含了另一個結構體作為其成員
相互引用
使用typedef
typedef struct date
{
int year;
int month;
int day;
} DATE;
typedef struct student
{
long studentID;
char studentName[10];
char studentSex;
DATE birthday;
int score[4];
} STUDENT;
STUDENT charon;
上面代碼中,定義了結構體變量birthday和charon,並給struct date和struct student分別取別名為DATE和STUDENT,
當出現結構體嵌套時,必須以級聯方式訪問結構體成員,即通過成員選擇運算符逐級找到最底層的成員時再引用
charon.birthday.day = 10;
printf ( "%d", charon.birthday.day );
不使用typedef
上面是使用了typedef的結構體嵌套,也可以不使用,代碼如下
struct STUDENT
{
long studentID;
char studentName[10];
char studentSex;
int score[4];
struct DATE
{
int year;
int month;
int day;
} birthday;
} student;
定義了結構體變量student和birthday,引用成員的的方法和上面類似
student.birthday.day = 100;
printf("%d", student.birthday.day);
C語言允許對具有相同結構體類型的變量進行整體賦值,注意:對字符數組型結構體成員進行賦值時一定要使用strcpy()
strcpy(stu1.studentName, “張三”);
而不能寫成
stu2.studentName = stu1.studentName
因為結構體成員studentName是一個字符型數組,studentName是該數組的名字,代表字符型數組的首地址,是一個常量,不能作為賦值表達式的左值。
自引用(self reference)(重點)
使用typedef 時
錯誤的方式:
typedef struct tag{
int value;
charon *link; /* 雖然也使用指針,但這裏的問題是:charon尚未被定義 */
} charon;
這裏的目的是使用typedef為結構體創建一個別名charon。但是這裏是錯誤的,因為類型名的作用域是從語句的結尾開始,而在結構體內部是不能使用的,因為還沒定義。
正確的方式:有三種,差別不大,使用哪種都可以。
當你使用typedef去代替整個結構體並且以原結構體名代替的時候,如果此時你內部還自嵌套了,那麼內部的成員變量之前一定要加上struct。
/* 方法一 */
typedef struct tag_1{
int value;
struct tag_1 *link;
} charon;
/* 方法二 */
struct tag_2; //結構體的不完全聲明
typedef struct tag_2 charon;
struct tag_2{
int value;
charon *link;
};
/* 方法三 */
struct tag_3{
int value;
struct tag *link;
};
typedef struct tag_3 charon;
不使用typedef時
錯誤的方式:
struct tag_1
{
struct tag_1 A; /* 結構體 */
int value;
};
這種聲明是錯誤的,因為這種聲明實際上是一個無限循環,成員b是一個結構體,b的內部還會有成員是結構體,依次下去,無線循環。在分配內存的時候,由於無限嵌套,也無法確定這個結構體的長度,所以這種方式是非法的。
正確的方式: (使用指針):
struct tag_1{
struct tag_1 *A; /* 指針 */
int value;
};
由於指針的長度是確定的(在64位機器上指針長度為8),所以編譯器能夠確定該結構體的長度。
可以用sizeof查看:
如何理解結構中包含一個指向結構本身的指針?
至於指向自己的問題,結構體只是個數據類型,不是一個變量。
只有在聲明一個結構體變量的時候才會在內存中分配一段空間,指針中存放的正是變量在內存中的地址。所以不是指針指向了結構體自己,而是指針指向了結構體變量。
C語言中的類型有完整和不完整之分,只有完整類型的變量才能夠使用。完整代表已確定了類型的大小,底層只需要大小屬性,而不需要類型屬性。
指針類型都是完整的,因為指針類型的大小都是確定的。struct tag * 是指針類型,而不是結構類型。結構的成員類型都是完整的,那麼結構類型就是完整的。
程序員可以使用強制類型轉換來將一段內存轉換為需要的數據類型,例如下面有一個數組a,現在將其強制轉換為一個結構體類型stu:
#include <stdio.h>
typedef struct student
{
int id;
char name[50];
} stu;
int a[10]={1,2,3,4,5};
int main()
{
stu *student1;
student1 =(student*)a;
printf("student1->name=%d\n",student1->name);
printf("student1->id=%d\n",student1->id);
return 0;
}
可以看到a[10]被強制轉換為student結構體類型,當然不使用強制類型轉換也是可以的,只是編譯器會報警報。
“一個結構體裏包含一個指針,這個指針指向的數據類型是這個結構體的數據類型”。
例子:
typedef struct Node
{
int data;
Node* next;
} node;
int main(){
node node1;
node node2={666};
node node3;
node1.next = &node2;
node2.next = &node3;
node3.next = NULL;
cout<<&node2<<endl;
cout<<node1.next<<endl;
cout<<node2.data<<endl;
cout<<node1.next->data<<endl;
return 0;
}
ge](![assets/image-20221125140606-4tpsamp.png]
參考鏈接
結構體(結構體嵌套、結構體指針、結構體參數傳遞) - 藍海人 - 博客園 (cnblogs.com)
如何理解結構中包含一個指向結構本身的指針? - 知乎 (zhihu.com)
詳解C語言中結構體的自引用和相互引用_C 語言_腳本之家 (jb51.net)
C 結構體 | 菜鳥教程 (runoob.com)
<img alt="知識共享許可協議" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" />
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。
<img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" />
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
使用者可以對本創作進行轉載、節選、混編、二次創作,但不得運用於商業目的,且使用時須進行署名,採用本創作的內容必須同樣採用本協議進行授權。