自定義型別 結構體 列舉與聯合

2021-08-20 07:52:18 字數 4122 閱讀 8294

結構體

1、結構體的宣告

基礎知識:結構是一些值的集合,這些值稱為成員變數,結構的每個成員可以是不同的變數。

結構體也是一種型別。

結構的宣告:

struct tag   //struct 關鍵字     tag 名稱(標籤)(可以但不建議省略)

variable_list; //可以省略

舉個例子:

struct stu     //描述乙個學生

; //注意:分號不能丟

特殊的宣告:

在宣告結構的時候,可以不完全的宣告

比如:

struct

x;struct

a[20],*p; //這兩個都為匿名結構體型別,省略了結構體標籤(tag)

但若在上面的基礎上,
p=&x;
合法嗎?

答案是非法的,,,因為編譯器會把上面的兩個宣告當成是完全不同的兩個型別。

2、結構的成員

結構的成員可以是標量、陣列、指標、甚至是其他結構體。

結構體成員的訪問:

結構體變數訪問成員是通過點操作符(.)訪問的。

如上述的結構體:

struct s s;

strcpy(s.name,"zhangsan");

s.age = 20;

但有時候我們得到的不是乙個結構體變數,而是指向乙個結構體的指標,那麼則應該這樣訪問:

struct s

s;void print(struct s* ps)

3.結構的自引用

結構中包含乙個型別為該結構本身的成員。

例如:

typedef struct node

node;

結構的不完整宣告:

若兩個結構體互相包含,且要正常使用,則需要這樣:

struct b;

struct a

;struct b

;

4.結構體變數的定義和初始化

定義的時候有兩種方式:

①宣告型別的同時定義變數;  ②直接定義結構體變數;

struct point     //①

p1;struct point p2; //②

初始化:定義變數的同時賦初值

struct point p3 = ;

struct stu //型別宣告

;struct stu s = ; //初始化

結構體還可以巢狀初始化:

struct node

n1 = , null };

struct node n2 = , null };

5.結構體內存對齊

首先我們要知道為什麼要存在記憶體對齊?

①平台原因(移植原因):

不是所有的硬體平台都能訪問任意位址上的任意資料的;某些硬體平台只能在某些位址處取某些特定型別的資料,否則丟擲硬體異常。

②效能原因:

資料結構(尤其是棧)應該盡可能的在自然邊界上對齊。

原因是要訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;而對齊的記憶體訪問只需要一次。

結構體的對齊規則(重要):

1.第乙個成員在與結構體變數偏移量為0的位址處(所以結構體成員的第乙個元素你需要記憶體對齊,預設對齊);

2.其他成員變數要對齊到某個數字(對齊數)的整數倍的位址處。     對齊數:編譯器預設的乙個對齊數與該成員大小得到較小值;                        vs中預設為8       linux中預設為4

3.結構體總大小為最大對齊數(每個成員變數都有乙個對齊數)的整數倍。

4.如果巢狀了結構體,巢狀的結構體對齊到自己最大對齊數的整數倍處,結構體的整體大小為所有最大對齊數的整數倍。

當我們了解了結構體的記憶體對齊後,就要來計算結構體的大小了:

來看幾個例子:

//練習1

struct s1

; //所以總大小為最大對齊數的倍數: 12

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

//練習2

struct s2

; //這次的結果是8 只是變換了一下成員的位置,結果就不一樣,正好說明了結構體的對齊規則

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

//練習3

struct s3

; //按照規則,結果為16

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

下面來乙個複雜的,結構體巢狀:

//練習4

struct s4

; //∴ 1+3+16+8=28 但要滿足總大小為最大對齊數的倍數 ∴結構體總大小為32

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

總的來說:結構體的記憶體對齊就是用空間來換取時間。

擴充套件:

#pragma pack()
這個可以用來修改編譯器預設的對齊數;

括號中只能是1、2、4、8; 若括號中什麼都不寫,表示恢復預設。

結構體在傳參的時候不發生降維;且盡量不要直接傳結構體變數,而應該傳結構體指標。

位段

位段的宣告和結構是類似的,但有兩個不同:

①位段的成員必須是int,unsigned int,signed int。

②位段的成員名後邊有乙個冒號和數字。

如:

struct a

; //因為它的單位是位元位,所以它的大小是8

位段的記憶體分配:

①位段的成員可以是int,unsigned int,signed int或者是char(屬於整形家族)型別;

②位段的空間上是按照需要以4個位元組(int)或者乙個位元組(char)的方式來開闢的;

③位段涉及很多不確定因素,位段是不跨平台的,注重可移植的程式盡量避免使用位段。

位段的跨平台問題:

①int位段被當成是有符號數還是無符號數是不確定的;

②位段中最大位的數目不能確定;

③位段中的成員在記憶體中從左行右分配,還是從右向左標準尚未定義;

④當乙個結構包含兩個位段,第二個位段成員比較大,無法容納於第乙個位段剩餘的位時,是捨棄剩餘的位還是利用,也不確定。

總結:位段跟結構相比,可以達到同樣的效果,還可以節省空間,但是有跨平台的問題存在。

列舉

列舉其實就是把可能的取值一一枚舉;

這裡就不介紹列舉了,我們來看看它的優點吧:

①增加**的可讀性和可維護性;

②和#define定義的識別符號比較列舉有型別檢查,更加嚴謹;

③防止了命名汙染(封裝)

④便於除錯;

⑤使用方便,一次可以定義多個常量。

聯合(共用體)

聯合其實也是一種特殊的自定義型別,它定義的變數也包含一系列的成員,

最大的特點就是這些成員共用同一塊空間; 

那麼乙個聯合變數的大小,就至少是最大成員的大小;

聯合大小的計算:

①聯合的大小至少是最大成員的大小;

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

這裡我們看乙個聯合和結構體的巧妙使用:

union ip_addr

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

可以將long型別的ip位址轉化為點分十進位制的表示形式。

自定義型別 結構體 列舉 聯合

結構體 所謂結構體,就是將一大堆值放在一起,建立乙個新的型別,這些成員可以是不同型別的變數。struct tag variable list tag 要求 1.見名知意 2.可以省略 3.不建議省略 member list c語言中,不能為空 variable list 變數列表,可以省略,建議省略...

自定義型別 結構體,列舉,聯合

首先先說一下c語言的資料型別,其框架型別如圖所示 今天主要說一下結構體,列舉,聯合這三種自定義型別。1,結構體 1.1 結構體的宣告 結構體是一些值的集合,這些值稱為成員變數,結構體的每個成員可以是不同型別得變數。1 結構體的成員 結構體的成員可以是標量,陣列,指標,甚至是其他結構體。2 結構體的宣...

自定義型別 結構體,列舉,聯合

結構體的宣告 struct tag variable list 例如描述乙個學生 struct stu 分號不能丟特殊的宣告 在宣告結構的時候,可以不完全的宣告。比如 匿名結構體型別 struct x struct a 20 p 警告 編譯器會把上面的兩個宣告當成完全不同的兩個型別。所以是非法的。結...