結構體與共用體-------C語言經典題目(3)

結構體

1.如何定義和使用結構體指針?

1.結構體指針的定義

首先需要定義結構體類型,例如表示學生信息的結構體:

struct Student {char name[50];int age;float score;
};

接著,使用struct 關鍵字和指針符號* 聲明結構體指針:

struct Student *pStudent;  // 聲明指向Student結構體的指針)

2.結構體指針的初始化

1.可以指向現有結構體變量:
struct Student stu1 = {"Alice", 20, 90.5};
struct Student *pStu = &stu1;  // 指針指向stu1的地址)
2.使用malloc 在堆上分配內存:
struct Student *pStu = (struct Student*)malloc(sizeof(struct Student));
if (pStu != NULL) 
{pStu->age = 22;  // 通過指針初始化成員strcpy(pStu->name, "Bob");
}
// 使用后需手動釋放內存
free(pStu);  // 避免內存泄漏)

3.通過指針訪問結構體成員

1.箭頭運算符->
printf("姓名:%s\n", pStu->name);  // 輸出:Bob)
2.解引用與點運算符結合
printf("年齡:%d\n", (*pStu).age);  // 輸出:22)

4.結構體指針的應用場景

1.函數參數傳遞

傳遞指針可以避免復制大型結構體,提升效率:

void updateScore(struct Student *s, float newScore) 
{s->score = newScore;  // 直接修改原結構體成員)
}
2.動態數據結構

用于構建鏈表、樹等動態結構:

struct Node {int data;struct Node *next;  // 指向下一個節點的指針)
};
3.遍歷結構體數組

通過指針操作數組元素:

struct Student stus[3] = {{"Tom", 18, 85.5}, ...};
struct Student *p = stus;
for (int i = 0; i < 3; i++) 
{printf("%s的分數:%.1f\n", p->name, p->score);p++;  // 指針移動到下一個元素)
}

2. 如何將結構體作為函數參數?

主要有三種方式:值傳遞、指針傳遞和數組傳遞。

1.結構體值傳遞

將結構體變量作為參數直接傳遞給函數。函數會復制整個結構體的副本。

優點:函數內部對結構體的修改不會影響外部變量。

缺點:如果結構體較大,會比較耗時間。

// 定義結構體
typedef struct {int id;char name[20];
} Student;// 值傳遞:函數接收結構體副本
void printStudent(Student stu) 
{printf("ID: %d, Name: %s\n", stu.id, stu.name);
}int main() 
{Student tom = {1, "Tom"};printStudent(tom);  // 傳遞結構體變量return 0;
}

2.結構體指針傳遞

將結構體變量的地址作為參數傳遞給函數,函數通過指針操作結構體。

優點:無需復制整個結構體,因此效率高,可在函數中修改原始結構體的值。

缺點:需要使用指針語法->,增加代碼復雜度。

typedef struct {int id;char name[20];
} Student;// 指針傳遞:函數接收結構體指針
void updateStudent(Student *stu, int newId, char *newName) 
{stu->id = newId;        // 通過指針訪問成員(-> 操作符)strcpy(stu->name, newName);
}int main() 
{Student tom = {1, "Tom"};updateStudent(&tom, 2, "Jerry");  // 傳遞結構體地址printf("ID: %d, Name: %s\n", tom.id, tom.name);  // 輸出修改后的值return 0;
}

3.結構體數組傳遞

當函數參數是結構體數組時,可傳遞數組名。

typedef struct {int id;char name[20];
} Student;// 傳遞結構體數組(數組名作為指針)
void printStudents(Student students[], int count) 
{for (int i = 0; i < count; i++) {printf("ID: %d, Name: %s\n", students[i].id, students[i].name);}
}int main() 
{Student class[] = {{1, "Alice"},{2, "Bob"}};printStudents(class, 2);  // 傳遞結構體數組return 0;
}

實際開發中,指針傳遞時最常用的方式,既能避免復制開銷,又能靈活操作結構體內容。

3. 結構體的內存對齊是什么?如何按指定字節對齊?

結構體的內存對齊是指編譯器在為結構體分配內存時,按照一定規則(如成員自身大小、指定對齊字數等)來調整成員的存儲位置,使得每個成員的地址滿足特定對齊要求的過程。

內存對齊的目的是提高內存訪問效率。

1.內存對齊的基本規則

1.成員對齊規則

結構體中的每個成員的偏移量(相對于結構體起始地址)必須是該成員類型大小的整數倍。

例如:

struct Example {char a;    // 大小 1 字節,偏移量 0(0 是 1 的倍數)int b;     // 大小 4 字節,偏移量需是 4 的倍數。由于前一個成員占 1 字節,下一個可用偏移量為 1,不是 4 的倍數,因此編譯器會在 char 后填充 3 字節,使 int 的偏移量為 4
};          // 結構體總大小為 8 字節(4 + 4,最后一個成員大小的整數倍)
2.結構體整體對齊規則

結構體的總大小必須是其成員中最大對齊數的整數倍。

2.按指定字節對齊的方法

1.使用#pragma pack()---------通用方法
// 設置對齊字節數為 n(n 通常為 1、2、4、8、16 等 2 的冪次)
#pragma pack(n)  struct AlignedStruct {char a;    // 偏移量 0(1 的倍數)double b;  // 若 n=8,double 大小 8 字節,偏移量需是 8 的倍數。char 后需填充 7 字節,使 double 偏移量為 8
};             // 總大小為 16 字節(8 的倍數)#pragma pack() // 恢復默認對齊(或用 #pragma pack(pop))
2.GCC 編譯器 __attribute__((aligned(n)))
// 指定結構體按 n 字節對齊(n 需是 2 的冪次)
struct AlignedStruct __attribute__((aligned(8))) {char a;double b;  // 偏移量 8(8 的倍數)
};


共用體

1.共用體和結構體有什么區別?

1.內存分配方式

結構體的每個成員有獨立的內存空間,結構體的總大小是所有成員大小之和。

struct Data {int a;     // 占 4 字節char b;    // 占 1 字節(對齊后可能補 3 字節)double c;  // 占 8 字節
};           // 總大小至少為 4 + 4(對齊) + 8 = 16 字節

共用體所有成員共享一塊內存空間,同一時刻只能有一個成員有效。共用體的總大小為其最大成員的大小。

union Data {int a;    char b;   double c; 
};           // 總大小為 8 字節(取最大成員 `double` 的大小)

2.內存訪問特性

結構體可以同時訪問所有成員,每個成員的值獨立存儲。

struct Data var;
var.a = 10;    // 合法
var.b = 'x';   // 合法
var.c = 3.14;  // 合法

共用體同一時間只能訪問最后一次賦值的成員,訪問其他成員會導致數據錯誤(除非成員類型兼容)

union Data var;
var.a = 10;    // 此時內存存儲 `int` 類型數據
printf("%d\n", var.a);  // 合法,輸出 10
printf("%f\n", var.c);  // 未定義行為(強行將 `int` 當作 `double` 解析)

3.初始化方式

結構體在定義時可以初始化所有成員:

struct Data var = {10, 'x', 3.14};  // 合法

共用體只能初始化第一個成員:

union Data var = {10};  // 合法,初始化 `a`
union Data var = {.c=3.14};  // C99 及以上支持指定成員初始化

4.典型用途

結構體用于存儲同時需要存在的多個相關數據,例如學生信息(姓名、年齡、成績),坐標點(x、y、z)等。

共用體用于節省內存,當數據在不同場景下以不同類型存在時(即互斥使用),例如表示一個變量可能是整數、字符、浮點數等,常見場景包括協議解析、內存共享等。

5.語法關鍵字

結構體用struct 關鍵字來定義:

struct 結構體名 { 成員列表; };

共用體用union 關鍵字來定義:

union 共用體名 { 成員列表; };

2. 共用體在內存中的存儲特點是什么?

1.內存共享空間

共用體的所有成員共享同一塊內存區域,該區域的起始地址相同。

例如:如果要定義一個包含int 和 char 成員的共用體,這兩個成員會從同一地址開始存儲。

union Data {int i;char c;
};
union Data d;  // d.i 和 d.c 共享同一塊內存

2.內存大小由最大成員決定

共用體的內存占用空間等于其最大成員的大小(需考慮內存對齊)。例如:

union Example {char c;        // 1 字節int i;         // 4 字節(假設 int 為 4 字節)double dbl;    // 8 字節
};  // 共用體大小為 8 字節(由 double 決定)

3.同一時間僅存儲一個成員的值

每次只能向共用體的一個成員賦值,后續賦值會覆蓋之前成員的數據。例如:

d.i = 100;     // 此時內存中存儲 int 類型的 100
d.c = 'A';     // 此時內存中存儲 char 類型的 'A'(覆蓋之前的 int 數據)

4.內存對齊規則

共用體的內存對齊方式遵循其成員中對齊要求最嚴格的成員。例如:

若成員包含double ,則共用體的起始地址會按 8 字節對齊。

5.訪問成員的類型安全問題

由于共用體成員共享內存,訪問時必須明確當前存儲的是哪個成員的類型,否則會導致未定義行為,產生段錯誤。

3. 如何用共用體判斷大小端?

利用共用體所有成員共享內存的特性,

1.定義一個包含 int 類型和 char?數組的共用體

2.給 int 成員賦值為1

3.檢查char 數組的第一個字節:

? ? ? ? 小端模式下最低位字節存放在低地址,所以第一個字節是1

? ? ? ? 大端模式下最高位字節存放在低地址,所以第一個字節是0

程序運行后會直接輸出當前系統的字節序類型。

union EndianCheck {int num;char bytes[sizeof(int)];
};int main() 
{union EndianCheck ec;ec.num = 1;if (ec.bytes[0] == 1) {printf("小端模式(Little-Endian)\n");} else {printf("大端模式(Big-Endian)\n");}return 0;
}

位域

1.什么是結構體位域?如何定義和使用位域?

結構體位域是一種允許在結構體中直接定義以位為單位的成員的機制,主要用于優化內存,比如處理寄存器配置。

定義:

位域通過在結構體成員聲明中使用? ? ? ? 類型? ?成員名? :? 位數

struct 結構體名 {類型 成員名1: 位數1;  // 位域成員類型 成員名2: 位數2;// 普通成員(非位域)類型 普通成員名;
};

類型:必須是int 、 unsigned int? 、 signed? int??

位數:表示該成員占用的二級制位數,取值為正整數。

例如:定義一個表示顏色分量(RGB)的結構體,每個分量占5位(共15位,用unsigned int 存儲)

struct Color {unsigned int red:   5;  // 紅色分量,占 5 位(0-31)unsigned int green: 5;  // 綠色分量,占 5 位unsigned int blue:  5;  // 藍色分量,占 5 位unsigned int alpha: 7;  // 透明度,占 7 位(0-127),剩余 1 位未使用
};

使用:

1.聲明變量并賦值:

struct Color c;// 像普通結構體成員一樣賦值
c.red = 20;   // 20 是合法值(20 < 32,未超過 5 位范圍)
c.green = 31; // 最大值 31(2^5 - 1)
c.blue = 0;
c.alpha = 127; // 最大值 127(2^7 - 1)

2.訪問位域成員:

通過結構體變量名 + 成員運算符 .? 直接訪問:

printf("Red: %u\n", c.red);   // 輸出:20
printf("Alpha: %u\n", c.alpha); // 輸出:127

2. 位域的應用場景有哪些?

位域是一種允許在結構體中定義以位為單位的字段的特性,其核心優勢是節省內存空間并直接操作二進制位。

1.硬件寄存器映射

嵌入式設備的寄存器通常由多個獨立位段組成,通過位域可直接讀寫寄存器的特定位段,無需手動進行位運算(如& 、 |):

// 寄存器地址:0x40000000
// 位31-24:保留  
// 位23-16:時鐘頻率選擇(8位)  
// 位15-8:使能標志(1位有效,其余保留)  
// 位7-0:數據長度(8位)  
struct peripheral_reg {unsigned int reserved1:8;   // 位31-24(保留)unsigned int clk_freq:8;    // 位23-16(時鐘頻率)unsigned int enable:1;      // 位15(使能標志,其余7位保留)unsigned int data_len:8;    // 位7-0(數據長度)
};
volatile struct peripheral_reg *reg = (volatile struct peripheral_reg*)0x40000000;// 使用位域操作:
reg->enable = 1;        // 使能設備
reg->clk_freq = 0b1010; // 設置時鐘頻率

不過,如果需要頻繁的對字段進行位運算(異或、移位),位域不如直接操作整數來得高效。

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

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

相關文章

未來教育風向標 | 教育學頂流985高校,華東師范大學《AIGC技術賦能教育數字化轉型的機遇與挑戰》,13所大學deepseek

今天大師兄給大家推薦的是華東師范大學祝智庭教授的《AIGC技術賦能教育數字化轉型的機遇與挑戰》。華東師范大學是一所985學校&#xff0c;在最新的國家學科測評中&#xff0c;軟件工程為A級&#xff0c;教育學為A級。 可以說在AI和教育的結合上是國內top級別的存在。 此講義探…

Java常用正則表達式及使用方法

在 Java 中&#xff0c;Pattern 和 Matcher 類是 java.util.regex 包的核心&#xff0c;用于處理正則表達式。 Pattern 類 Pattern 類表示編譯后的正則表達式&#xff0c;它提供了一種將正則表達式字符串編譯成可執行對象的方式&#xff0c;以便后續用于匹配操作。 常用方法…

車載軟件架構 --- 駕駛員不感知的控制器軟件運行

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 周末洗了一個澡,換了一身衣服,出了門卻不知道去哪兒,不知道去找誰,漫無目的走著,大概這就是成年人最深的孤獨吧! 舊人不知我近況,新人不知我過…

深度學習3.5 圖像分類數據集

%matplotlib inline import torch import torchvision from torch.utils import data from torchvision import transforms from d2l import torch as d2l代碼執行流程圖 #mermaid-svg-WWhBmQvijswiICpI {font-family:"trebuchet ms",verdana,arial,sans-serif;font-…

Kotlin集合全解析:List和Map高頻操作手冊

Kotlin 中 Map 和 List 常用功能總結 List 常用功能 創建 List val immutableList listOf(1, 2, 3) // 不可變列表 val mutableList mutableListOf("a", "b", "c") // 可變列表 val emptyList emptyList<String>() // 空列表基本…

Yocto項目實戰教程-第7章定制鏡像菜譜與內核菜譜-7.2小節-定制應用程序

&#x1f50d; B站相應的視頻教程&#xff1a; &#x1f4cc; Yocto項目實戰教程-第7章-定制鏡像菜譜與內核菜譜 記得三連&#xff0c;標為原始粉絲,感謝大神支持。 在嵌入式Linux系統開發中&#xff0c;定制專屬應用程序往往是最貼近產品交付的那一環。而Yocto項目&#xff0c…

【圖像輪廓特征查找】圖像處理(OpenCV) -part8

17 圖像輪廓特征查找 圖像輪廓特征查找其實就是他的外接輪廓。 應用&#xff1a; 圖像分割 形狀分析 物體檢測與識別 根據輪廓點進行&#xff0c;所以要先找到輪廓。 先灰度化、二值化。目標物體白色&#xff0c;非目標物體黑色&#xff0c;選擇合適的兒值化方式。 有了輪…

C# 的 字符串插值($) 和 逐字字符串(@) 功能

這段代碼使用了 C# 的 字符串插值&#xff08;$&#xff09; 和 逐字字符串&#xff08;&#xff09; 功能&#xff0c;并在 SQL 語句中動態拼接變量。下面詳細解釋它們的用法&#xff1a; 1. $&#xff08;字符串插值&#xff09; $ 是 C# 的 字符串插值 符號&#xff0c;允許…

mockMvc構建web單元測試學習筆記

web應用本來需要依靠tomcat這個環境運行 現在用mockMvc是為了模擬這個web環境&#xff0c;簡化測試 什么是mock(模擬) 模擬對象---mock object是以可控方式模擬真實對象行為的假對象&#xff0c;通過模擬輸入數據&#xff0c;驗證程序達到預期結果 為什么使用mock對象 因為…

6.7.圖的深度優先遍歷(英文縮寫DFS)

樹是特殊的圖&#xff0c;沒有回路的圖就是樹 BFS與DFS的區別在于&#xff0c;BFS使用了隊列&#xff0c;DFS使用了棧 一.深度優先遍歷&#xff1a; 1.樹的深度優先遍歷&#xff1a; 樹的深度優先遍歷分為先根遍歷和后根遍歷。 以樹的先根遍歷為例&#xff1a; 上述圖片里…

VOS3000內存滿了怎么刪除,錄音格式如何轉換呢

一、清理VOS3000內存&#xff08;刪除舊錄音文件&#xff09; 定位錄音存儲目錄 通常錄音文件存儲在以下路徑&#xff08;以實際配置為準&#xff09;&#xff1a; bash 復制 下載 /usr/local/vos/record # 默認錄音目錄 /var/log/vos/logs # 系統日志目錄&#xff08;…

【圖問答】DeepSeek-VL 論文閱讀筆記

《DeepSeek-VL: Towards Real-World Vision-Language Understanding》 1. 摘要/引言 基于圖片問答&#xff08;Visual Question Answering&#xff0c;VQA&#xff09;的任務 2. 模型結構 和 三段式訓練 1&#xff09;使用 SigLIP 和 SAM 作為混合的vision encoder&#xf…

MATLAB - 模型預測控制(MPC)使用 ADMM 求解器四分之一汽車懸架懸掛系統動力學控制

系列文章目錄 目錄 系列文章目錄 前言 一、四分車懸架模型 二、道路干擾剖面 三、設計模型預測控制器 四、設置優化求解器 五、輔助函數 前言 本例展示了如何為四分之一汽車懸架系統設計模型預測控制器 (MPC)&#xff0c;采用乘法交替方向法 (ADMM) 求解器來控制主動懸架…

基于多模態融合算法的航空武器毀傷評估技術方案

基于多模態融合算法的航空武器毀傷評估技術方案 1. 引言 航空武器毀傷評估(Damage Assessment, DA)是現代戰爭中的關鍵環節,直接影響后續作戰決策。傳統的人工評估方式效率低、主觀性強,且在高強度戰場環境下難以實時完成。因此,本研究提出一種基于多模態融合算法的自動…

LeetCode算法題(Go語言實現)_49

題目 給定整數數組 nums 和整數 k&#xff0c;請返回數組中第 k 個最大的元素。 請注意&#xff0c;你需要找的是數組排序后的第 k 個最大的元素&#xff0c;而不是第 k 個不同的元素。 你必須設計并實現時間復雜度為 O(n) 的算法解決此問題。 一、代碼實現&#xff08;快速選擇…

【HCIA】簡易的兩個VLAN分別使用DHCP分配IP

前言 之前我們通過 靜態ip地址實現了Vlan間通信 &#xff0c;現在我們添加一個常用的DHCP功能。 文章目錄 前言1. 配置交換機2. 接口模式3. 全局模式后記修改記錄 1. 配置交換機 首先&#xff0c;使用DHCP&#xff0c;需要先啟動DHCP服務&#xff1a; [Huawei]dhcp enable I…

【技術派后端篇】技術派通用敏感詞替換:原理、實現與應用

在當今互聯網環境下&#xff0c;數據脫敏對于國內的互聯網企業而言已經成為一項標配。這不僅是為了滿足合規性要求&#xff0c;更是保障用戶信息安全和企業聲譽的重要舉措。本文將深入探討技術派中實現數據脫敏的關鍵技術——通用敏感詞替換&#xff0c;從算法原理到具體實現&a…

Android RK356X TVSettings USB調試開關

Android RK356X TVSettings USB調試開關 平臺概述操作-打開USB調試實現源碼補充說明 平臺 RK3568 Android 11 概述 RK3568 是瑞芯微&#xff08;Rockchip&#xff09;推出的一款高性能處理器&#xff0c;支持 USB OTG&#xff08;On-The-Go&#xff09;和 USB Host 功能。US…

Microsoft Edge for linux debian

下載地址 https://www.microsoft.com/en-us/edge/download?formMA13FJ 安裝 # 下載安裝包 wget https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/microsoft-edge-stable_135.0.3179.85-1_amd64.deb?brandM102 # 安裝 sudo dpkg -i microsoft…

typedef MVS_API CLISTDEF0IDX(ViewScore, IIndex) ViewScoreArr;

查找 MVS_API 定義 我們沒有在 List.h 文件中找到 MVS_API 的定義。MVS_API 很可能在 MVS 庫的其他地方定義。一般來說&#xff0c;MVS_API 是控制 OpenMVS 庫導入導出的宏&#xff0c;通常會出現在 MVS 的頭文件中。為了回答這個問題&#xff0c;我可以提供 MVS 代碼中常見的…