自定義類型詳解(1)

文章目錄

  • 目錄
    • 1. 結構體
      • 1.1 結構的基礎知識
      • 1.2 結構的聲明
      • 1.3 特殊的聲明
      • 1.4 結構的自引用
      • 1.5 結構體變量的定義和初始化
      • 1.6 結構體內存對齊
      • 1.7 修改默認對齊數
      • 1.8 結構體傳參
    • 2. 位段
      • 2.1 什么是位段
      • 2.2 位段的內存分配
      • 2.3 位段的跨平臺問題
      • 2.4 位段的應用
    • 3. 枚舉
      • 3.1 枚舉類型的定義
      • 3.2 枚舉的優點
      • 3.3 枚舉的使用
    • 4. 聯合(共用體)
      • 4.1 聯合類型的定義
      • 4.2 聯合的特點
      • 4.3 聯合大小的計算

目錄

  • 結構體
  • 位段
  • 枚舉
  • 聯合體
  • 通訊錄的實現

1. 結構體

1.1 結構的基礎知識

結構是一些值的集合,這些值稱為成員變量,結構的每個成員可以是不同類型的變量。

區分:

數組:一組相同類型元素的集合

1.2 結構的聲明

結構的聲明
舉個例子:
結構的聲明舉例

1.3 特殊的聲明

在聲明結構的時候,可以不完全的聲明。

struct
{int a;char c;float f;
}x;struct
{int a;char c;float f;
}* p;//上面的兩個結構在聲明的時候省略掉了結構體標簽(tag)。int main()
{//p = &x;//errreturn 0;
}

注:
編譯器會把上面的兩個聲明當成完全不同的兩個類型,所以是非法的。

1.4 結構的自引用

結構的自引用
以下寫法是錯誤的:

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

應該這樣寫:

typedef struct Node
{int data;struct Node* next;
}Node;int main()
{Node n = { 0 };return 0;
}

1.5 結構體變量的定義和初始化

#include <stdio.h>struct SN
{char c;int i;
}sn1 = { 'q', 100 }, sn2 = { .i = 200, .c = 'w'};//全局變量struct S
{double d;struct SN sn;int arr[10];
};int main()
{struct SN sn3, sn4;//局部變量printf("%c %d\n", sn2.c, sn2.i);struct S s = { 3.14, { 'a', 99 }, { 1, 2, 3 } };printf("%lf %c %d\n", s.d, s.sn.c, s.sn.i);int i = 0;for (i = 0; i < 10; i++){printf("%d ", s.arr[i]);}return 0;
}

1.6 結構體內存對齊

結構體的對齊規則:

  1. 第一個成員在與結構體變量偏移量為0的地址處。
  2. 其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處。
    對齊數 = 編譯器默認的一個對齊數該成員大小較小值
  • VS中默認的值為8
  • Linux中沒有默認對齊數,對齊數就是成員自身的大小
  1. 結構體總大小為最大對齊數(每個成員變量都有一個對齊數)的整數倍。
  2. 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。

我們可以通過代碼來觀察:

#include <stdio.h>
#include <stddef.h>struct S1
{char c1;int i;char c2;
};struct S2
{int i;char c1;char c2;
};int main()
{printf("%d\n", sizeof(struct S1));//12printf("%d\n", sizeof(struct S2));//8//可以計算結構體成員相較于結構體起始位置的偏移量printf("%d\n", offsetof(struct S1, c1));//0printf("%d\n", offsetof(struct S1, i));//4printf("%d\n", offsetof(struct S1, c2));//8return 0;
}

再舉個例子:

#include <stdio.h>struct S3
{double d;char c;int i;
};struct S4
{char c1;struct S3 s3;double d;
};int main()
{printf("%d\n", sizeof(struct S3));//16printf("%d\n", sizeof(struct S4));//32return 0;
}

如果結構體中有數組,我們將它看作一個一個的元素即可:

#include <stdio.h>
#include <stddef.h>struct S
{char c;int arr[4];
};int main()
{	printf("%d\n", offsetof(struct S, arr[0]));//4printf("%d\n", offsetof(struct S, arr[1]));//8printf("%d\n", offsetof(struct S, arr[2]));//12printf("%d\n", offsetof(struct S, arr[3]));//16printf("%d\n", sizeof(struct S));//20return 0;
}

為什么存在內存對齊?

  1. 平臺原因(移植原因):
    不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常。
  2. 性能原因:
    數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。

內存對齊的原因
總體來說:

結構體的內存對齊是拿空間來換取時間的做法。

那在設計結構體的時候,我們既要滿足對齊,又要節省空間,如何做到:

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

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

S1和S2類型的成員一模一樣,但是S1和S2所占空間的大小有了一些區別。

1.7 修改默認對齊數

之前我們見過了 #pragma 這個預處理指令,這里我們再次使用,可以改變我們的默認對齊數。

#include <stdio.h>#pragma pack(8)//設置默認對齊數為8struct S1
{char c1;int i;char c2;
};#pragma pack()//取消設置的默認對齊數,還原為默認#pragma pack(1)//設置默認對齊數為1struct S2
{char c1;int i;char c2;
};#pragma pack()//取消設置的默認對齊數,還原為默認int main()
{printf("%d\n", sizeof(struct S1));//12printf("%d\n", sizeof(struct S2));//6return 0;
}

結論:

結構在對齊方式不合適的時候,我們可以自己更改默認對齊數。

一道筆試題:

寫一個宏,計算結構體中某變量相對于首地址的偏移,并給出說明

考察: offsetof 宏的實現
注:這里還沒學習宏,可以放在宏講解完后再實現。

1.8 結構體傳參

#include <stdio.h>struct S
{int data[100];int num;
};void print1(struct S tmp)
{printf("%d\n", tmp.num);
}void print2(const struct S* ps)
{printf("%d\n", ps->num);
}int main()
{struct S s = { { 1, 2, 3 }, 100 };print1(s);print2(&s);return 0;
}

上面的 print1 和 print2 函數哪個好些?
答案是:首選print2函數。
原因:

函數傳參的時候,參數是需要壓棧,會有時間和空間上的系統開銷。
如果傳遞一個結構體對象的時候,結構體過大,參數壓棧的的系統開銷比較大,所以會導致性能的下降。

結論:
結構體傳參的時候,要傳結構體的地址

2. 位段

結構體講完就得講講結構體實現位段的能力。

2.1 什么是位段

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

  1. 位段的成員必須是 charintunsigned intsigned int
  2. 位段的成員名后邊有一個冒號一個數字
#include <stdio.h>//00
//01
//10
//11
//比如_a只有這四種取值,那么只需要兩個比特位就可以解決,就不需要一個整型那么大的空間了struct A
{int _a : 2;//二進制位int _b : 5;int _c : 10;int _d : 30;
};int main()
{printf("%d\n", sizeof(struct A));//8return 0;
}

2.2 位段的內存分配

  1. 位段的成員可以是 intunsigned intsigned int 或者是 char (屬于整形家族)類型
  2. 位段的空間上是按照需要以4個字節( int )或者1個字節( char )的方式來開辟的。
  3. 位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程序應該避免使用位段。

我們來看一下在VS上位段的內存分配:

#include <stdio.h>struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};int main()
{struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;printf("%d\n", sizeof(s));//3return 0;
}

VS上位段的內存分配
VS上位段的內存分配(1)

2.3 位段的跨平臺問題

  1. int 位段被當成有符號數還是無符號數是不確定的。
  2. 位段中最大位的數目不能確定。(16位機器最大16,32位機器最大32,寫成27,在16位機器會出問題。)
  3. 位段中的成員在內存中從左向右分配,還是從右向左分配標準尚未定義。
  4. 當一個結構包含兩個位段,第二個位段成員比較大,無法容納于第一個位段剩余的位時,是舍棄剩余的位還是利用,這是不確定的。

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

2.4 位段的應用

IP數據包的格式:
IP數據包的格式

3. 枚舉

枚舉顧名思義就是一一列舉,把可能的取值一一列舉。
比如我們現實生活中:

一周的星期一到星期日是有限的7天,可以一一列舉。
性別有:男、女、保密,也可以一一列舉。
月份有12個月,也可以一一列舉

這里就可以使用枚舉了。

3.1 枚舉類型的定義

#include <stdio.h>enum Color
{RED,GREEN,BLUE
};int main()
{printf("%d\n", RED);//0printf("%d\n", GREEN);//1printf("%d\n", BLUE);//2return 0;
}

{}中的內容是枚舉類型的可能取值,也叫枚舉常量
這些可能取值都是有值的,默認從0開始,依次遞增1,當然在聲明枚舉類型的時候也可以賦初值。
例如:

#include <stdio.h>enum Color
{RED = 4,GREEN,BLUE
};int main()
{printf("%d\n", RED);//4printf("%d\n", GREEN);//5printf("%d\n", BLUE);//6return 0;
}
#include <stdio.h>enum Color
{RED,GREEN = 8,BLUE
};int main()
{printf("%d\n", RED);//0printf("%d\n", GREEN);//8printf("%d\n", BLUE);//9return 0;
}
#include <stdio.h>enum Color
{RED = 4,GREEN = 8,BLUE = 1
};int main()
{printf("%d\n", RED);//4printf("%d\n", GREEN);//8printf("%d\n", BLUE);//1return 0;
}

3.2 枚舉的優點

我們可以使用 #define 定義常量,為什么非要使用枚舉?
枚舉的優點:

  1. 增加代碼的可讀性和可維護性
  2. 和#define定義的標識符比較枚舉有類型檢查,更加嚴謹。
  3. 便于調試
  4. 使用方便,一次可以定義多個常量

3.3 枚舉的使用

enum Color
{RED,GREEN,BLUE
};int main()
{enum Color c = GREEN;return 0;
}

4. 聯合(共用體)

4.1 聯合類型的定義

聯合也是一種特殊的自定義類型,這種類型定義的變量也包含一系列的成員,特征是這些成員公用同一塊空間(所以聯合也叫共用體)。
比如:

#include <stdio.h>union Un
{char c;int i;
};int main()
{printf("%d\n", sizeof(union Un));//4union Un un = { 0 };return 0;
}

4.2 聯合的特點

聯合的成員是共用同一塊內存空間的,這樣一個聯合變量的大小,至少是最大成員的大小(因為聯合至少得有能力保存最大的那個成員)。

#include <stdio.h>union Un
{char c;int i;
};int main()
{union Un un = { 0 };printf("%p\n", &un);printf("%p\n", &un.i);printf("%p\n", &un.c);//以上三個地址是一樣的un.i = 0x11223344;un.c = 0x55;return 0;
}

聯合的特點
有這樣一道題目:

判斷當前計算機的大小端存儲

#include <stdio.h>union Un
{int i;char c;
};int main()
{union Un u = { 0 };u.i = 1;if (1 == u.c){printf("小端\n");}else{printf("大端\n");}return 0;
}
#include <stdio.h>int check_sys()
{union{int i;char c;}un = { .i = 1 };return un.c;
}int main()
{int ret = check_sys();if (1 == ret){printf("小端\n");}else{printf("大端\n");}return 0;
}

4.3 聯合大小的計算

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

比如:

#include <stdio.h>union Un1
{char c[5];//1 8 1         5int i;//4 8 4
};union Un2
{short c[7];//2 8 2         14int i;//4 8 4
};int main()
{printf("%d\n", sizeof(union Un1));//8printf("%d\n", sizeof(union Un2));//16return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/214016.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/214016.shtml
英文地址,請注明出處:http://en.pswp.cn/news/214016.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

linux向日葵開機自啟動

有個服務需要先開啟: sudo systemctl start runsunloginclient.service 開機自啟動服務: sudo systemctl enable runsunloginclient.service 然后再啟動就可以了 sudo systemctl status runsunloginclient.service 開機自啟后進行檢查service服務狀態 開發板ubuntu系統上如…

蝦皮選品:如何在蝦皮平臺上進行選品以提高銷售額和利潤

在蝦皮&#xff08;Shopee&#xff09;平臺上進行選品時&#xff0c;可以遵循以下策略和技巧&#xff0c;以便找到有潛力的產品并提高銷售額。 先給大家推薦一款shopee知蝦數據運營工具 知蝦免費體驗地址&#xff08;復制瀏覽器打開&#xff09;&#xff1a;d.ddqbt.com/JU5o …

Java并發(二)

一、并發編程三要素 1、原子性 原子性指的是一個或者多個操作&#xff0c;要么全部執行并且在執行的過程中不被其他操作打斷&#xff0c;要么就全部都不執行。 2、可見性 可見性指多個線程操作一個共享變量時&#xff0c;其中一個線程對變量進行修改后&#xff0c;其他線程可以…

亞信安慧通過ISO20000認證,AntDB數據庫團隊服務能力再上新臺階

近日&#xff0c;湖南亞信安慧科技有限公司&#xff08;簡稱“亞信安慧”&#xff09;獲得《信息安全管理服務管理體系認證證書》&#xff0c;標志著公司已建立起一套與國際對標的IT系統管理體系&#xff0c;在信息技術服務能力上取得了新的里程碑。 圖1 亞信安慧通過ISO20000認…

【Unity】Addressable包資源加載失敗:CRC Mismatch.

Error while downloading Asset Bundle: CRC Mismatch. 是資源下載校驗失敗&#xff0c;但是資源和上次打包的資源是一樣的。沒有排查到原因&#xff0c;在谷歌搜索后看到 大概就是指Unity版本修改后打包&#xff0c;會破壞原來的CRC信息&#xff0c;導致導報出來的資源無法通…

軟件測試BUG管理神器——禪道

背景與用途 使用背景 針對開發的產品進行BUG質量管理&#xff0c;通過需求、任務、bug來進行交相互動&#xff0c;終通過項目拿到合格的產品&#xff01; 場景介紹 測試人員提出bug -> 開發人員解決bug -> 測試人員驗證關閉 下載安裝 一、搜索禪道官網 1.1在瀏覽器搜索…

Boost:asio單io_service,多線程run

io_service相當于注冊異步回調的一個上下文環境,而run相當于處理異步io的上下文(通常是一個線程)。 單io_service,多線程run,相當于多個線程同時來處理注冊在一個io_service上的回調: //sio_mth.cpp #include <boost/asio.hpp> #include <boost/date_time/pos…

Java集合進階

目錄 集合體系結構 Collection集合 List集合 ArrayList集合 LinkedList集合 集合體系結構 注意:有序:存進去的數組和取出來時一樣 而不是大小的那種有序 Collection集合 單列集合頂層接口Collection import java.util.ArrayList; import java.util.Collection;public cl…

外貿獲客怎么做?有哪些技巧?

外貿獲客是許多企業拓展海外市場的關鍵一環&#xff0c;為了成功地吸引潛在客戶&#xff0c;我們需要了解一些基本的獲客技巧&#xff0c;本文將分享一些實用的方法和技巧&#xff0c;幫助您在外貿領域獲得更多的客戶。 一、了解目標客戶 在開展外貿業務之前&#xff0c;了解…

java Optional類

Java 8 引入的 Optional 類,主要解決的問題是空指針異常&#xff08;NullPointerException&#xff09; 返回值/修飾符方法詳細static Optionalempty() 返回一個空的 Optional實例。Optional<String> stringOptional Optional.empty();booleanequals(Object obj) 判斷…

IO流的使用(四)

對象序列化機制 概念&#xff1a;允許把內存中的Java對象轉換成與平臺無關的二進制流&#xff0c;從而允許把這種二進制流持久地保存在磁盤上&#xff0c;或通過網絡將這種二進制流傳輸到另一個網絡節點&#xff1b;當其它程序取了這種二進制流&#xff0c;就可恢復成原來的Ja…

C# OpenCvSharp DNN 部署YOLOV6目標檢測

目錄 效果 模型信息 項目 代碼 下載 C# OpenCvSharp DNN 部署YOLOV6目標檢測 效果 模型信息 Inputs ------------------------- name&#xff1a;image_arrays tensor&#xff1a;Float[1, 3, 640, 640] -------------------------------------------------------------…

一個不上進的愛好,讓我走進了計算機世界

為什么當初選擇計算機行業 當初選擇計算機專業&#xff0c;真的就是覺得學習計算機專業&#xff0c;就可以經常接觸計算機&#xff0c;可以有很多的機會可以玩游戲。 后來高考的時候&#xff0c;考試成績也不理想&#xff0c;分數就不好意思說了。但是喜從天降&#xff0c;居…

Windows Terminal的半透明效果

打開Windows Terminal的半透明效果 最終實現效果&#xff1a; 系統&#xff1a;win11 23H2 步驟&#xff1a; 1.winx打開終端 2.右鍵打開設置 3.打開外觀->亞克力材料開啟 4.默認值->外觀->透明度&#xff0c;按喜好選擇即可

Linux+Moba+虛擬機

軟件&#xff1a; VMware Workstation ProMobaXterm 簡介 是一款由VMware公司開發的強大的虛擬機軟件。它可以在單臺物理計算機上創建、運行和管理多個虛擬機&#xff0c;每個虛擬機都可以獨立運行不同的操作系統和應用程序。 功能&#xff1a; 虛擬化&#xff1a;能…

PPP協議概述與實驗示例

PPP協議概述與實驗示例 概述 PPP&#xff08;Point-to-Point Protocol&#xff09;是一種用于在點對點連接上傳輸多協議數據包的標準方法。它已經成為最廣泛使用的互聯網接入數據鏈路層協議&#xff0c;可以與各種技術結合&#xff0c;如ADSL、LAN等&#xff0c;實現寬帶接入…

如何通過內網穿透工具實現任意瀏覽器遠程訪問Linux本地zabbix web管理界面

前言 Zabbix是一個基于WEB界面的提供分布式系統監視以及網絡監視功能的企業級的開源解決方案。能監視各種網絡參數&#xff0c;保證服務器系統的安全運營&#xff1b;并提供靈活的通知機制以讓系統管理員快速定位/解決存在的各種問題。 本地zabbix web管理界面限制在只能局域…

MyBatis-Plus - 論 1 個實體類被 N 個DAO 類綁定,導致 MP 特性(邏輯刪)失效的解決方案

問題描述 最近遇到一個奇奇怪怪的問題&#xff0c;發現 Mybatis-Plus『邏輯刪』特性失效&#xff0c;而且是偶現&#xff0c;有時候可以&#xff0c;有時候又不行。于是開啟了 Debug Mybatis-Plus 源碼之旅 原因分析 我們接下來重點關注 TableInfoHelper 類 /** Copyright (…

算法:兩數之和(暴力解法和優化算法)

暴力解法&#xff1a;使用快慢指針解決&#xff0c;時間復雜度 O(n^2)&#xff0c;空間復雜度 O(n) /*** param {number[]} nums* param {number} target* return {number[]}*/ var twoSum function(nums, target) {let slow 0let fast 1// 如果慢指針沒有超過nums邊界就繼…

代理IP怎么使用?Mac蘋果系統設置http代理IP教程

代理IP是一種通過將請求轉發到另一個服務器&#xff0c;以隱藏自己的真實IP地址的服務器。使用代理IP可以保護您的隱私和安全&#xff0c;防止被跟蹤或被攻擊。在本文中&#xff0c;我們將介紹如何在Mac蘋果系統上設置http代理IP教程。 一、了解代理IP 代理IP地址是一種可以用來…