#C語言——學習攻略:自定義類型路線--結構體--結構體類型,結構體變量的創建和初始化,結構體內存對齊,結構體傳參,結構體實現位段

🌟菜鳥主頁:@晨非辰的主頁

👀學習專欄:《C語言學習》

💪學習階段:C語言方向初學者

?名言欣賞:“人理解迭代,神理解遞歸。”


目錄

1.? 結構體類型

1.1? 舊知識回顧

1.1.1? 結構體聲明

1.1.2? 結構體的創建和初始化

1.2? 結構體的特殊聲明

1.3? 結構體的自引用

2.? 結構體內存對齊(熱門考點)

2.1? 對齊規則

習題1

習題2

習題3

習題4--嵌套結構體大小

2.2? 為什么存在內存對齊

2.3? 修改默認對齊數

3.? 結構體傳參

4.? 結構體實現位段

4.1? 什么是位段

4.2? 位段的內存分配

4.3? 位段的跨平臺問題

4.4? 位段的應用

4.5? 位段使用注意事項


1.? 結構體類型

1.1? 舊知識回顧

-- 在前面操作符部分有過初步的了解:?結構體是一些值的集合,這些值稱為成員變量。結構體的每個成員都可以是不同類型的變量;比如:數組、指針、其他結構題、體等等。

????????--博客跳轉鏈接:結構成員訪問操作符

1.1.1? 結構體聲明

struct tag        struct表明是結構體
{member-list;    一個或多個成員變量
}variable - list    變量列表, 可有可無,在聲明變量類型是可同時定義的變量,且為全局變量

--比如,當我們想描述一個學生時,就要包括;姓名、成績、年齡、學號等等,這時候單一的內置類型就顯得力不從心; 哎~~,結構體就上場了:

struct Stu
{char name[20];  名字拼音int age;        年齡char id[20];    學號float score;    成績//……
};    分號絕對不能丟

1.1.2? 結構體的創建和初始化

struct Stu
{char name[20];int age;char sex[5];char id[20];
};int main()
{//初始化--按成員順序struct Stu s1 = { "liming", 18, "man", "2023319829" };//進行訪問-printf("name:%s\n", s1.name);printf("age:%d\n", s1.age);printf("sex:%s\n", s1.sex);printf("id:%s\n", s1.id);printf("\n");//初始化--按指定順序struct Stu s2 = { .age = 20, .sex = "man", .name = "zhangsan", .id = "2023393839" };printf("name:%s\n", s1.name);printf("age:%d\n", s1.age);printf("sex:%s\n", s1.sex);printf("id:%s\n", s1.id);return 0;
}

1.2? 結構體的特殊聲明

--在聲明時,結構體也存在著不完全聲明:

? ? ? ? --匿名結構體:

struct
{int a;char b;float c;
}x;//全局變量struct
{int a;char b;float c;
}* p;

--可以發現,上面的結構體省略了標簽-tag;

--那如果在上面代碼的基礎,那下面的合理嗎?

p = &x;

警告:

--雖然上面兩個結構體成員相同,但是編譯器會將上面兩個聲明當作不同的類型(類似兩個同名但不同地址的房屋),會導致類型不兼容錯誤;

--匿名結構體(無標簽)只能通過原始定義使用(同時聲明結構體、變量),無法在其他地方引用相同的類型匿名結構體無法復用-改進:

????????--使用結構體標簽、用?typedef?創建類型別名;

1.3? 結構體的自引用

--在對結構體進行定義后,那是否可以將結構體本身當作結構體的一個成員呢?

? ? ? ? --比如定義一個鏈表的結點:

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

--這樣其實是錯誤的!!

? ? ? ? --因為當結構體里面在包含一個同類型的結構體,會導致結構體內存無限大,顯然是錯誤的。

--但是可以這樣操作(可以包含指向同類型的指針):

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

--在結構體自引用使用的過程中,夾雜了 typedef?對匿名結構體類型重命名,容易引入問題,看下面的代碼:

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

? ? ? ? --這樣也是錯誤的!!因為Node是重命名來的,在結構體內部提前使用重命名的結果是不可行的。所以最好不要使用匿名結構體!!


2.? 結構體內存對齊(熱門考點)

--了解了基礎知識后,下面來談一談它的內存如何計算??

2.1? 對齊規則

結構體對齊規則:

  • 結構體的第?個成員對齊到和結構體變量起始位置偏移量為0的地址處;
  • 其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處;

????????--?對齊數 = 編譯器默認的?個對齊數與該成員變量大小的較小值。

? ? ? ? ????????--?VS 中默認的值為 8 、 Linux中 gcc 沒有默認對齊數,對齊數就是成員自身的大小;

  • 結構體總大小為最大對齊數(結構體中每個成員變量都有?個對齊數,所有對齊數中最大的)的整數倍;
  • 如果嵌套了結構體,嵌套者對齊到自己成員最大對齊數的整數倍,總的結構體大小由第3條進行判斷(包括嵌套者的成員);

--這樣干巴得理解還是有點模糊,別急,下面幾道例題來救一下!

習題1


struct s1
{char c1;int i;char c2;
};int main()
{printf("%zd\n", sizeof(struct s1));	12return 0;
}

圖解演示——

習題2

struct S2
{char c1;char c2;int i;
};int main()
{printf("%zu\n", sizeof(struct S2));//8
}

圖解演示——

習題3

struct S3
{double d;char c;int i;
};int main()
{printf("%zu\n", sizeof(struct S3));//16
}

圖解演示——

習題4--嵌套結構體大小

struct S3
{double d;char c;int i;
};
struct S4
{char c1;struct S3 s3;double d;
};int main()
{printf("%zu\n", sizeof(struct S4));  32
}

圖解演示——

2.2? 為什么存在內存對齊

  • 平臺原因 (移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常;

  • 性能原因:數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要?次訪問。假設?個處理器總是從內存中取8個字節,則地址必須是8的倍數。如果我們能保證將所有的double類型的數據的地址都對齊成8的倍數,那么就可以用?個內存操作來讀或者寫值了。否則,我們可能需要執行兩次內存訪問,因為對象可能被分放在兩個8字節內存塊中;

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

--那該如何做到是設計結構體是,滿足對齊和節省空間呢,對此,我們可以讓占用空間小的成員盡量集中在一起。(減少空間浪費)

struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};int main()
{printf("%zu\n", sizeof(struct S1));  12printf("%zu\n", sizeof(struct S2));  8
}

--看上面的代碼,成員一樣,但是排列順序不同,明顯看到S2更小一點。

2.3? 修改默認對齊數

--#pragma 預處理指令,可以改變默認對齊數,但是一般設置為2的次方數。

#pragma pack(2)//設置默認對?數為2
struct S
{char c1;int i;char c2;
};
#pragma pack()//取消設置的對?數,還原為默認,下次再到別的結構體中就是默認的了
int main()
{//輸出的結果是什么?printf("%d\n", sizeof(struct S));//8return 0;
}

3.? 結構體傳參

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

--很顯然,通過地址來傳參數最好的:

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

4.? 結構體實現位段

4.1? 什么是位段

--位段的聲明和結構十分類似,但有者兩個不同:

  • 位段的成員必須是?int、unsigned int 或signed int?,在C99中位段成員的類型也可以選擇其他類型;
  • 位段的成員名后邊有?個冒號和一個數字;

????????--比如:

struct A
{int _a:  2 ;int _b:  5 ;int _c:  10 ;int _d:  30 ;
};

補充——

--一般習慣在位段成員加上'-' ;

--冒號后面的數字表示:這個成員要占用的比特位的數量;

--A就是?個位段類型。 那位段A所占內存的大小是多少呢?? ?

4.2? 位段的內存分配

  1. 位段的成員可以是?int unsigned int signed int?或者是?char?等類型‘
  2. 位段的空間上是按照需要以4個字節(?int?)或者1個字節(?char?)的方式來開辟的;
  3. 位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程序應該避免使用位段。;
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;//空間是如何開辟的?
}

4.3? 位段的跨平臺問題

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

總結——

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

4.4? 位段的應用

--圖片為網絡協議中,IP數據報的格式,可以看到其中大多屬性只需要幾個bit位就能描述,這里使用位段能夠實現想要的結果,也節省了空間;這樣網絡傳輸的數據報大小也會較小一些,對網絡的暢通是有幫助的。

4.5? 位段使用注意事項

--位段的幾個成員共有同?個字節,導致有些成員的起始位置不是某個字節的起始位置,那么這些位置處是沒有地址的。內存中每個字節分配一個地址,一個字節內部的bit位是沒有地址的。所以不能對位段的成員使用&操作符,這樣就不能使用scanf直接給位段的成員輸入值只能是先輸入放在?個變量中,然后賦值給位段的成員。

struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};int main()
{struct A sa = { 0 };scanf("%d", &sa._b); // 這是錯誤的// 正確的示范int b = 0;scanf("%d", &b);sa._b = b;return 0;
}

舊知識回顧——

#C語言——學習攻略:數據在內存中的存儲--整數在內存中的存儲,大小端字節序和字節序判斷,浮點數在內存中的存儲

#C語言——學習攻略:探索內存函數--memcpy、memmove的使用和模擬實現,memset、memcmp函數的使用

結語:本篇文章到此結束,呈現了自定義--結構體的內容,內涵豐富,大家要多次回顧,如果這篇文章對你的學習有幫助的話,歡迎一起討論學習,你這么帥、這么美給個三連吧~~~

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

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

相關文章

機器學習——TF-IDF算法

TF-IDF(Term Frequency-Inverse Document Frequency)是一種廣泛應用于文本挖掘和信息檢索領域的經典加權算法,主要用于評估一個詞語在文檔集合中的重要程度。其核心思想是:一個詞語在文檔中出現的頻率越高,同時在所有文…

區塊鏈技術原理(9)-什么是以太幣

文章目錄前言什么是加密貨幣?什么是以太幣(ETH)鑄造 ETH燃燒 ETHETH 面額傳輸 ETH查詢 ETH以太幣的經濟模型:發行與流通以太幣與其他代幣的區別以太幣的歷史與市場地位總結前言 以太幣(Ether,簡稱 ETH&…

【Oracle APEX開發小技巧16】交互式網格操作內容根據是否啟用進行隱藏/展示

在日常開發中,有想要根據某一狀態或條件去限制/隱藏對應權限或操作按鈕的情況,于是用簡報模板列表進行展示,并提供以下功能:顯示模板基本信息提供啟用/禁用模板的開關提供編輯模板的入口根據模板狀態顯示不同的操作選項效果展示&a…

AIStarter:全網唯一跨平臺桌面AI管理工具,支持Windows、Mac和Linux一鍵部署

AIStarter作為全網唯一支持Windows、Mac和Linux的桌面AI管理平臺,為開發者提供高效的項目管理、模型插件和工作流共享體驗。最近,熊哥發布了4.1.0版本更新視頻,詳細演示了如何在多平臺上安裝、使用和分享AI項目。本文基于視頻內容&#xff0c…

AP模式/ESP32作為TCP服務端,轉發串口接收的數據給網絡調試助手

此代碼為接收STM32的數據然后直接轉發到網絡調試助手,當有設備連接到esp32軟件熱點時會通過串口發送字符’a’給STM32,當有設備斷開連接時會通過串口發送字符’b’,ESP32的TX:GPIO4, RX:GPIO5ESP32作為TCP服務器地址為192.168.4.1 監聽端口為3333#include <string.h> #in…

kafka 中的Broker 是什么?它在集群中起什么作用?

Kafka中的Broker&#xff1a;集群的核心支柱 在分布式消息系統Apache Kafka中&#xff0c;Broker是構成Kafka集群的核心節點或服務器。 簡單來說&#xff0c;每一個Broker就是運行著Kafka服務的一個實例&#xff0c;多臺Broker共同協作&#xff0c;形成了強大的、可擴展的消息處…

【SOA用于噪聲抑制】光纖DFB激光器中弛豫振蕩噪聲抑制

概述&#xff1a;本章記錄了我們在光纖分布式反饋DFB激光器中使用飽和SOA來降低RIN的工作&#xff0c;以用于低頻傳感器應用。結果表明&#xff0c;放大器的增益動力學允許光纖激光器的弛豫振蕩RO噪聲分量減少30dB。 1 背景到目前為止&#xff0c;我研究了將飽和半導體光放大器…

神經網絡的核心組件解析:從理論到實踐

神經網絡作為深度學習的核心技術&#xff0c;其復雜性常常令人望而卻步。然而&#xff0c;盡管神經網絡的結構、參數和計算過程看似繁瑣&#xff0c;但其核心組件卻是相對簡潔且易于理解的。本文將深入探討神經網絡的四大核心組件——層、模型、損失函數與優化器&#xff0c;并…

Spring Boot項目通過Feign調用三方接口的詳細教程

目錄 一、環境準備 二、啟用Feign客戶端 三、定義Feign客戶端接口 四、定義請求/響應DTO 五、調用Feign客戶端 六、高級配置 1. 添加請求頭&#xff08;如認證&#xff09; 2. 超時配置&#xff08;application.yml&#xff09; 3. 日志配置 七、錯誤處理 自定義錯誤…

ubuntu24.04安裝 bpftool 以及生成 vmlinux.h 文件

文章目錄前言一、apt安裝二、源碼安裝三、生成vmlinux.h參考資料前言 $ cat /etc/os-release PRETTY_NAME"Ubuntu 24.04.2 LTS"$ uname -r 6.14.0-27-generic一、apt安裝 安裝bpftool&#xff1a; $ sudo apt install linux-tools-commonThe following NEW packa…

Pytorch FSDP權重分片保存與合并

注&#xff1a;本文章方法只適用Pytorch FSDP1的模型&#xff0c;且切分策略為SHARDED_STATE_DICT場景。 在使用FSDP訓練模型時&#xff0c;為了節省顯存通常會把模型權重也進行切分&#xff0c;在保存權重時為了加速保存通常每個進程各自保存自己持有的部分權重&#xff0c;避…

IDEA自動生成Mapper、XML和實體文件

1. 引入插件 <build><finalName>demo</finalName><plugins><plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.3.5</version><depe…

單例模式的理解

目錄單例模式1.餓漢式(線程安全)2.懶漢式(通過synchronized修飾獲取實例的方法保證線程安全)3.雙重校驗鎖的方式實現單例模式4.靜態內部類方式實現單例模式【推薦】單例模式 1.餓漢式(線程安全) package 并發的例子.單例模式; // 餓漢式單例模式&#xff08;天然線程安全&…

NLP---IF-IDF案例分析

一案例 - 紅樓夢1首先準備語料庫http://www.dxsxs.com這個網址去下載2 任務一&#xff1a;拆分提取import os import redef split_hongloumeng():# 1. 配置路徑&#xff08;關鍵&#xff1a;根據實際文件位置修改&#xff09; # 腳本所在文件夾&#xff08;自動獲取&#xff0…

LaTeX(排版系統)Texlive(環境)Vscode(編輯器)環境配置與安裝

LaTeX、Texlive 和 Vscode 三者之間的關系&#xff0c;可以把它們理解成語言、工具鏈和編輯器的配合關系。 1.下載Texlive 華為鏡像網站下載 小編這邊下載的是texlive2025.iso最新版的&#xff0c;下載什么版本看自己需求&#xff0c;只要下載后綴未.iso的即可。為避免錯誤&am…

【深入淺出STM32(1)】 GPIO 深度解析:引腳特性、工作模式、速度選型及上下拉電阻詳解

GPIO 深度解析&#xff1a;引腳特性、工作模式、速度選型及上下拉電阻詳解一、GPIO概述二、GPIO的工作模式1、簡述&#xff08;1&#xff09;4種輸入模式&#xff08;2&#xff09;4種輸出模式&#xff08;3&#xff09;4種最大輸出速度2、引腳速度&#xff08;1&#xff09;輸…

第1節 大模型分布式推理基礎與技術體系

前言:為什么分布式推理是大模型時代的核心能力? 當我們談論大模型時,往往首先想到的是訓練階段的千億參數、千卡集群和數月的訓練周期。但對于商業落地而言,推理階段的技術挑戰可能比訓練更復雜。 2025年,某頭部AI公司推出的130B參數模型在單機推理時面臨兩個選擇:要么…

《軟件工程導論》實驗報告一 軟件工程文檔

目 錄 一、實驗目的 二、實驗環境 三、實驗內容與步驟 四、實驗心得 一、實驗目的 1. 理解軟件工程的基本概念&#xff0c;熟悉軟件&#xff0c;軟件生命周期&#xff0c;軟件生存周期過程和軟件生命周期各階段的定義和內容。 2. 了解軟件工程文檔的類別、內容及撰寫軟件工…

基于elk實現分布式日志

1.基本介紹 1.1 什么是分布式日志 在分布式應用中&#xff0c;日志被分散在儲存不同的設備上。如果你管理數十上百臺服務器&#xff0c;你還在使用依次登錄每臺機器的傳統方法查閱日志。這樣是不是感覺很繁瑣和效率低下。所以我們使用集中化的日志管理&#xff0c;分布式日志…

多模態RAG賽題實戰之策略優化--Datawhale AI夏令營

科大訊飛AI大賽&#xff08;多模態RAG方向&#xff09; - Datawhale 項目流程圖 1、升級數據解析方案&#xff1a;從 fitz 到 MinerU PyMuPDF&#xff08;fitz&#xff09;是基于規則的方式提取pdf里面的數據&#xff1b;MinerU是基于深度學習模型通過把PDF內的頁面看成是圖片…