1. 程式人生 > 其它 >C語言--結構體總結

C語言--結構體總結

結構體

一、概念

  • 在實際問題中,一組資料往往有很多種不同的資料型別。例如,登記學生的資訊,可能需要用到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;
}	

四、出現過的問題

  1. 不能將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即可