C語言--結構體總結
阿新 • • 發佈:2022-03-25
結構體
一、概念
-
在實際問題中,一組資料往往有很多種不同的資料型別。例如,登記學生的資訊,可能需要用到
char
型的姓名,int
型或char
型的學號,int
型的年齡,char
型的性別,float
型的成績。又例如,對於記錄一本書,需要char
型的書名,char
型的作者名,float
型的價格。在這些情況下,使用簡單的基本資料型別甚至是陣列都是很困難的。而結構體(類似Pascal中的“記錄”),則可以有效的解決這個問題。 -
結構體本質上還是一種資料型別,但它可以包括若干個“成員”,每個成員的型別可以相同也可以不同,也可以是基本資料型別或者又是一個構造型別。
-
結構體的優點:結構體不僅可以記錄不同型別的資料,而且使得資料結構是“高內聚,低耦合”的,更利於程式的閱讀理解和移植,而且結構體的儲存方式可以提高CPU對記憶體的訪問速度。
二、程式實現
1.結構體的基本使用
建立結構體
-
strcut
識別符號,使用者自定義的名字 - 結構體不一定只能放外面,
main
函式裡面也可以放。(作用域不同) - 定義一個結構體型別,
struct Student
合起來才算是型別名 - 成員變數不能直接賦值
struct Student
{
char name[50];
int age;
int score;
};//有分號
命名結構體變數
- 型別
+
變數 -
struct Student
合起來才是型別名 -
s
是變數
struct Student s;
訪問成員變數
- 如果是普通變數(非指標),使用
.
運算子
stpcpy(s.name,"黑馬"); s.age = 18; s.score = 100;
- 如果是指標變數,使用
->
運算子
printf("%s %d %d\n",(&s)->name,(&s)->age,(&s)->score);//列印結構體中的值
2.結構體的變數
- 普通結構體變數
//普通結構體變數初始化(此方式需要按照結構體定義順序賦值)
struct Student s = {"abc",10,30};
//列印初始化的值
printf("%s, %d, %d",s.name,s.age,s.score);
- 結構體陣列變數
//普通結構體變數初始化 struct Student a[] = { {"heibi", 18, 100}, {"testw", 25, 300}, {"eicis", 49, 200}, }; //列印結構體陣列個數 int n = sizeof(a)/sizeof(a[0]); printf("n:%d",n); //訪問結構體陣列內容(4種方式) for(int i = 0; i<n ; ++i) { printf("%s, %d, %d\n",a[i].name,a[i].age,a[i].score); //printf("%s, %d, %d\n",(*(a+i)).name,(*(a+i)).age,(*(a+i)).score); //printf("%s, %d, %d\n",(&a[i])->name,(&a[i])->age,(&a[i])->score); //printf("%s, %d, %d\n",(a+i)->name,(a+i)->age,(a+i)->score); }
3.結構體的巢狀
-
引例
這裡可以看到兩個結構體得內容相差不大,有三個重複的變數,這種情況即可以使用結構體的巢狀
struct Info
{
int age;
char sex;
char name[50];
};
struct Student
{
int age;
char sex;
char name[50];
int score;
};
- 巢狀例子
struct Info
{
int age;
char sex;
char name[50];
};
struct Student
{
struct Info info;//巢狀另外一個結構體變數
int score;
};
- 初始化及訪問
//結構體巢狀初始化(和不巢狀的結構體一致)
struct Student s = {18, 'm', "xiaoming", 18};
//列印巢狀結構體的內容
printf("%d, %c, %s, %d\n",s.info.age, s.info.sex, s.info.name, s.score);
-
注意事項
- 巢狀結構體變數,不能是本結構體型別。
- 但是可以巢狀任何型別的結構體指標變數。
struct Student { struct Info info; int score; //巢狀本結構體型別,會報錯。因為`struct Student`型別不確定,記憶體大小無法確定 // struct Student temp; //巢狀任何型別的結構體指標變數。因為指標大小是確定的。32位系統4位元組大小,64位系統8位元組大小 struct Student *next; };
-
補充:
typedef
的使用:將struct Student
結構體重新命名代替為為Student
- 傳統用法
struct Student { int age; char sex; char name[50]; }; typedef struct Student Student;//有分號
- 常見用法
typedef struct Student { int age; char sex; char name[50]; }Student;
4.同類型結構體相互賦值
- 直接賦值
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//typedef改一個型別名,把複雜名字改簡單點,不能創造新的型別
typedef struct Student
{
int age;
char sex;
char name[50];
}Student;
int main(void)
{
Student s1 = {18, 'm', "xiaoming"};
Student s2;
// s2 = s1; //同類型的兩個變數相互賦值
strcpy(s2.name,s1.name);
// 成員變數逐一拷貝
s2.age = s1.age;
s2.sex = s1.sex;
printf("%d, %c, %s\n",s2.age, s2.sex, s2.name);
return 0;
}
-
通過函式實現
-
關於記憶體棧區與堆區操作(這裡是實現用指標形式訪問結構體成員)
- 如果結構體只定義了一個空指標,便不能操作其內容,要讓其指標指向有效地址(棧區或者堆區)
- 在進行指向棧區空間時,不需要釋放,該空間由系統自行維護。
- 在進行指向堆區空間時,需要自己申請空間,並在使用完成後手動釋放。
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct Student { int age; char sex; char name[50]; }Student; int main(void) { //定義一個結構體指標變數 Student *p = NULL; //讓指標有個合法指向(指向棧區) // Student temp;//普通結構體變數,棧區 // p = &temp; //讓指標有個合法指向(指向堆區) p = (Student *) malloc(sizeof(Student));//申請一個`Student`大小的地址,並用p指向 if(p == NULL) { printf("分配記憶體失敗\n"); return -1; } strcpy(p->name, "小明"); p->sex = 'm'; p->age = 19; printf("%d, %c, %s\n",p->age, p->sex, p->name); //手動釋放堆區空間 free(p); p = NULL; return 0; }
-
函式程式碼實現賦值
- 這裡使用了
const
,使s1傳入進來的引數不可改變。可防止誤改傳入引數s1
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct Student { int age; char sex; char name[50]; }Student; //從左往右看,const修飾最近的字元,這修飾的是指標變數 //這裡使用了const,使s1所指向的記憶體(成員變數)不可改變,指標變數可以改變 void fun(const Student *s1, Student *s2) { //兩個指標的內容實現賦值 *s2 = *s1; } int main(void) { Student s1; Student s2 = {0,'0',"0"}; printf("%d, %c, %s\n",s2.age, s2.sex, s2.name); strcpy(s1.name, "小明"); s1.sex = 'm'; s1.age = 19; //通過一個函式將s1的值賦值給s2 fun(&s1, &s2); printf("%d, %c, %s\n",s2.age, s2.sex, s2.name); return 0; }
- 這裡使用了
-
三、記憶體分析
- 通過一個函式,動態分配記憶體
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Student
{
int age;
char sex;
char name[50];
}Student;
//需要注意的是:所有子函式執行完後的變數都會自動進行釋放(不能再直接呼叫)
Student* fun()
{
Student *a;
a = (Student *)malloc(sizeof(Student));
return a;
}
int main(void)
{
Student *p = NULL;
p = fun();//返回並用`p`指向所開闢出來的地址
//只有p指向相應地址時,才能進行賦值
strcpy(p->name, "小明");
p->sex = 'm';
p->age = 19;
printf("%d, %c, %s\n", p->age, p->sex, p->name);
//釋放子函式中`a`開闢出來的空間(`p`和`a`均會指向,只不過`a`在函式呼叫完後被釋放了)
free(p);
p = NULL;
return 0;
}
四、出現過的問題
-
不能將
x*型別的值
分配到x*型別的實體
(以下程式碼會出現該問題)#include <stdlib.h> //malloc(); #include <stdio.h> typedef struct /*Student*/ //出錯原因:在定義結構體時沒有定義變數 { //資料域(位置) int x, y; //指標域 struct Point *next; }Student; void main() { Student *pnew = (Student *)malloc(sizeof(Student)); Student *s = NULL; pnew->next = s; //此處會報錯:`s為x*型別的值` , pnew->next為`x*型別的實體` }
解決方法:在定義結構體時加上結構體變數
Student
即可