首頁 > 軟體

C語言自定義型別全解析

2022-02-10 13:01:02

前言

初學C語言

我們先接觸的都是內建的型別

比如說int char short float double long等等

這一期就來聊一聊自定義型別的知識

結構體型別

首先我們要知道什麼是結構體

結構體就是各種值集合

這些值被稱作結構體成員,這些成員可包括各種不同的型別

struct tag         //這裡的struct是結構體的關鍵字,tag是結構體標籤,也就是結構體的名稱
{
	number - list; //結構體成員列表
}veriable-list;    //結構體的變數 

結構體的宣告

如果結構體的標籤是student,我拿student來舉例子

結構體的完整宣告

struct Student
{
    char name[20];//姓名
    char sex;//性別
    int age;//年齡
    int num;//學號
};   //這裡的分號不能丟

結構體的不完全宣告(匿名結構體型別)

struct
{
	int a;
	char b;
	double c;
}s;   //這s不能省略

匿名結構體的特點就是沒有結構體標籤

但這樣寫使用者使用時只能使用一次,也就是說只能在結構體宣告時就定義變數

因為你找不到結構體標籤,就相當於找不到門牌號一樣,無法再對其定義一個變數

結構體變數的定義與初始化

結構體的定義大致分為三種情況

<1>結構體宣告的同時定義

struct Student 
{
    char name[20];//姓名
    char sex[20];//性別
    int age;//年齡
}s={"zhangsan","nan",20};

<2>結構體先宣告,再定義

#include<stdio.h>
struct Student
{
    char name[20];//姓名
    char sex;//性別
    int age;//年齡
    int num;//學號
};
 
 
int main()
{
    struct Student s = { "zhangsan",'w',20,111 };
	return 0;
}

<3>匿名結構體定義

struct 
{
    char name[20];//姓名
    char sex[20];//性別
    int age;//年齡
} s = { "zhangsan","nan",20};

注意:結構體初始化與陣列相同,都必須整體進行賦值。

結構體的自參照

struct Node //初始話連結串列
{
	int a;
	struct Node next;
};

結構體的自參照就是結構體再套用自己

學過資料結構的朋友應該知道這是初始化連結串列

不過這一個程式碼有問題的

問題在於無法求出這個結構體的大小,不清楚這個結構體有多大,因為無法求出自參照的結構體有多大

所有自參照的結構體要用指標來存取

struct Node //初始話連結串列
{
	int a;
	struct Node* next;
};

故就可以通過指標來存取每一個結點

結構體的存取

當結構體定義且變數初始化完成後,就可以通過操作符來存取變數中的成員

當然,這裡給出了兩個操作符

分別是  .操作符和 -> 操作符

當使用結構體變數時,就用點操作符,當存取結構體指標變數就用箭頭操作符

(1)通過結構體變數進行存取:

printf("%sn",s.name);

(2)通過結構體指標進行存取:

printf("%sn",ps->name);

結構體的傳參

函數的呼叫有時候需要傳一些引數

引數的型別可以是不同的型別,可以是陣列,也可以是指標

同樣結構體的傳參也可通過傳結構體或者傳指標

傳結構體

#include<stdio.h>  
struct tag
{
	int a;
	char b[20];
}s1 = { 100,"abcdef" };
void print()
{
	printf("%d", s1.a);
}
int main()
{
	print(s1);
	return 0;
}

傳地址

#include<stdio.h>
struct tag
{
	int a;
	char b[20];
}s2 = { 100,"abcdef" };
void print(struct tag*s2)
{
	printf("%d", s2->a);
}
int main()
{
	print(&s2);
	return 0;
}

我們要知道函數傳參是形參就是實參的臨時拷貝

引數是要壓棧的(向系統申請空間),既然是臨時拷貝,就會再次再棧上開闢空間,當實參足夠大時,顯然會浪費一定的空間和時間

相比較與傳結構體,傳指標會更好 

結構體的記憶體對齊(強烈建議觀看)

在另外一篇文章詳細講過——C語言結構體中記憶體對齊的問題理解

位元欄

可能有人沒有聽過什麼是位元欄

位元欄的結構型別跟結構體有些類似可以類似結構體去學習

也可以說

位元欄是結構體特殊的實現

位元欄的宣告

相較於結構體,位元欄的宣告有兩點不同

<1>規定位元欄的成員的必須是int,unsigned int ,signed int (但是寫成char型別也沒什麼大的問題)

<2>位元欄的成員後面有一個冒號和一個數位

struct A  //位元欄的宣告
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};

位元欄的記憶體管理

#include<stdio.h>
struct A
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};
int main()
{
	
	printf("大小是%d位元組", sizeof(struct A));
	return 0;
}

為什麼位元欄的大小是八個位元組呢?

成員內包含的數位代表的是這個成員需要利用的記憶體,單位是bit。

位元欄成員申請記憶體時都是以四個位元組或者一個位元組單位(當成員是char型別時)

int a : 2; //申請4個位元組,也就是32個bit位,利用兩個還剩30個
int b : 5; //利用5個,還剩25個
int c : 10; //利用10個,還剩15個
int d : 30; //這裡的十五不夠,所以再申請了4個位元組

最終的結果就是八位元組

但問題是,變數d利用的空間是留下的15個bit加上重新申請的空間呢

這個結果在不同的環境的結果是不同的,所以位元欄的跨平臺性比較差

位元欄使用的前提是你知道儲存的記憶體大概有多大

就比如說年齡

十個bit位0111111111,最大值就可以達到1023

就不需要再申請一次利用一個int型別的空間大小

這就達到了節省記憶體的作用,存在即合理

位元欄的應用 

struct A
{
	char a : 3;
	char b : 4;
	char c : 5;
};
main()
{
	struct A s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	return 0;
}

位元欄的跨平臺性

1.int位元欄被當成有符號數還是無符號數是不確定的,有時候系統會自動轉化為無符號整形。

2.位元欄中最大位的數目不能確定。(因為在早期的16位元機器int最大16,而32位元機器int最大32)

3.位元欄中的成員在記憶體中從左向右分配,還是從右向左分配標準尚未定義。

意思當記憶體一個位元組00000000,存入01010,可能會出現00001010或者01010000

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

 列舉型別

列舉型別適用於可以一一列舉的型別

比如說星期、性別

列舉型別的定義

enum Day   //星期
{
	//列舉的可能取值
	Mon,
	Tues,
	Wed,
	Thir,
	Fri,
	Sta,
	Sun
};
enum Sex  //性別
{
	MALE,//0
	FEMALE,//1
	SECRET//2
};

列舉型別是有初始值的

如果你沒賦予它初始值,就會預設從零開始,依次加一

列舉型別賦予初始值

#include<stdio.h>
enum Sex  //性別
{
	MALE = 4,
	FEMALE=10,
	SECRET//
};
main()
{
	printf("%d %d %d", MALE,FEMALE,SECRET);
	return 0;
}

可以看到,其實預設的值是可以改的

當某個成員被賦予某個值的時候,後續的成員就在此基礎上加一

列舉型別的優點

1.相較於數位,列舉增加程式碼的可讀性和可維護性

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

3.防止了命名汙染(封裝)

4.便於偵錯

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

聯合體型別

聯合體型別也叫共用體

聯合體的定義

union Un  
{
	int a;
	char b;
};

union是聯合體關鍵字,Un是聯合體的標籤

聯合體的特點

共用體,顧名思義,這裡的共用就是公用記憶體

記憶體的也可被摺疊

#include<stdio.h>
union Un
{
	char c;
	int i;
};
int main()
{
	union Un u = {0};
	printf("%dn", sizeof(u));
	printf("%pn", &u);
	printf("%pn", &(u.c));
	printf("%pn", &(u.i));
	return 0;
}

他們有相同的地址

c和i存放在同一塊記憶體空間上,修改c或者i都會影響到另一個成員。

 聯合體記憶體大小的計算

<1>聯合體記憶體大小是最大成員的大小

<2>最大成員的大小如果不是最大對齊數的整數倍,就會對齊到最大對齊數的整數倍

(聯合體也存在記憶體對齊)

#include<stdio.h>
union Un1
{
	char c[5];  
	int i;
};          
//Un1成員最大成員大小5,最大對齊數是4,所以Un1的大小是8;
union Un2
{
	char c[7];
		int i;
};
//Un2成員最大成員大小7,最大對齊數是4,所以Un2的大小是8;
int main()
{
	printf("%dn", sizeof(union Un1));
	printf("%dn", sizeof(union Un2));
	return 0;
}

到此這篇關於C語言自定義型別全解析的文章就介紹到這了,更多相關C語言 自定義型別內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com