C++:結構體(Structure)

目錄

第一性原理出發:我們要解決什么問題?

定義結構體(Defining Structures)

問題:名字太長怎么辦?

如何定義結構體變量?

結構體的大小(Size of Structures)

初始化結構體(Initialize Structures)

訪問結構體成員(Access Structures)

結構體的指針(Pointers to Structures)

如何通過指針訪問結構體成員?

👣 分析 (*p).width 的由來:

結構體作為函數參數(Structures as Parameters)

為什么結構體很重要?


第一性原理出發:我們要解決什么問題?

第一性原理是從根本出發,不依賴已有抽象來理解一個問題。

問題:我們如何表達復雜的數據?

在 C 語言里,我們已經知道可以用變量存儲數據,比如:

int width = 10;
int height = 5;

這兩個變量表示一個矩形的寬和高。但如果我們有多個矩形,就可能會有一堆變量:

int r1_width = 10, r1_height = 5;
int r2_width = 20, r2_height = 10;

?這會讓代碼變得混亂、不好維護。

💡第一性答案:我們要把相關數據打包成一個“新類型”

我們想要的是:把一個矩形的寬和高“綁定”在一起,像一個單元一樣使用它。

這就引出了結構體(Structure)的概念:

結構體是 C 語言提供的一種機制,它允許我們把多個不同或相同類型的數據組合在一起,形成一個“新的數據類型”。


定義結構體(Defining Structures)

我們可以用 struct 關鍵字定義一個矩形結構體:

struct Rectangle {int width;int height;
};

這段代碼做了什么?

  • 它定義了一個新的類型,叫做 struct Rectangle,可以用它創建變量

  • 這個類型有兩個成員:

    • width:整型,表示矩形的寬

    • height:整型,表示矩形的高

可以把它理解為一個“結構體工廠”,以后我們可以用它制造一個一個“矩形對象”。

問題:名字太長怎么辦?

每次都寫 struct Rectangle 很麻煩,所以我們可以使用 typedef定義一個別名,簡化使用:

typedef struct Rectangle {int width;int height;
} Rectangle;
  • struct Rectangle 是原始名字

  • Rectangle 是你定義的新別名

這樣你以后可以直接寫:

Rectangle r1;
//等價于
struct Rectangle r1;

💡更進一步:定義和別名可以合并寫成匿名結構體

如果你不打算用 struct Rectangle 這種原始名,可以這樣簡寫:

typedef struct {int width;int height;
} Rectangle;

這也是合法的結構體定義方式,結構體沒有“名”,但有了一個別名 Rectangle。?


如何定義結構體變量?

1?? 先定義結構體,再定義變量:

struct Rectangle {int width;int height;
};struct Rectangle r1, r2;

2?? 定義時直接創建變量:

struct Rectangle {int width;int height;
} r1, r2;

?3?? 使用 typedef 簡化后:

typedef struct {int width;int height;
} Rectangle;Rectangle r1, r2;

我們可以這樣用這個結構體定義實際的矩形:

struct Rectangle r1;
r1.width = 10;
r1.height = 5;

這段代碼:

  • 創建了一個結構體變量 r1

  • 給它的 widthheight 賦值

?現在 r1 是一個“真正的矩形”,有自己的寬和高!

我們可以像這樣使用它:

int area = r1.width * r1.height;
printf("Area: %d\n", area);

結構體的大小(Size of Structures)

🔎 問題:一個結構體到底占用多少內存?

我們可以用 sizeof() 運算符來查看:

#include <stdio.h>typedef struct {int width;int height;
} Rectangle;int main() {printf("Size of Rectangle: %lu\n", sizeof(Rectangle));return 0;
}

分析:它的大小是所有成員的大小之和嗎?

直覺上你可能以為是:

  • int width:4 字節

  • int height:4 字節

  • 總共:8 字節

?這個答案大多數時候是對的,但是……

🧠現實中會有對齊(Padding)

有時結構體會包含“填充字節”(padding),為了讓內存對齊(alignment),提高訪問效率。

比如下面這個結構體:

struct Example {char a;int b;
};

你可能以為大小是 1 + 4 = 5 字節,但其實 sizeof(struct Example) 很可能是 8

原因是:

  • char 占 1 字節

  • 為了讓 int 對齊到 4 字節位置,會插入 3 個字節的“空白”

  • 所以實際內存布局是:

字節偏移內容
0a
1~3填充
4~7b

如何減少結構體大小?

把小的字段放在一起,有時可以減少對齊浪費:

struct Compact {char a;char b;int c;
};

這樣會比 char + int 的組合更緊湊一些。?


初始化結構體(Initialize Structures)

有幾種方式可以給結構體變量賦初值。

?方法一:逐個成員賦值

r1.width = 10;
r1.height = 5;

方法二:使用初始化列表

可以直接一次性賦值:

Rectangle r2 = {20, 15};

它會按順序對應結構體中的成員:第一個是 width,第二個是 height。?

??結構體不能直接比較?

if (r1 == r2) {  // 錯誤,結構體不能用 == 比較// ...
}

要比較結構體,得自己比較各個字段:

if (r1.width == r2.width && r1.height == r2.height) {// ...
}

訪問結構體成員(Access Structures)

訪問結構體成員有兩種主要方式:

1. 用點運算符 .(用于結構體變量)

Rectangle r1 = {10, 5};
printf("Width: %d\n", r1.width);
printf("Height: %d\n", r1.height);

. 是成員訪問運算符,用于訪問結構體變量的成員。?

可以理解為:

  • 你有一個“復合數據類型”(結構體)

  • . 告訴編譯器:“我要訪問這個結構里的某一個字段”

2. 用箭頭運算符 ->(用于結構體指針)?

Rectangle *p = &r1;
printf("Width: %d\n", p->width);
printf("Height: %d\n", p->height);

這兩個方式的差別在于:

  • . 用于結構體變量本身

  • -> 用于結構體指針

等價的寫法也可以是:

(*p).width  // 與 p->width 等價,但不常用

結構體的指針(Pointers to Structures)

為什么我們需要結構體指針?

結構體通常存儲多個數據,傳遞或處理時如果結構體很大(有很多字段),傳結構體副本效率低、內存開銷大。

所以就像數組、字符串一樣,我們經常用“結構體指針”來:

  • 節省內存

  • 修改原始結構體的數據

  • 在函數之間高效傳遞結構體

如何聲明和使用結構體指針?

假設我們有這個結構體:

typedef struct {int width;int height;
} Rectangle;Rectangle r1 = {10, 5};

聲明結構體指針,并指向變量 r1:

Rectangle *p = &r1;  // p 是一個指向 Rectangle 的指針
  • p 存儲的是 r1 的地址

  • *p 表示通過指針訪問 r1


如何通過指針訪問結構體成員?

有兩種方式:

方式一:箭頭運算符 ->

p->width    // 訪問 r1 的 width
p->height   // 訪問 r1 的 height

這是最常用的方式,代碼簡潔可讀性好。?

方式二:使用 (*p).member?

👣 分析 (*p).width 的由來:

  1. p 是一個結構體指針,指向 r1

  2. *p 就是解引用,表示 r1 本體

  3. (*p).width 的意思是:

  • 先從指針 p 拿到結構體 r1

  • 再取 r1.width

💡 但注意括號一定要加!

如果寫成 *p.width 是錯誤的,解釋如下:

  • 運算符優先級中 . 的優先級高于 *

  • 所以 *p.width 實際上是 *(p.width),這表示的是 “p 的成員 width 的值,再解引用”,根本不是我們要的意思!

🔁 所以:正確寫法是 (*p).member,等價于:p->member

你可以理解為:p->width ? ≡ ? (*p).width

-> 是語法糖,更方便。


結構體作為函數參數(Structures as Parameters)

在 C/C++ 語言中,結構體可以作為函數的參數傳遞進去。根據不同的傳遞方式,它的行為會有非常大的區別。主要有三種方式:

按值傳遞(Pass by Value)

結構體按值傳遞時,函數接收到的是結構體的一份副本(copy),修改這個副本不會影響原始結構體。

#include <stdio.h>typedef struct {int width;int height;
} Rectangle;void change(Rectangle r) {r.width = 999;
}int main() {Rectangle r1 = {10, 5};change(r1);printf("r1.width = %d\n", r1.width);  // 輸出 10,而不是 999return 0;
}
  • change(r1);r1 拷貝了一份,傳給了函數

  • 函數里 r.width = 999; 修改的是副本,不影響 r1 本體

?按指針傳遞(Pass by Pointer)

把結構體變量的地址傳遞給函數。函數可以通過這個地址訪問并修改結構體的內容。

#include <stdio.h>typedef struct {int width;int height;
} Rectangle;void change(Rectangle *p) {p->width = 999;  // 或 (*p).width = 999;
}int main() {Rectangle r1 = {10, 5};change(&r1);  // 注意傳的是地址printf("r1.width = %d\n", r1.width);  //  輸出 999return 0;
}
  • change(&r1) 傳遞結構體的地址

  • p->width = 999; 直接修改原始結構體變量 r1

  • 指針傳遞效率高,尤其是結構體比較大時非常有用

按引用傳遞(?Pass by Reference)

使用 &,函數接收的是真實結構體的“別名”,不會產生拷貝,函數內部的修改會作用于原始變量

#include <iostream>
using namespace std;struct Rectangle {int width;int height;
};void change(Rectangle &r) {r.width = 999;
}int main() {Rectangle r1 = {10, 5};change(r1);cout << "r1.width: " << r1.width << endl;  // 輸出 999 return 0;
}
  • 省去了拷貝,性能高

  • 可以直接修改原始數據

也可以升級為按常量引用傳遞?(Pass by const Reference)

這是 C++ 最推薦的方式之一,特別是只讀訪問結構體時

void printArea(const Rectangle &r) {cout << "Area: " << r.width * r.height << endl;
}

為什么結構體很重要?

結構體帶來的核心好處是:

  • 組織數據:相關的數據(比如矩形的寬和高)可以組合在一起

  • 可讀性強:代碼語義更清晰

  • 可擴展性強:以后如果要添加更多屬性(比如顏色、邊框樣式),直接在結構體中加字段就行

例如,我們要擴展 Rectangle 加一個顏色:

struct Rectangle {int width;int height;char color[20];
};

結構體 + 函數 = 更強大!

你甚至可以定義一個函數來計算矩形面積,結構體作為參數傳入:

int getArea(struct Rectangle r) {return r.width * r.height;
}int area = getArea(r1);

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

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

相關文章

化學結構式解讀指南:從基礎認知到InDraw智能識別

中文名稱&#xff1a;3-[2-(二甲基氨基)乙基]-1H-吲哚英文名稱&#xff1a;3-[2-(dimethylamino)ethyl]-1H-indole分子式: C12H16N2分子量: 188.2740這是什么結構式&#xff1f;怎么繪制呢&#xff1f;可以用InDraw里的AI圖像識別這個結構式&#xff0c;也可以手動繪圖&#xf…

如何使用一臺電腦adb調試多個Android設備

目錄 一、臨時斷開其中一個設備連接 二、指定調試設備 總結 當我們使用Android調試工具調試多個設備&#xff0c;例如一開始使用adb連接了一臺Android真機進行調試&#xff0c;此時又在Android studio中打開了一個模擬機&#xff0c;此時我們在adb命令窗口中使用adb命令的…

ChatGPT的下一站:從“答案引擎”到“思維教練”

摘要&#xff1a;我們正處在一個“萬物皆可ChatGPT”的時代&#xff0c;但當它淪為最高效的“代碼搬運工”和“作業速成器”時&#xff0c;我們得到的究竟是效率的提升還是思維的退化&#xff1f;本文深入探討一個引人深思的概念——“導師模式”的AI。它不再直接提供答案&…

SpringBoot集成Flyway

SpringBoot集成Flyway_springboot flyway-CSDN博客 Flyway 本質上是一個開源的數據庫遷移工具&#xff0c;它能夠以自動化、可重復且可靠的方式管理數據庫的變更。無論是小型項目還是大型企業級應用&#xff0c;Flyway 都能助力開發者輕松應對數據庫架構的演進。它支持多種數據…

【實時Linux實戰系列】實時圖像處理應用開發

在當今快速發展的技術領域&#xff0c;實時圖像處理應用在眾多領域發揮著至關重要的作用。從自動駕駛汽車、工業自動化檢測到醫療影像診斷&#xff0c;實時圖像處理技術的應用場景無處不在。通過在實時Linux系統中開發圖像處理應用&#xff0c;開發者能夠充分利用Linux的穩定性…

Caterpillar Fungus Optimizer, CFO

核心算法解析1. 算法框架與初始化class EnhancedCFO: def __init__(self, objective_func, dim10, pop_size30, max_iter200, lb-10, ub10):??改進點??&#xff1a;針對傳統優化算法后期易停滯的問題&#xff0c;結合了精英策略、多樣性控制和自適應參數??關鍵特性??&a…

c++設計模式編程練習

一、運用觀察者模式原理編寫鳥類模型運行結果&#xff1a;二、運用簡單工廠模式編寫打怪掉裝備模型運行結果

FastMCP本地構建Server和Clinet交互

1. MCP Server介紹 MCP Server 是實現模型上下文協議&#xff08;MCP&#xff09;的服務器&#xff0c;旨在為 AI 模型提供一個標準化接口&#xff0c;連接外部數據源和工具&#xff0c;例如文件系統、數據庫或 API。 相比之下&#xff0c;在MCP出現前&#xff0c;AI調用工具…

工業企業與清潔生產匹配數據庫(1998-2015年)

1484工業企業與清潔生產匹配數據庫&#xff08;1998-2015年&#xff09;“清潔生產”近年發文趨勢及主題分布數據來源中華人民共和國生態環境部以及中國工業企業數據庫&#xff0c;由數據皮皮俠團隊整理時間跨度1998-2015年數據范圍各工業企業數據指標參考文獻孫博文,鄭世林.環…

第13屆藍橋杯C++青少組中/高級組選拔賽2022年1月22日真題

第13屆藍橋杯C青少組中/高級組選拔賽2022年1月22日真題 更多內容請查看網站&#xff1a;【試卷中心 -----> 藍橋杯----> C ----> 選拔賽】 網站鏈接 青少年軟件編程歷年真題模擬題實時更新 編程題 第 1 題 比大小 題目描述&#xff1a; 給出兩個不同的整數&#…

從0到1學PHP(七):PHP 與 HTML 表單:實現數據交互

目錄一、表單的創建與提交方式1.1 HTML 表單的基本結構1.2 GET 和 POST 提交方式的區別及適用場景二、表單數據的接收與處理2.1 使用\$_GET、\$_POST 超全局變量獲取表單數據2.2 對接收的數據進行驗證三、表單安全處理3.1 防止 XSS 攻擊的方法3.2 防止 CSRF 攻擊的措施一、表單…

Docker compose和Docker-compose的區別

Docker Compose 的兩個命令形式 docker compose&#xff08;空格連接&#xff09;與 docker-compose&#xff08;短橫線連接&#xff09;核心區別如下&#xff1a;一、技術本質docker-compose&#xff08;短橫線&#xff09;獨立可執行文件&#xff1a;早期實現方式&#xff0c…

自定心深凹槽參數檢測裝置及檢測方法 - 激光頻率梳 3D 輪廓檢測

一、引言在機械零件深凹槽檢測中&#xff0c;傳統方法常因定心不準導致檢測誤差。如平臺推表檢測時零件基準面與測量平臺難以精準對齊&#xff0c;三坐標測量需人工找正&#xff0c;效率低且誤差大。激光頻率梳 3D 輪廓檢測雖精度高&#xff0c;但缺乏自定心機制會影響深凹槽軸…

C語言---結構體(格式、用法、嵌套、初始化)、共用體、枚舉類型、typedef類型

目錄 結構體與共用體 1、結構體(struct) (1) 格式與用法 (2) 結構體允許嵌套 (3) 結構體成員初始化 (4) 指針替換變量 (5) 求結構體在內存空間所占字節 2、共用體(union) (1) 格式與概念 (2) 應用 3、枚舉類型(enum) (1) 格式與概念 (2) 應用 4、typedef 類型 結構體與共用…

輻射源定位方法簡述

文章目錄 前言 一、按照信息建模分類 1.1.時間參數 1.1.1.到達時間&#xff08;TOA, Time of Arrival&#xff09;定位 1.1.2.到達時間差&#xff08;TDOA, Time Difference of Arrival&#xff09;定位 1.2.角度參數 1.2.1.到達角度&#xff08;AOA, Angle of Arrival&a…

CamX-設置SceneMode:CONTROL_SCENE_MODE_FACE_PRIORITY不生效問題解決

應用設置CONTROL_SCENE_MODE_FACE_PRIORITY設置不生效 問題&#xff1a;app 代碼 CaptureRequest.Builder captureRequestBuilder mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);builder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTRO…

MFC CChartCtrl編程

重點&#xff1a;創建CChartCtrl控件有2種方式1、直接創建CChartCtrl2、窗體上添加Custom Control&#xff08;切記一定不能是Static Text或者Picture Control&#xff0c;否則無法響應鼠標消息&#xff09;&#xff0c;然后根據ID關聯CChartCtrl控件&#xff0c;初始化代碼如下…

從0到1了解熱部署

熱部署&#xff08;Hot Deployment&#xff09;是軟件開發中一種提高開發效率的技術&#xff0c;指的是在應用程序不停止運行的情況下&#xff0c;動態更新代碼、配置或資源&#xff08;如頁面、圖片等&#xff09;&#xff0c;并讓這些修改立即生效的過程。熱部署主要用于開發…

[12月考試] E

[12月考試] E 題目描述 給定 nnn 個正整數 a1,a2,…,ana_1,a_2,\ldots,a_na1?,a2?,…,an?&#xff0c;小 E 可以進行若干次交換&#xff0c;每一次可以交換兩個相鄰的整數。 求小 E 至少要交換多少次&#xff0c;才可以讓 a1a_1a1? 是 nnn 個數里的最小值&#xff0c;ana_n…

kmp復習,需要多看多練

151. 反轉字符串中的單詞 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:string reverseWords(string s) {int n s.size();int i n - 1;string res;while(i > 0){while(i > 0 && s[i] ) i--;if(i < 0) break;int j i;while(i > …