C語言自定義類型:結構體的使用及其內存對齊【超詳細建議點贊收藏】

目錄

  • 1. 結構體類型的聲明
    • 1.1 結構的聲明
    • 1.2 結構體變量的創建和初始化
    • 1.3 結構的特殊聲明---匿名結構體
    • 1.4 結構的自引用
  • 2.結構體內存對齊(重點!!)
    • 2.1 對齊規則
    • 2.2 例題講解
    • 2.3 為什么存在內存對齊?
    • 2.4 修改默認對齊數
  • 3. 結構體傳參

1. 結構體類型的聲明

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

1.1 結構的聲明

在這里插入圖片描述
注意:

  1. 成員列表可以是不同類型的變量;
  2. 成員后一定要有分號;
  3. 花括號后也有一個分號。

例如描述一個學生:

struct Stu
{char name[20];//姓名int age;//年齡char tele[12];//電話char sex[3];//性別
};//分號不能丟

注意:上述代碼沒有創建變量,也沒有初始化,只是聲明了一個結構體類型,就像int,float一樣,只是一種類型。

1.2 結構體變量的創建和初始化

接下來我們可以用結構體類型創建變量

方式1:
聲明類型的同時創建,這是全局變量

struct Stu
{char name[20];//姓名int age;//年齡char tele[12];//電話char sex[3];//性別
}s1,s1;

方式2:
我們也可以在函數內部創建局部變量

# include <stdio.h>struct Stu
{char name[20];//姓名int age;//年齡char tele[12];//電話char sex[3];//性別
};int main()
{struct Stu s3;//s3是局部變量return 0;
}

我們再對結構體變量進行初始化:
方式1:直接初始化

struct Point
{int x;int y;
}p1 = { 2,3 };struct Stu
{char name[20];//姓名int age;//年齡}s1 = { "zhangsan",27 };

方式2:也可以再函數內部進行初始化

struct Stu
{char name[20];//姓名int age;//年齡
};int main()
{struct Stu s1 = { "zhangsan",24 };return 0;
}

當然,也有結構體的嵌套:

struct score
{int n;char ch;
};struct Stu
{char name[20];//姓名int age;//年齡struct score s;//嵌套一個結構體變量
};int main()
{struct Stu s1 = { "zhangsan",24 ,{45,'a'} };return 0;
}

我們也可以將其打印出來:

struct score
{int n;char ch;
};struct Stu
{char name[20];//姓名int age;//年齡struct score s;
};int main()
{struct Stu s1 = { "zhangsan",24 ,{45,'a'} };printf("%s %d %d %c\n", s1.name, s1.age, s1.s.n, s1.s.ch);return 0;
}

打印結果:
在這里插入圖片描述

1.3 結構的特殊聲明—匿名結構體

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

struct 
{char name[20];//姓名int age;//年齡char tele[12];//電話char sex[3];//性別
};

上面的結構在聲明時省略了結構體標簽,我們稱為匿名結構體

注意:匿名結構體只能通過創建全局變量使用一次!!

# include <stdio.h>struct 
{char name[20];//姓名int age;//年齡char tele[12];//電話char sex[3];//性別
}s1;int main()
{return 0;
}

1.4 結構的自引用

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

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. 其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處。
    注意:
    (1) 對齊數=編譯器默認的對齊數與該成員大小的較小值
    (1)VS中默認值為8(注意:其他編譯器是沒有默認對齊數的,對齊數就是自身大小。)

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

  4. 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊的整數倍處。
    結構體的整體大小就是所有最大對齊數的(含嵌套結構體的對齊數)的整數倍。

2.2 例題講解

例1:

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

輸出結果:
在這里插入圖片描述

畫圖解釋:
在這里插入圖片描述

例2:

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

輸出結果:
在這里插入圖片描述

畫圖解釋:
在這里插入圖片描述
例3:

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

輸出結果:
在這里插入圖片描述

畫圖解釋:
在這里插入圖片描述
例4:結構體嵌套問題

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

輸出結果:
在這里插入圖片描述

畫圖解釋:
在這里插入圖片描述

2.3 為什么存在內存對齊?

大部分的參考資料都是這樣說的:

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

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

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

讓占用空間小的成員盡量集中在?起
如例1和例2,s1和s2的成員一模一樣,但兩者所占空間大小卻不同。

2.4 修改默認對齊數

  • #pragma : 預處理指令,可以改變編譯器的默認對齊數。
  • 使用時要引用頭文件<stddef.h>
  • 注意:一般修改的對齊數都為2的n次方。不會修改為1,3,5……或負數。

例如還是用例1來說明:

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

輸出結果:
在這里插入圖片描述
修改之前大小是12個字節,修改后變成了6字節。

3. 結構體傳參

方式1:傳值調用。訪問結構體時用點(.)操作符。

#include <stdio.h>struct S
{int data[1000];int num;
};void print1(struct S ss)
{int i = 0;for (i = 0; i < 3; i++){printf("%d ", ss.data[i]);}printf("%d\n", ss.num);
}int main()
{struct S s = { {1,2,3},200 };print1(s);  //傳值調用,直接傳變量名return 0;
}

方式2:傳址調用。訪問結構體時用箭頭(->)操作符。

#include <stdio.h>struct S
{int data[1000];int num;
};void print2(const struct S* ps)
{int i = 0;for (i = 0; i < 3; i++){printf("%d ", ps->data[i]);}printf("%d\n", ps->num);
}int main()
{struct S s = { {1,2,3},200 };print2(&s); //傳址調用,傳變量名的地址return 0;

輸出結果都為:
在這里插入圖片描述

上面的兩種方式哪個更好呢?
答案是:傳址調用更好。

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

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

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

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

相關文章

OpenGauss數據庫本地搭建并結合內網穿透實現遠程訪問

文章目錄 前言1. Linux 安裝 openGauss2. Linux 安裝cpolar3. 創建openGauss主節點端口號公網地址4. 遠程連接openGauss5. 固定連接TCP公網地址6. 固定地址連接測試 前言 openGauss是一款開源關系型數據庫管理系統&#xff0c;采用木蘭寬松許可證v2發行。openGauss內核深度融合…

云渲染農場服務給力嗎?全天候不間斷服務如何保障?

近年來&#xff0c;云渲染農場以其強大的計算能力和高效的渲染速度而聞名&#xff0c;為各行各業的視覺創作提供了不可或缺的支持。但是&#xff0c;人們普遍關心的是&#xff0c;云渲染農場服務是否真的給力&#xff1f;全天候的服務又是如何保障呢&#xff1f; 實際上&#…

用Python插入頁碼到PDF文檔

頁碼是許多類型文件中的重要內容&#xff0c;它能方便讀者在文檔中的導航。在創建PDF文檔時&#xff0c;添加頁碼對于組織和引用內容特別有用。在本文中&#xff0c;我們將探討如何利用Python程序高效地插入頁碼到PDF文檔中&#xff0c;簡化工作流程并創建出精美、結構合理的PD…

【JavaEE】_synchronized關鍵字——監視器鎖monitor lock

目錄 1. synchronized的特性 2. synchronized的使用 3. Java標準庫中的線程安全類 1. synchronized的特性 &#xff08;1&#xff09;互斥&#xff1a; 前文已經介紹&#xff0c;某個線程執行到某個對象的synchronized中時&#xff0c;其他線程如果也執行到同一個對象&…

2024 Sora來了!“手機Agent智能體”也來了!

近日&#xff0c;Open AI發布了能夠根據文本生成超現實視頻的工具Sora&#xff0c;多款震撼視頻引爆科技圈刷屏&#xff0c;熱度持續發酵占據AI領域話題中心&#xff0c;被認為是AGI實現過程里的重大里程碑事件。新一輪的人工智能浪潮給人類未來的生產和生活方式帶來巨大而深遠…

VPN | 世界那么大,我想“魔法”上網看看,可以嗎?

Hi&#xff0c;大家好&#xff0c;我是半畝花海。世界那么大&#xff0c;我想“魔法”上網看看&#xff0c;可以嗎&#xff1f;隨著網絡的發展與普及&#xff0c;暢游網絡世界已成為人們生活的一部分。它給我們開拓了視野&#xff0c;增長了見聞&#xff0c;豐富了知識&#xf…

FISCO BCOS(二)———配置及使用控制臺

一、前言 FISCO BCOS是由金融區塊鏈合作聯盟&#xff08;深圳&#xff09;與微眾銀行共同發起的開源區塊鏈項目&#xff0c;支持多鏈多賬本&#xff0c;滿足金融行業復雜業務需求。本文將介紹如何在Ubuntu操作系統上使用Linux命令配置FISCO BCOS的控制臺并進行get/set操作。 目…

React 模態框的設計(一)拖動組件的設計

春節終結束了&#xff0c;忙得我頭疼。終于有時間弄自己的東西了。今天來寫一個關于拖動的實例講解。先看效果&#xff1a; 這是一個簡單的組件設計&#xff0c;如果用原生的js設計就很簡單&#xff0c;但在React中有些事件必須要多考慮一些。這是一個系列的文章&#xff0c;…

SpringBoot3整合elasticsearch8

版本 SpringBoot 3.0 Elasticsearch 8.12.1 依賴 我使用的 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> 還可以用&#xff0c;但我沒用…

YOLOv9來咧!

文章目錄 論文:主要內容一、提出使用PGI&#xff08;Programmable Gradient Information&#xff0c;可編程梯度信息&#xff09;來解決信息瓶頸問題和深度監督機制不適合輕量級神經網絡的問題。二、設計了GELAN&#xff08;Generalized ELAN &#xff0c;廣義ELAN&#xff09;…

LLM 模型融合實踐指南:低成本構建高性能語言模型

編者按&#xff1a;隨著大語言模型技術的快速發展&#xff0c;模型融合成為一種低成本但高性能的模型構建新途徑。本文作者 Maxime Labonne 利用 mergekit 庫探索了四種模型融合方法&#xff1a;SLERP、TIES、DARE和passthrough。通過配置示例和案例分析&#xff0c;作者詳細闡…

Ansible playbook 劇本部署WEB NFS rsync sersync(及時監控)架構

ansible playbook劇本介紹&#xff1a; playbook 是ansible用于配置&#xff0c;部署和管理被節點的劇本 由一個或多個模塊組成&#xff0c;完成統一的目的&#xff0c;實現自動化操作 劇本編寫需遵循yaml語法 yaml的三要素&#xff1a; 縮進&#xff1a;兩個字符&#xff0c;默…

【Vue3】toRefs和toRef在reactive中的一些應用

&#x1f497;&#x1f497;&#x1f497;歡迎來到我的博客&#xff0c;你將找到有關如何使用技術解決問題的文章&#xff0c;也會找到某個技術的學習路線。無論你是何種職業&#xff0c;我都希望我的博客對你有所幫助。最后不要忘記訂閱我的博客以獲取最新文章&#xff0c;也歡…

快速上手vue指南

Vue.js 是一款非常流行且易于上手的前端框架&#xff0c;用于構建用戶界面和單頁應用程序&#xff08;SPA&#xff09;。它以其簡潔的API、靈活的組件系統和高效的性能著稱。如果你是初學者&#xff0c;以下是一些關鍵步驟和建議&#xff0c;可以幫助你快速上手 Vue.js。 1. 理…

【Qt】實現 Ctrl + 鼠標滾輪 縮放文本功能

【Qt】實現 Ctrl 鼠標滾輪 縮放文本功能 文章目錄 I - 實現自定義控件II - 完整代碼III - 參考鏈接 I - 實現自定義控件 主要原理 繼承 QTextEdit 或者 QPlainTextEdit 類&#xff0c;重寫滾輪事件 wheelEvent, QTextEdit 和 QPlainTextEdit 中均包含此函數 頭文件 TextEdit…

學習springMVC第二天

REST簡介 REST(Representational State Transfer)&#xff0c;表現形式狀態轉換 傳統風格資源描述形式 http://localhost/user/getById?id1 http://localhost/user/saveUser REST風格描述形式 http://localhost/user/1 http://localhost/user 優點&#xff1a; 隱藏資源的訪問…

C++模板->模板的概念、函數模板基本語法、函數模板注意事項、普通函數與函數模板區別、普通函數與函數模板調用規則、模板的局限性

#include<iostream> using namespace std; //交換兩個整型函數 void swapInt(int& a, int& b) { int temp a; a b; b temp; } //交換兩個浮點型函數 void swapDouble(double& a, double& b) { double temp a; a b; b te…

MATLAB中gtext函數用法

目錄 語法 說明 示例 使用鼠標將文本添加到圖窗 指定字體大小和顏色 在創建后修改文本 gtext函數的功能是使用鼠標將文本添加到圖窗。 語法 gtext(str) gtext(str,Name,Value) t gtext(___) 說明 gtext(str) 在使用鼠標選擇的位置插入文本 str。當將鼠標指針懸停在圖…

Oracle普通用戶啟停JOB報錯ORA 27486權限不足

Oracle普通用戶啟停JOB報錯ORA 27486權限不足 問題與現象原因與對策 問題與現象 應用用戶通過DBMS_SCHEDULER啟停自己的JOB需要的權限&#xff1a; grant execute on dbms_scheduler to appuser;該普通用戶有CREATE JOB的權限。通過DBMS_SCHEDULER停止自己的JOB時&#xff1a…

3個wordpress中文企業主題模板

農業畜牧養殖wordpress主題 簡潔大氣的農業畜牧養殖wordpress主題&#xff0c;農業農村現代化&#xff0c;離不開新農人、新技術。 https://www.jianzhanpress.com/?p3051 老年公寓wordpress主題 淺綠色簡潔實用的老年公寓wordpress主題&#xff0c;適合做養老業務的老年公…