C語言 自定義類型全面系統理解

一、結構體

結構體是不同類型變量的集合體

1.結構體的聲明

struct Book      
{
 char name[20];//名字
 int Price;//價格
 char Writer[5];//作者
 char Time[20];//日期
};    //註意分號不能丟

struct為結構體關鍵字,Book為結構體標簽,中間不同類型的變量為結構體的成員。上述現在隻是定義瞭一個結構體類型struct Book。

局部結構體變量

int main()
{
   struct Book B1; // B1為局部結構體變量
   return 0;
}

全局結構體變量

struct Book      
{
 char name[20];
 int Price;
 char Writer[5];
 char Time[20];
}B3,B4,B5;      //在結構體類型後可連續定義多個全局結構體變量
 
struct Book B2;   //B2為全局結構體變量
 
int main()
{
   return 0;
}

2.特殊聲明

不完全聲明

//匿名結構體類型--沒有結構體標簽
struct
{
 int a;
 char b;
 float c;
}x;
//這樣的結構體類型必須緊跟著定義結構體變量
//後面不能定義變量

不完全聲明類型隻能在定義使用一次,並且在vs中:

struct
{
 int a;
 char b;
 float c;
}x;
 
struct
{
 int a;
 char b;
 float c;
}* ps;
 
int main()
{
  ps=&x;      //編譯器默認兩者類型不兼容  //且是錯誤寫法
  return 0;
}

因此不完全聲明很少使用,不推薦。 

3.結構體的自引用

這裡可以用鏈表的實現來理解:

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

這樣就實現瞭自己類型的對象找自己類型對象的方法,這就是結構體的自引用。 

4.結構體變量的初始化

以上面struct Book為例:

struct Book      
{
 char name[20];
 int Price;
 char Writer[5];
 char Time[20];
}; 
 
 
int main()
{
   struct Book B1={"三腳貓",50,“阿裡”,“20081001”};
   //初始化要用大括號
   return 0;
}

嵌套結構體的初始化:

struct Data
{
  int a;
  char b[6];
};
 
struct Book      
{
 struct Data D;
 char name[20];
 int Price;
 char Writer[5];
 char Time[20];
}; 
 
 
int main()
{
   struct Book B1={{4,"haha"},"三腳貓",50,"阿裡","20081001"};
   //大括號裡加大括號
   return 0;
}

5.結構體內存對齊 

計算結構體在內存中的大小

方法:

1. 第一個成員為起始,設從下標為0的地址開始向後存儲。

2. 其他成員變量要對齊到對齊數的整數倍的地址處。 對齊數 = 編譯器默認的一個對齊數與 該成員大小的較小值。 VS中默認的值為8

3. 結構體總大小為所有成員對齊數中最大對齊數的整數倍。

4. 如果嵌套瞭結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。

舉例說明:

#include <stdio.h>
struct S1
{
	char c1;
	int i;
	char c2;
};
 
struct S2
{
	char c1;
	char c2;
	int i;
};
 
int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

分析:

嵌套結構體

#include <stdio.h>
struct S1
{
	char c1;
	int i;
	char c2;
};
 
struct S2
{
	char c1;
	char c2;
	struct S1 s1;
	int i;
};
 
int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

分析:

內存對齊的存在,在平臺和性能兩方面,可以使訪問空間更加高效,用空間換取時間。

讓占用空間小的成員盡量集中在一起。

6.修改默認對齊數

#pragma pack(1)     //修改默認對齊數為1
 
//一般修改的對齊數為2^n

舉例說明:

#include <stdio.h>
#pragma pack(1)   //修改為1相當於取消瞭對齊,沒有優化,在實際應用中很少用
struct S1
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%d\n", sizeof(struct S1));  // 6
	return 0;
}

當默認對齊數被修改後,每個類型的對齊數都變為1,整體的最大對齊數也為1(相當於沒有對齊),整體大小是1的倍數,則1+4+1=6。

7.結構體傳參

當一個函數涉及到結構體時,最好用傳址調用:

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;
}

傳址調用原因:

1.函數傳參的時候,參數是需要壓棧,會有時間和空間上的系統開銷。

2.如果傳遞一個結構體對象的時候,結構體過大,參數壓棧的的系統開銷比較大,所以會導致性能的下降。

二、位段

位段的聲明和結構是類似的,有兩個不同:

1.位段的成員必須是整型。

2.位段的成員名後邊有一個冒號和一個數字。

舉例:

struct S
{
 int _a:2;
 int _b:5;
 int _c:20;
 int _d:25;
};

此時S就是一個位段類型

他的大小為8

printf("%d\n", sizeof(struct S));

分析:

下面我們來分析位段在內存中的存儲:

註:若初始化的值大於給其指定的空間,則先會發生截斷(斷左取右),再進行存儲

位段是根據實際需求來進行開辟空間,目的是為瞭節省空間提高效率。

跨平臺問題:

1. int 位段被當成有符號數還是無符號數是不確定的。

2. 位段中最大位的數目不能確定。(16位機器最大16,32位機器最大32,寫成27,在16位機 器會出問題。

3. 位段中的成員在內存中從左向右分配,還是從右向左分配標準尚未定義。(取決於編譯器)

4. 當一個結構包含兩個位段,第二個位段成員比較大,無法容納於第一個位段剩餘的位時,是 舍棄剩餘的位還是利用,這是不確定的。

三、枚舉

就是把可能的取值一一列舉

枚舉類型的定義:

例子:

enum Day  //星期
{
 Mon,
 Tues,
 Wed,
 Thur,
 Fri,
 Sat,
 Sun
};

enum Day就是一個枚舉類型,{}中的內容是枚舉類型的可能取值,也叫枚舉常量。

枚舉常量是有值的,默認從0開始,依次遞增。

int main()
{
   enum Day d=Mon;   //定義一個變量,賦予{}內可能的取值。
   return 0;
}

在定義的時候也可以賦初值,後面的常量依次遞增一

枚舉的優點 

1. 增加代碼的可讀性和可維護性

2. 和#define定義的標識符比較枚舉有類型檢查,更加嚴謹。

3. 防止瞭命名污染(封裝)

4. 便於調試

5. 使用方便,一次可以定義多個常量

註:最好用枚舉常量給枚舉變量賦值,才不會出現類型上的差異。

舉例:

當用cpp來運行程序時會報錯,因為cpp對代碼的格式會更加嚴格

而c就可以運行過去

養成良好的代碼風格,做到認真嚴謹。

四、聯合

聯合也是一種特殊的自定義類型

1.聯合類型的定義

//聯合類型的聲明
union Un
{
 char c;
 int i;
};
//聯合變量的定義
union Un un;
//計算連個變量的大小
printf("%d\n", sizeof(un));

2.聯合的特點 

聯合的成員是共用同一塊內存空間的,這樣一個聯合變量的大小,至少是最大成員的大小。

union Un
{
	int i;
	char c;
};
union Un un;
 
int main()
{
	printf("%d\n", &(un.i));
	printf("%d\n", &(un.c));
	return 0;
}

共用一塊空間,起始地址相同。

使用案例:

union Un
{
	int i;
	char c;
};
union Un un;
 
int main()
{
	//printf("%d\n", &(un.i));
	//printf("%d\n", &(un.c));
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);
	return 0;
}

分析:

3.聯合大小的計算 

聯合的大小至少是最大成員的大小。

當最大成員大小不是最大對齊數的整數倍的時候,就要對齊到最大對齊數的整數倍。

舉例:

union Un1
{
	char c[5];
	int i;
};
union Un2
{
	short c[7];
	int i;
};
//下面輸出的結果是什麼?
int main()
{
	printf("%d\n", sizeof(union Un1));
	printf("%d\n", sizeof(union Un2));
	return 0;
}

分析:

到此這篇關於C語言 自定義類型全面理解的文章就介紹到這瞭,更多相關C語言 自定義類型內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: