結構體指針:使用結構體指針訪問和修改結構體成員。

知識點

  • 結構體指針

    • Employee *p; 保存結構體的地址;

    • p->member 用箭頭運算符訪問或修改成員。

  • 數組與指針

    • Employee *emps = malloc(N * sizeof *emps); 動態創建結構體數組;

    • p < emps + Np++ 配合遍歷。

  • scanf 與數組退化

    • p->namechar name[50] 的首地址,無需 &

    • %49s 限制最大讀取字符數,防止溢出。

  • 函數參數傳遞

    • give_raise(Employee *e, ...) 傳入指針,不拷貝整個結構體;

    • 在函數內部用 -> 修改原變量。

  • 動態內存管理

    • malloc 申請、free 釋放,避免內存泄露。

通過本練習,你將深入理解如何用結構體指針靈活高效地訪問和修改結構體成員。

題目描述
本題要求你熟練使用 結構體指針 來訪問和修改結構體成員,并將其與數組和動態內存結合:

  1. 定義一個 Employee 結構體,包含員工姓名、工號和工資;

  2. main 中使用 malloc 動態分配一個 Employee 數組,長度為 3;

  3. 通過 結構體指針箭頭運算符->)讀取用戶輸入并打印初始信息;

  4. 實現函數 void give_raise(Employee *e, double pct);,通過傳入結構體指針給指定員工加薪;

  5. main 中用指針遍歷全體員工,統一加薪 10%,然后再次打印更新后的信息;

  6. 最后釋放動態內存并退出。

參考代碼:

#include <stdio.h>
#include <stdlib.h>#define N 3
#define NAME_LEN 50//1.結構體定義:保存員工信息
typedef struct
{char name[NAME_LEN];//員工姓名int id;//員工號double salary;//工資
} Employee;/**給單個員工加薪*@param e 指向Employee的指針*@param pct 加薪百分比,列入加10表示加10%*/
void give_raise(Employee *e ,double pct)
{//e->salary等價于(*e).salarye->salary *=(1.0 + pct / 100.0);
}int main(void)
{Employee *emps = malloc(N * sizeof *emps);if(!emps){fprintf(stderr,"內存分配失敗!\n");return EXIT_FAILURE;}//2.讀取初始信息for(int i = 0;i<N;i++){Employee *p = &emps[i];//指向第i個結構體printf("請輸入員工%d的 姓名 工號 工資:",i+1);//p->name自動退化為char*if(scanf("%49s %d %lf",p->name,&p->id,&p->salary) != 3){fprintf(stderr,"輸入格式錯誤!\n");free(emps);return EXIT_FAILURE;}}//3.打印初始信息printf("\n======初始員工信息==========\n");for(Employee *p = emps; p < emps+N; p++){//p是Employee*,用->訪問成員printf("姓名:%-10s  工號:%4d  工資%.2f\n",p->name,p->id,p->salary);}//4.為所有員工加薪10%for(Employee *p = emps; p < emps + N ; p++){give_raise(p,10.0);}//5.打印加薪后的信息printf("\n=======加薪后員工信息(10%)=========\n");for(Employee *p = emps ; p < emps + N;p++){printf("姓名:%-10s  工號:%4d  工資%.2f\n",p->name,p->id,p->salary);}free(emps);return EXIT_SUCCESS;
}

代碼逐行分析

  1. 結構體定義

    typedef struct { char name[NAME_LEN]; int id; double salary; } Employee;

    • name 是字符數組,存放 C 字符串;

    • id 是員工工號;

    • salary 是工資。

  2. 動態分配

    Employee *emps = malloc(N * sizeof *emps);

    • malloc 申請 NEmployee 大小的連續內存;

    • sizeof *emps 等同 sizeof(Employee)

    • 若返回 NULL,則分配失敗。

  3. 讀取輸入

    Employee *p = &emps[i]; scanf("%49s %d %lf", p->name, &p->id, &p->salary);

    • p = &emps[i]p 指向第 i 個結構體;

    • p->name(無 &):數組名在表達式中退化為 char *

    • &p->id&p->salary:取基本類型變量的地址。

  4. 打印初始信息

    for (Employee *p = emps; p < emps + N; p++) { printf("%s %d %.2f\n", p->name, p->id, p->salary); }

    • 指針 pemps(首地址)向后移動,直到末尾。

  5. 加薪函數

    void give_raise(Employee *e, double pct) { e->salary *= (1 + pct/100); }

    • e->salary 等價 (*e).salary

    • 直接修改了原內存中的 salary

  6. 加薪后打印
    同上,只是數據已被 give_raise 更新。

  7. 釋放內存

    free(emps);

Employee *emps = malloc(N * sizeof *emps);

這一行的目的是在堆上動態分配一塊足夠存放 NEmployee 結構體的連續內存,并讓指針 emps 指向它。分解來看:

?sizeof(Employee *) ——也就是指針本身的大小,通常是 8 字節,表示“*emps 的類型大小”,即 sizeof(Employee)。)

  1. Employee *emps
    聲明了一個指針變量 emps,它將用來保存那塊新分配內存的起始地址。

  2. malloc(...)
    從堆上申請一段未初始化的內存,返回一個 void *,隨后被賦值給 emps

  3. N * sizeof *emps

    • *emps 的類型是 Employee,所以 sizeof *emps 等價于 sizeof(Employee)——也就是一個員工記錄所占的字節數。

    • 將它乘以 N,就得到存放 NEmployee 結構體所需的總字節數。

  4. 賦值給 emps
    malloc 返回的 void * 自動轉為 Employee *(在 C 中不需要顯式 cast),于是 emps 就指向了這塊能容下 NEmployee 的內存。

之后你就可以像操作數組一樣,用 emps[0]emps[N-1] 來讀寫這些動態分配的 Employee 結構了。記得在最后用 free(emps); 釋放這段內存,避免泄露。

for (Employee *p = emps;   p < emps + N;   p++) {/* 循環體:用 p->… 訪問或修改當前 Employee */
}
  1. 初始化Employee *p = emps;

    • 聲明了一個指針變量 p,類型是 “指向 Employee 的指針” (Employee *)。

    • 把它初始化為 emps,也就是指向剛剛用 malloc 分配的結構體數組的第 0 個元素(emps[0])的地址。

  2. 循環條件p < emps + N

    • emps 是數組首地址,emps + N 是“跳過 N 個 Employee 大小的字節”后的位置,也就是數組末尾之后的地址。

    • 只要 p 指向的地址 嚴格小于 emps + N(即還沒走到數組末尾后),就繼續執行循環體。

    • 這樣保證 p 會依次指向 emps[0]emps[1]emps[N-1],不會越界。

  3. 迭代表達式p++

    • 這是指針算術,每執行一次 p++p 都會向后移動 一個 Employee 對象的大小,等價于 p = p + 1;

    • 所以第一次循環 p==emps(第 0 個),第二次 p==emps+1(第 1 個),……,直到 p==emps+N-1(第 N-1 個)。


整體流程

  • 第一步p = emps; 指向第一個員工結構。

  • 檢查p < emps + N ? 對于 N=3,就檢查 p < emps+3,當 pemps+2 時依然進入;當 p 自增到 emps+3 時條件不滿足,循環結束。

  • 循環體:在 { … } 中,你可以寫 printf("%s", p->name);give_raise(p, 10);p->member 就是訪問當前 Employee 的成員。

  • 迭代:每次循環結束后執行 p++,跳到下一個元素。

這種“用指針當下標” 的寫法在處理動態分配的數組、或者需要同時傳遞首地址和尾后指針(empsemps+N)時特別方便,也更貼近 C 底層對內存的操作方式。

Employee *p = &emps[i];
  • emps 是什么?

    • 在前面我們用 mallocEmployee emps[N]; 得到了一塊連續的內存,里面按順序存放了 N 個 Employee 結構體對象。

    • emps 在表達式里會退化為指向第 0 個元素的指針,類型是 Employee *

  • emps[i] 得到第 i 個結構體

    • 通過數組下標 iemps[i] 就是第 iEmployee 變量,類型是 Employee

  • &emps[i] 取出它的地址

    • 前面加上取地址符 &&emps[i] 的類型就是 Employee *,表示“指向第 i 個結構體”的指針。

  • 把地址賦給 p

    • 聲明 Employee *p,就是一個可以保存 Employee 對象地址的指針變量。

    • p = &emps[i]; 后,p 就指向了 emps 數組中的第 i 個元素。

  • 后續怎么用?

    • 既然 p 指向了那塊內存,就可以用箭頭運算符訪問或修改它的成員:

    • p->id     = 1234;
      printf("%s\n", p->name);
      

      這等價于對 emps[i] 本身做操作:

    • emps[i].id   = 1234;
      printf("%s\n", emps[i].name);
      

      總結

    • Employee *p 是一個指向 Employee 的指針;

    • &emps[i] 是取得數組中第 i 個結構體的地址;

    • 把它們結合,就能“通過指針”來訪問或修改 emps[i]

if (scanf("%49s %d %lf", p->name, &p->id, &p->salary) != 3)…
  1. scanf 的格式串

    • %49s

      • 讀入一個不含空白(空格、Tab、換行)的字符串,最多讀 49 個字符,自動在第 50 個位置寫入 '\0'

      • 這樣保證不會超出 p->name 的緩沖區(char name[50])。

    • %d:讀入一個十進制整數,存到后面對應的 int * 地址。

    • %lf:讀入一個雙精度浮點數(double),存到對應的 double * 地址。

  2. 參數列表

    • p->name

      • pEmployee *p->name 就是其內部 char name[50] 數組名,退化成 char *,正好匹配 %s

      • 不需要再寫 &p->name,因為數組名已經是地址。

    • &p->id

      • p->id 是一個 int%d 需要 int *,所以要取地址。

    • &p->salary

      • p->salarydouble%lf 需要 double *,同樣取地址。

  3. 返回值檢查

    • scanf 成功讀取并賦值的項數應該正好是 3(字符串、整數、浮點各一項)。

    • 如果不等于 3,就意味著輸入格式有誤,通常需要進入 if 塊做錯誤處理(如打印提示并退出)。

printf("姓名:%-10s  工號:%4d  工資:%.2f\n",p->name, p->id, p->salary);
  • 格式串解釋

    • %-10s:打印一個字符串,左對齊,占 10 個字符寬度,不足的右側補空格。

    • %4d:打印一個整數,右對齊,占 4 個字符寬度,左側不足補空格。

    • %.2f:打印一個浮點數,默認右對齊,保留 小數點后 2 位,小數點和整數部分一并計算寬度(這里未指定最小寬度)。

  • 參數

    • p->name:要打印的字符串起始地址。

    • p->id:要打印的整數學號。

    • p->salary:要打印的浮點工資。

  • 舉例
    如果 p->name = "Alice"p->id = 42p->salary = 5230.5,則輸出:

  • 姓名:Alice       工號:  42  工資:5230.50
    

    • "Alice" 占 5 字符,%-10s 會在后面補 5 個空格;

    • " 42" 占 4 字符;

    • "5230.50" 是默認緊湊輸出兩位小數。

  • 總結

  • 這兩行一行負責安全地從輸入流中讀取一個字符串、一個整數和一個雙精度浮點數,并檢查是否都讀對了;

  • 另一行則用格式化對齊的方式,按“左對齊姓名、右對齊工號和保留兩位小數的工資”整齊地打印出來。

結構體指針的原理與作用

1. 內存與指針的關系

  • 結構體對象
    當你寫 Employee emps[N]; 或者用 malloc 得到一塊連續內存時,系統會在內存中為每個 Employee 分配一段固定大小的空間,按成員順序排列:

    [ name[50] ][ id (4B) ][ salary (8B) ] [ name[50] ][ id (4B) ][ salary (8B) ] …

  • 指針(Employee *p
    p 保存的就是某個 Employee 對象在內存中的起始地址(即它第一個成員 name 的首字節地址)。

  • p + 1
    指針算術:當 p 的類型是 Employee * 時,p + 1 會自動跳過 sizeof(Employee) 字節,指向下一個結構體對象。


2. 訪問與修改成員

  • 點運算符 .:用于結構體變量本身

    Employee e; e.id = 1001;

  • 箭頭運算符 ->:用于結構體指針

    Employee *p = &e; p->id = 1001; // 等價于 (*p).id = 1001;

    • p->id 先解引用 p(得到一個結構體),再訪問它的 id 成員。

    • 語法更簡潔,不需要寫 (*p).id


3. 作用與優勢

  1. 動態管理

    • mallocfree 可在運行時靈活控制結構體數組的大小;

    • 只需存一個指針,不必用固定長度的全局或棧數組。

  2. 高效傳參

    • 將指針傳給函數(如 give_raise(Employee *e, …)),只復制 8 字節地址,不復制整個結構體(可能幾十字節甚至更多)。

    • 函數內部直接修改原對象,無需返回修改后的新副本。

  3. 遍歷與通用性

    • 結構體數組指針 emps 既能像數組那樣使用下標 emps[i],也能用指針算術 for (Employee *p = emps; p < emps+N; p++) 遍歷;

    • 這種“首地址 + 元素大小” 的通用訪問方式,使得代碼更簡潔、可移植。

  4. 抽象與封裝

    • 函數只關心“指向某個結構體”的地址,無需知道結構體在棧還是堆,也不關心它前后還有多少元素;

    • 例如 give_raise 只用 Employee *e,對任何單個員工對象都通用。


小結

  • 結構體指針 本質上就是保存了“某個結構體對象首地址”的變量。

  • 通過 p->member 或者指針算術,你可以任意訪問、修改該對象乃至緊鄰的那些同類型對象。

  • 它讓我們可以在動態內存函數調用數據遍歷中,都以同一種“指針+大小” 的模式來高效操作結構化數據。

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

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

相關文章

支持零樣本和少樣本的文本到語音48k star的配音工具:GPT-SoVITS-WebUI

支持零樣本和少樣本的文本到語音48k star的配音工具&#xff1a;GPT-SoVITS-WebUI 官網&#xff1a;RVC-Boss/GPT-SoVITS: 1 min voice data can also be used to train a good TTS model! (few shot voice cloning) 用戶手冊&#xff1a;GPT-SoVITS指南 功能 零樣本文本到語…

基于odoo17的設計模式詳解---備忘模式

大家好&#xff0c;我是你的Odoo技術伙伴。在開發復雜的業務流程時&#xff0c;我們有時會遇到這樣的需求&#xff1a;在對一個對象進行一系列復雜操作之前&#xff0c;保存其當前狀態&#xff0c;以便在操作失敗或用戶希望撤銷時&#xff0c;能夠一鍵恢復到操作之前的樣子。或…

基于Web門戶架構的監獄內網改版實踐:值班排班系統設計與信創適配探討

面向監獄內網改版場景的門戶平臺技術架構與智能排班實踐關鍵詞&#xff1a;監獄內網改版、監獄內部網站改版、值班排班系統、信創適配、智能門戶架構一、場景背景與問題分析 在信創國產化、等級保護合規、政務集約化趨勢持續推進的背景下&#xff0c;傳統監獄內部網站普遍面臨如…

二分查找篇——在排序數組中查找元素的第一個和最后一個位置【LeetCode】

34. 在排序數組中查找元素的第一個和最后一個位置 一、算法邏輯&#xff08;逐步通順講解每一步思路&#xff09; 該算法用于在一個升序排列的數組 nums 中查找某個目標值 target 的第一個出現的位置和最后一個出現的位置。 ? 1?? 定義 lower_bound 函數 def lower_boun…

【深度學習新浪潮】AI在材料力學領域的研究進展一覽

一、材料力學的研究范疇 材料力學是固體力學的核心分支,聚焦于材料在載荷作用下的變形、失效規律及性能優化,其核心任務是揭示材料的強度、剛度和穩定性機制。具體研究內容包括: 基本力學行為:分析桿、梁、軸等結構在拉伸、壓縮、彎曲、扭轉等載荷下的應力分布與應變響應。…

WPF之命令

命令的定義&#xff1a;命令與事件的區別&#xff1a;命令是具有約束性的。命令還可以控制接收者"先做校驗&#xff0c;再保存&#xff0c;再關閉"。命令&#xff1a;WPF的命令&#xff0c;實際上就是實現了ICommand接口的類&#xff0c;平時使用最多的是RoutedComma…

百度文心一言開源大模型ERNIE-4.5-0.3B-PT深度測評

號外號外&#xff01;6月30號&#xff0c;百度文心一言官宣開源ERNIE 4.5大模型&#xff01;&#xff01;&#xff01; 一收到這個消息&#xff0c;博主就立馬從GitCode拉了個模型&#xff0c;本地私有化部署體驗了一下&#xff0c;一個字&#xff0c;酷&#xff01; 鑒于絕大…

零基礎,使用Idea工具寫一個郵件報警程序

打開idea&#xff0c;創建一個project打開文件目錄下的pom.xml文件&#xff0c;添加下面的內容安裝依賴&#xff0c;等待下載完成<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId> &…

字體 Unicode 區塊字符展示 PDF 生成器

Unicode 字體字符集可視化工具 - 代碼介紹 項目概述 這個工具是一個用于分析和可視化字體文件中包含的 Unicode 字符的實用程序&#xff0c;能夠掃描指定字體文件&#xff0c;提取其中包含的所有 Unicode 字符&#xff0c;并按 Unicode 區塊分類生成 PDF 文檔&#xff0c;直觀展…

第4章:實戰項目一 打造你的第一個AI知識庫問答機器人 (RAG)

各位老鐵&#xff0c;歡迎來到我們專欄的第一個實戰項目。 在過去的三個章節里&#xff0c;我們已經完成了所有的理論儲備和環境搭建。我們理解了LLM的本質&#xff0c;掌握了Prompt Engineering的要領&#xff0c;洞悉了Embedding和向量數據庫的魔力&#xff0c;并且熟悉了La…

身份證識別api-便捷生活與安全社會的雙重保障

身份證識別技術是人工智能和圖像處理領域的杰出產物之一&#xff0c;正逐步滲透到我們生活的方方面面。而最直觀的作用就是簡化身份證驗證流程。現如今&#xff0c;無論是銀行開戶、酒店入住還是政務辦理、線上支付&#xff0c;都需要輸入 身份證信息進行身份驗證&#xff0c;傳…

跨國企業進入中國市場:如何利用亞馬遜云科技文檔 MCP 服務器解決區域差異問題

業務場景 想象一下&#xff0c;您是一家美國科技公司的 IT 架構師&#xff0c;公司剛剛決定將業務擴展到中國市場。作為技術負責人&#xff0c;您需要規劃如何將現有的基于亞馬遜云科技的應用遷移到中國區域。然而&#xff0c;您很快發現中國區的云服務環境與您熟悉的全球區域…

WPF使用WebBrowser 解決href標簽target=_blank在瀏覽器窗口打開新鏈接而非窗體內部打開的問題

前言 最近在WPF中使用WebBrowser控件顯示網頁的時候遇到一個問題,由于網頁里面有大規模的連接標簽使用了target=_blank的屬性,導致打開的網頁不是在我們的程序內部,而是調用系統瀏覽器打開了我們的網頁內容,這種情況非常的影響用戶體驗。于是就有了這篇文章內容。本文將詳細…

制作MikTex本地包可用于離線安裝包

MikTex安裝包版本是basic-miktex-24.1-x64.exe。注&#xff1a;basic版本表示只安裝MikTex基本包&#xff0c;不安裝全部包。在能夠聯網的電腦上安裝MikTex軟件后&#xff0c;可以按以下步驟制作本地包庫。一、制作本地包庫1、新建一個文件夾&#xff0c;比如在D盤新建miktex-l…

Redis基礎的介紹與使用(一)(Redis簡介以及Redis下載和安裝)

0 引言 本系列用于和大伙兒一起入門Redis&#xff0c;主要包括Redis的下載&#xff0c;分別在終端&#xff0c;圖形顯示界面以及JAVA代碼中進行使用&#xff0c;適合給需要快速了解Redis是什么以及上手使用的朋友們&#xff0c;希望我用最簡單的語言來講清楚相關內容&#xff…

七牛云C++開發面試題及參考答案

智能指針的原理及應用場景是什么&#xff1f; 智能指針是 C 中用于管理動態分配內存的工具&#xff0c;其核心原理是通過 RAII&#xff08;資源獲取即初始化&#xff09;技術&#xff0c;將堆內存的生命周期與對象的生命周期綁定&#xff0c;從而避免手動管理內存帶來的內存泄…

【Python辦公】Excel橫板表頭轉豎版通用工具(GUI版本)橫向到縱向的數據重構

目錄 專欄導讀前言項目概述功能特性技術棧核心代碼解析1. 類結構設計2. 界面布局設計3. 滾動列表實現4. 數據轉換核心邏輯5. 預覽功能實現設計亮點1. 用戶體驗優化2. 技術實現優勢3. 代碼結構優勢使用場景擴展建議總結完整代碼結尾專欄導讀 ?? 歡迎來到Python辦公自動化專欄—…

C#項目 在Vue/React前端項目中 使用使用wkeWebBrowser引用并且內部使用iframe網頁外鏈 頁面部分白屏

如果是使用wkeWebBrowser的引用方式 非常有可能是版本問題導致的 問題分析 1. wkeWebBrowser 的局限性 不支持或不完全支持 ES6 語法&#xff08;如 let, const, Promise, async/await&#xff09; 缺少對現代 Web API 的支持&#xff08;如 Intl, fetch, WebSocket&#xff0…

系統架構設計師論文分享-論微服務架構

我的軟考歷程 摘要 2023年2月&#xff0c;我所在的公司通過了研發紗線MES系統的立項&#xff0c;該系統為國內紗線工廠提供SAAS服務&#xff0c;旨在提高紗線工廠的數字化和智能化水平。我在該項目中擔任系統架構設計師一職&#xff0c;負責該項目的架構設計工作。本文結合我…

The History of Big Data

數據洪流悄然重塑世界的進程中&#xff0c;大數據的歷史是技術迭代與需求驅動的交響。從 2003 年分布式系統雛形初現&#xff0c;到 Hadoop 掀起開源浪潮&#xff0c;再到 Spark、容器化技術與深度學習的接力革新&#xff0c;以及 Hadoop 生態的興衰起落&#xff0c;大數據發展…