C語言:自定義型別部分知識點
>結構體型別建立
結構的基礎知識:結構是一些值的集合。這些值稱為成員變數。結構的每個成員可以是不同型別的變數。 結構的宣告:
struct Stu
{
char name[20];//名字
int age;//年齡
char sex[15];//性別
char id[20];//學號
};//分號不能丟
特殊的宣告: 匿名結構體型別(去掉結構體標籤,且匿名結構體型別一般只用一次)
struct { int a; char b; float c; }x; struct { int a; char b; float c; }a[20],*p;
以上的兩個結構體在宣告的時候省略掉了結構體標籤。 在上面程式碼的基礎上,
p = &x;
警告:編譯器會把上面的兩個聲明當成完全不同的兩個型別,所以千萬不能這樣定義。 結構體成員的訪問方式: ①通過點操作符(.)訪問
struct Stu
{
char name[20];
int age;
};
struct Stu s;//定義結構體變數 我們可以看到s有成員name和age;但是如何訪問s的成員呢? struct S s; s.name = "zhangsan"; //(錯誤,name是個陣列,不能直接訪問) strcpy(s.name, "zhangsan");//使用.訪問name成員 s.age = 20;//使用.訪問age成員 ②通過(->)訪問
struct S
{
char name[20];
int age;
}s;
void print(struct S* ps)
{
printf("name=%s age=%d\n", (*ps).name, (*ps).age);//這種太麻煩,不建議使用
printf("name=%s age=%d\n", ps->name, ps->age);//ps指向物件struct的成員name和age
}
結構體的自引用
struct Node
{
int data;
struct Node* next;
};
注意:結構體在自引用時不能省略 struct Node* next;中的struct;也不能使用匿名結構體型別 >結構體初始化 有了結構體型別,定義變數和初始化,就很簡單了 初始化:定義變數的同時並賦初值
struct Stu//型別宣告
{
char name[15];//名字
int age;//年齡
};
struct Stu s = { "zhangsan", 20 };//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = { 10, { 4, 5 }, NULL };//結構體巢狀初始化
struct Node n2 = { 20, { 5, 6 }, NULL };////結構體巢狀初始化
>結構體記憶體對齊 首先來說說為什麼會存在結構體記憶體對齊呢? 大部分資料是這樣說的: 1.平臺原因(移植原因): 不是所有的硬體平臺都能任意訪問任意地址上的任意資料的;某些硬體平臺只能在某些地址處取某些特定型別的資料,否則丟擲硬體異常。 2.效能原因: 資料結構(尤其是棧)應該儘可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問。 瞭解了為什麼存在記憶體對齊之後,接下來我們就得掌握結構體的對齊規則: ①第一個成員變數永遠放在起始位置的0偏移處; ②第二個成員往後的所有的成員都要對齊到某個對齊處的整數倍。對齊數就是自身大小和預設對齊數的最小值,在VS中,預設對齊數的值為8,Linux中的預設對齊數為4。 ③結構體總大小必須是所有對齊數中最大對齊數的整數倍。 ④如果嵌套了結構體的情況,巢狀的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體總大小就是所有最大對齊數(含巢狀結構體的對齊數)的整數倍。 總的來說: 結構體的記憶體對齊是拿空間來換取時間的做法。所以在設計結構體的時候,我們既要滿足對齊,又要節省空間,就要做到:讓佔用空間小的成員儘量集中在一起。 練習1:
#include <stdio.h>
#include <stdlib.h>
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));
答案:12 練習2
#include <stdio.h>
#include <stdlib.h>
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));
答案:8
練習3:
#include <stdio.h>
#include <stdlib.h>
struct S3
{
double d;
char c;
int i;
};
printf("%d\n", sizeof(struct S3));
答案:16 練習4-結構體巢狀問題
#include <stdio.h>
#include <stdlib.h>
struct S4
{
char c1;
struct S3 s3;
double d;
};
printf("%d\n", sizeof(struct S4));
答案:32
>位段 什麼是位段? 位段的宣告和結構是類似的,有兩個不同: 1.位段的成員必須是int、unsigned int或signed int。 2.位段的成員名後邊有一個冒號和一個數字。 C語言中允許在一個結構體中以位為單位來指定其成員所佔記憶體長度,利用位段能夠用較少的位數儲存資料。 位段大小的計算: (1)位段成員的型別必須指定為unsigned或int型別; (2)一個位段必須儲存在同一個儲存在同一個單元中,不能跨兩個單元; (3)位段的長度不能大於儲存單元的長度,也不能定義位段陣列; (4)位段定義的第一個位段長度不能為0。 比如:
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
printf("%d\n", sizeof(struct A)); A就是一個位段型別;那麼位段A的大小如何算呢? 根據位段的計算規則,可以算出A的大小為:2個位元組
位段的跨平臺問題 1.int位段被當成有符號數還是無符號數是不確定的。 2.位段中大的最大位的數目不能確定。(16位機器最大是16,32位機器最大是32,寫成27,在16位機器上會出問題。) 3.位段中的成員在記憶體中從左向右分配,還是從右向左分配標準尚未定義。 4.當一個結構包含兩個位段,第二個位段成員比較大,無法容納於第一個位段剩餘的位時,是捨棄餘的位還是利用,這是不確定的。 總結:跟結構相比,位段可以達到同樣的效果,但是可以很好的節省空間,但是有跨平臺的問題存在。>列舉 為什麼使用列舉? 1.增加程式碼的可讀性和可維護性 2.和define定義的識別符號比較,列舉有型別檢查,更加嚴謹 3.防止了命名汙染(封裝) 4.便於除錯 5.使用方便,一次可以定義多個常量 列舉的定義(列舉不佔據空間,列舉是一種型別,大小為4個位元組)
enum Sex//性別 { MALE, FEMALE, SECRET }; enum變數型別還可以給其中的常量符號賦值,如果不賦值則會從被賦初值的那個常量開始依次加1;如果都沒有賦值,他們的值從0開始依次遞增1.例如用一個常量表示不同顏色: enum Color { GREEN = 1, RED, BLUE, GREEN_RED = 10, GREEN_BLUE }ColorVal; 其中各常量代表的值分別為: GREEN = 1 RED=2 BLUE=3 GREEN_RED = 10 GREEN_BLUE=11>聯合 聯合是一種特殊的自定義型別,這種型別定義的變數也包含一系列的成員,特徵就是這些成員公用同一塊空間(所以聯合也叫共用體) 比如: 聯合型別的宣告
union Un
{
char c;
int i;
};
聯合變數的定義
union Un un;
計算聯合某個變數的大小
printf("%d\n", sizeof(un));
聯合的特點 聯合的成員是共用同一塊空間的,這樣一個聯合變數的大小,至少是最大成員的大小(因為聯合至少得有能力儲存最大的那個成員)。 聯合的計算 1.聯合的大小至少是最大成員的大小。 2.當最大成員不是最大對齊數的整數倍的時候,就要對齊到最大對齊數的整數倍處。 比如:
union Un1
{
char c[5];
int i;
};
printf("%d\n", sizeof(union un1));
結果:8
union Un2
{
short[7];
int i;
};
printf("%d\n", sizeof(union un2));
結果:16 聯合的應用:判斷當前機器的大小端儲存
#include <stdio.h>
#include <stdlib.h>
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;
}
int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
聯合和結構體的巧妙使用:將long型別的IP地址,轉為點分十進位制的表示形式
#include <stdio.h>
#include <stdlib.h>
union ip_addr
{
unsigned long addr;
struct
{
unsigned char c1;
unsigned char c2;
unsigned char c3;
unsigned char c4;
}ip;
};
int main()
{
union ip_addr my_ip;
my_ip.addr = 176238749;
printf("%d.%d.%d.%d\n", my_ip.ip.c4, my_ip.ip.c3, my_ip.ip.c2, my_ip.ip.c1);
return 0;
}