第19講:自定義類型:結構體

目錄

  • 1.結構體類型的聲明
    • 1.1 結構體回顧
      • 1.1.1 結構的聲明
    • 特殊的結構聲明
    • 1.3 結構的?引?
  • 2. 結構體內存的對齊
    • 2.2 為什么存在內存對??
    • 2.3 修改默認對?數
  • 3. 結構體傳參
  • 4. 結構體實現位段
    • 4.1 什么是位段
    • 4.2 位段的內存分配
    • 4.3 位段的跨平臺問題
    • 4.5 位段使?的注意事項

正文開始

1.結構體類型的聲明

前面我們在學習操作符的時候,已經學習了結構體的知識,這里稍微復習一下。

1.1 結構體回顧

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

1.1.1 結構的聲明

struct tag
{member-list;
}variable-list;

例如描述一個學生:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct Stu
{char name[20];//名字int age;//年齡char sex[5];//性別char id[20];//學號
};int main()
{//按照結構體成員的初始化struct Stu s = { "張三", 20, "男", "20230818001" };printf("name:%s\n", s.name);printf("age:%d\n", s.age);printf("sex:%s\n", s.sex);printf("id:%s\n", s.id);//按照指定的順序初始化struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex = "女" };printf("name:%s\n", s2.name);printf("age:%d\n", s2.age);printf("id:%s\n", s2.id);printf("sex:%s\n", s2.sex);return 0;
}

特殊的結構聲明

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

//匿名結構體類型
struct
{int a;char b;float c;
}x;
struct
{int a;char b;float c;
}a[20], *p;

上?的兩個結構在聲明的時候省略掉了結構體標簽(tag)。那么問題來了?

//在上?代碼的基礎上,下?的代碼合法嗎?
p = &x;

警告:
編譯器會把上?的兩個聲明當成完全不同的兩個類型,所以是?法的。匿名的結構體類型,如果沒有對結構體類型重命名的話,基本上只能使??次。

1.3 結構的?引?

在結構中包含?個類型為該結構本?的成員是否可以呢?
?如,定義?個鏈表的節點:

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

上述代碼正確嗎?如果正確,那 sizeof(struct Node) 是多少?
仔細分析,其實是不?的,因為?個結構體中再包含?個同類型的結構體變量,這樣結構體變量的??就會?窮的?,是不合理的。正確的?引??式:

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

在結構體?引?使?的過程中,夾雜了 typedef 對匿名結構體類型重命名,也容易引?問題,看看下?的代碼,可?嗎?

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

答案是不?的,因為Node是對前?的匿名結構體類型的重命名產?的,但是在匿名結構體內部提前使?Node類型來創建成員變量,這是不?的。
解決?案如下:定義結構體不要使?匿名結構體了

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

2. 結構體內存的對齊

我們已經掌握了結構體的基本使?了。
現在我們深?討論?個問題:計算結構體的??。
這也是?個特別熱?的考點: 結構體內存對?
2.1 對?規則
?先得掌握結構體的對?規則:

  1. 結構體的第?個成員對?到和結構體變量起始位置偏移量為0的地址處
  2. 其他成員變量要對?到某個數字(對?數)的整數倍的地址處。
    對?數 = 編譯器默認的?個對?數 與 該成員變量??的較?值。
  • VS 中默認的值為 8
  • Linux中 gcc 沒有默認對?數,對?數就是成員??的??
  1. 結構體總??為最?對?數(結構體中每個成員變量都有?個對?數,所有對?數中最?的)的
    整數倍。
  2. 如果嵌套了結構體的情況,嵌套的結構體成員對?到??的成員中最?對?數的整數倍處,結構
    體的整體??就是所有最?對?數(含嵌套結構體中成員的對?數)的整數倍。
//練習1
struct S1
{char c1;int i;char c2;
};
printf("%d\n", sizeof(struct S1));
//練習2
struct S2
{char c1;char c2;int i;
};
printf("%d\n", sizeof(struct S2));
//練習3
struct S3
{double d;char c;int i;
};
printf("%d\n", sizeof(struct S3));
//練習4-結構體嵌套問題
struct S4
{char c1;struct S3 s3;double d;
};
printf("%d\n", sizeof(struct S4));

2.2 為什么存在內存對??

?部分的參考資料都是這樣說的:
1. 平臺原因 (移植原因):
不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常。
2. 性能原因:
數據結構(尤其是棧)應該盡可能地在?然邊界上對?。原因在于,為了訪問未對?的內存,處理器需要作兩次內存訪問;?對?的內存訪問僅需要?次訪問。假設?個處理器總是從內存中取8個字節,則地址必須是8的倍數。如果我們能保證將所有的double類型的數據的地址都對?成8的倍數,那么就可以
??個內存操作來讀或者寫值了。否則,我們可能需要執?兩次內存訪問,因為對象可能被分放在兩個8字節內存塊中。
總體來說:結構體的內存對?是拿空間來換取時間的做法。
那在設計結構體的時候,我們既要滿?對?,?要節省空間,如何做到:
讓占?空間?的成員盡量集中在?起

//例如:
struct S1
{char c1;int i;char c2;
};struct S2
{char c1;char c2;int i;
};

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

2.3 修改默認對?數

#pragma 這個預處理指令,可以改變編譯器的默認對?數。

#include <stdio.h>
#pragma pack(1)//設置默認對?數為1
struct S
{char c1;int i;char c2;
};
#pragma pack()//取消設置的對?數,還原為默認
int main()
{//輸出的結果是什么?printf("%d\n", sizeof(struct S));return 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;
}

上?的 print1 和 print2 函數哪個好些?
答案是:?選print2函數。
原因:函數傳參的時候,參數是需要壓棧,會有時間和空間上的系統開銷。
如果傳遞?個結構體對象的時候,結構體過?,參數壓棧的的系統開銷?較?,所以會導致性能的下降。
結論:結構體傳參的時候,要傳結構體的地址。

4. 結構體實現位段

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

4.1 什么是位段

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

  1. 位段的成員必須是 int、unsigned int 或signed int ,在C99中位段成員的類型也可以選擇其他類型。
  2. 位段的成員名后邊有?個冒號和?個數字。
    ?如:
struct A
{int _a:2;int _b:5;int _c:10;int _d:30;
};

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

printf("%d\n", sizeof(struct 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;
};
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;
}

完。

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

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

相關文章

梳理 JavaScript 中空數組調用 every方法返回true 帶來驚訝的問題

前言 人生總是在意外之中. 情況大概是這樣的. 前兩天版本上線以后, 無意中發現了一個bug, 雖然不是很大, 為了不讓用戶使用時感覺到問題. 還是對著一個小小的bug進行了修復, 并重新在上線一次, 雖然問題不大, 但帶來的時間成本還是存在的. 以及上線后用戶體驗并不是很好. 問題…

JVM學習-垃圾收集器(二)

Serial回收器&#xff1a;串行回收 Serial收集器是最基本、歷史最悠久的收集器JDK1.3之前新生代唯一的選擇Hotpot中Client模式下的默認新生代垃圾收集器采用復制算法&#xff0c;串行回收“Stop-the-world”機制的方式執行內存回收除了年輕代之外&#xff0c;Serial收集器還提…

TG-5006CG溫補晶振在WiFi6無線路由器模塊的應用

WiFi6無線路由器是采用了wiFi6技術的無線網絡設備&#xff0c;旨在為家庭、辦公室或其他場所提供高速、穩定的無線網絡連接。它不僅能實現更高的數據傳輸速率和更低的延遲&#xff0c;還提供了更先進的加密和安全措施&#xff0c;確保用戶數據安全。為了支持這些高級功能&#…

深入 Rust 標準庫,Rust標準庫源代碼系統分析

系列文章目錄 送書第一期 《用戶畫像&#xff1a;平臺構建與業務實踐》 送書活動之抽獎工具的打造 《獲取博客評論用戶抽取幸運中獎者》 送書第二期 《Spring Cloud Alibaba核心技術與實戰案例》 送書第三期 《深入淺出Java虛擬機》 送書第四期 《AI時代項目經理成長之道》 …

Elasticsearch集群部署以及認證配置

文檔地址&#xff1a; 官網文檔地址&#xff1a; https://www.elastic.co/guide/index.html rpm包/源碼下載地址&#xff1a;https://www.elastic.co/cn/downloads 源碼安裝-環境準備&#xff1a; node-01 192.168.95.174 node-02 192.168.95.173 node-03 …

關于Mysql基本概念的理解

系列文章 關于時間復雜度o(1), o(n), o(logn), o(nlogn)的理解 關于HashMap的哈希碰撞、拉鏈法和key的哈希函數設計 關于JVM內存模型和堆內存模型的理解 關于代理模式的理解 關于Mysql基本概念的理解 關于軟件設計模式的理解 文章目錄 前言一、事務隔離級別二、存儲引擎1.…

【Python爬蟲】案例_斗魚

聲明&#xff1a;案例只用于學習&#xff0c;不得惡意使用 要求&#xff1a;獲取直播間標題、類型、主播、熱度&#xff0c;并實現翻頁 定位隨著網站更新可能不會實現&#xff0c;請自行更改 from selenium import webdriver from selenium.webdriver.chrome.options import…

【uniapp】CSS實現多行文本展開收起的文字環繞效果

1. 效果圖 收起狀態 展開狀態 2. 代碼實現 <view class"word-wrap" id"descriptionTxt"><view class"fold-text" v-if"isFold"><text class"fold-btn" click"changFold">全文</text&g…

【網絡安全】Linux 應急響應-溯源-系統日志排查知識點

Linux 應急響應-溯源-系統日志排查知識點匯總 1. 查看當前已經登錄到系統的用戶 (w 命令) w2. 查看所有用戶最近一次登錄 (lastlog 命令) lastlog lastlog | grep -v "Never logged in"3. 查看歷史登錄用戶以及登錄失敗的用戶 (last 和 lastb 命令) last lastb4. …

使用docker完整搭建前后端分離項目

1、docker的優勢&#xff0c;為啥用docker 2、docker的核心概念 鏡像【Image】- 只讀模板 容器【Container】- 運行鏡像的一個外殼&#xff0c;相當于一個獨立的虛擬機 倉庫【repository】- 鏡像的管理工具&#xff0c;可公開&#xff0c;可私有&#xff1b;類似git倉庫 3、c…

【前端】js通過元素屬性獲取元素

【前端】js通過元素屬性獲取元素 <div for"hc_opportunity_config">aaaaa</div>//通過屬性獲取元素document.querySelector([for"hc_opportunity_config"]) document.querySelector([屬性"屬性值"])

操作教程|通過DataEase開源BI工具對接金山多維表格

前言 金山多維表格是企業數據處理分析經常會用到的一款數據表格工具&#xff0c;它能夠將企業數據以統一的列格式整齊地匯總至其中。DataEase開源數據可視化分析工具可以與金山多維表格對接&#xff0c;方便企業更加快捷地以金山多維表格為數據源&#xff0c;制作出可以實時更…

包拯斷案 | MySQL5.7替換路上踩過的坑 一鍵get解決辦法@還故障一個真相

提問&#xff1a;作為DBA運維的你&#xff0c;是否有過這些煩惱 1、業務系統進行替換投產時&#xff0c;發現數據庫回放并行度低 2、雖然2個數據庫集群使用同一份數據&#xff0c;卻在關閉雙一后&#xff0c;二級從庫的回放效率依舊緩慢&#xff0c;不知是什么原因&#xff1f…

機器人開源項目分享,助力一戶一機器人

最初&#xff0c;因隋煬帝思念心切&#xff0c;命工匠按照柳抃的形象制作了木偶機器人&#xff0c;被認為是歷史上最早的機器人之一。這些木偶機器人通過精巧設計的機關&#xff0c;能夠執行坐、起、拜、伏等動作。 如今&#xff0c;隨著科技的發展&#xff0c;機器人已經廣泛…

從ES5邁向ES6:探索 JavaScript 新增聲明命令與解構賦值的魅力

個人主頁&#xff1a;學習前端的小z 個人專欄&#xff1a;JavaScript 精粹 本專欄旨在分享記錄每日學習的前端知識和學習筆記的歸納總結&#xff0c;歡迎大家在評論區交流討論&#xff01; ES5、ES6介紹 文章目錄 &#x1f4af;聲明命令 let、const&#x1f35f;1 let聲明符&a…

Linux磁盤初始化與fstab文件更新

環境&#xff1a; Redhat 7.9 本文操作&#xff1a; >>給disk設置分區 &#xff08;fdisk&#xff09; >>給disk設置file system格式 (mkfs ) >>創建路徑&#xff0c;并將disk mount上(mkdir和mount ) >>修改fstab文件 初始化Disk 初始化前&#xff…

Vue組件通信 - 組件傳值 / 什么是組件

1.什么是組件通信&#xff1a; 組件&#xff08;.vue&#xff09;通過某種方式來傳遞信息以達到某個目的 2.組件通信可以解決什么問題&#xff1a; 每個組件之間都有獨立的作用域&#xff0c;組件間的數據是無法共享的&#xff0c;但在實際開發中我們常常需要讓組件之間共享…

【計算機網絡原理】對傳輸層TCP協議的重點知識的總結

?????? write in front ??????? ?????????大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之處請多多諒解&#xff0c;讓我們一起共同進步????? . ?? ?xiaoxie?????????—CSDN博客 本文由xiaoxie????????? 原創 CSDN 如…

Spring Bean Map漫游:遍歷背后的生命周期奧秘

1. 引言 在Spring框架中&#xff0c;Bean的生命周期是一個復雜而精妙的過程。其中&#xff0c;遍歷存儲Bean實例的Map&#xff08;通常是DefaultSingletonBeanRegistry中的singletonObjects&#xff09;是這一過程中的重要環節。理解這個遍歷過程以及它在Bean生命周期中的作用…

桌面文件不見了怎么恢復?五種方法解決文件恢復難題,建議收藏

不小心誤刪除了桌面文件&#xff0c;導致文件丟失。事實上誤刪的文件并沒有被永久刪除&#xff0c;而是被移動到了回收站中&#xff0c;可以恢復這些文件。本文將分享多種方法&#xff0c;具體步驟如下。 方法一&#xff1a;從回收站中恢復 大多數操作系統都有回收站或垃圾桶的…