C 語言:第 20 天筆記:typedef(類型重命名規則、應用場景與實戰案例)

C語言:第20天筆記

內容提要

  • 構造類型
  • 枚舉類型
  • typedef
  • 綜合案例:斗地主
  • 預處理

構造類型:枚舉類型

使用建議

  • 如果定義不相干的常量,使用宏定義(符號常量);
  • 如果需要定義一組相關聯的常量(如月份011、星期06、方向03、男女01等),使用枚舉,進行統一管理。
  • 正式開發中,switch的case后面訪問的通常是枚舉中的常量。

定義

  • 一般情況下,定義常量使用宏定義(#define 宏名稱 宏值),宏定義適合無關聯關系的常量;
  • 當需要對一組有關聯關系的量(如月份011、星期06、方向0~3等)進行定義時,宏定義清晰度低、不便于統一管理且會增加代碼量,此時需使用枚舉;
  • 枚舉的作用是將多個有關聯關系的常量組合到一起,提高代碼的可讀性。

說明

  1. 枚舉定義了一組常量,開發中可直接使用這些常量(常用);
  2. 枚舉類型也可類似于結構體定義變量等操作(不常用);
  3. 枚舉常量有默認值,從0開始依次+1;可在定義時指定默認值,若個別未賦值,可根據已賦值常量依次+1推導。

特點

  1. 定義了一組常量,類似于定義了多個符號常量(宏定義);
  2. 提高了代碼的可讀性。

語法

語法分類格式說明
先定義類型后定義變量enum 枚舉類型名 變量列表;定義枚舉類型名后,再定義該枚舉類型的變量,枚舉的元素是符號常量
定義類型同時定義變量enum 枚舉類型名{枚舉元素列表} 變量列表;在定義枚舉類型的同時,定義該枚舉類型的變量
直接定義枚舉變量enum {枚舉元素列表} 變量列表;不單獨定義枚舉類型名,直接定義枚舉變量

案例(demo01.c)

#include <stdio.h>void test1()
{// 定義一個枚舉類型// 注意:枚舉類型名一般首字母大寫,主要是跟枚舉元素名區分enum Week{// 定義枚舉元素,元素本質上就是常量,在編譯期,會被替換為字面量// 枚舉元素的命名和符號常量命名一致,都是大寫+下劃線// 多個枚舉元素之間使用逗號分隔// SUN,MON,TUE,WED,THU,FRI,SAT // 此時,這7個常量的值依次為:0~6SUN = 10, MON, TUE, WED, THU, FRI, SAT // 此時,這7個常量的值依次為:10~16};// 1. 直接訪問枚舉元素,適合于switchprintf("%d,%d,%d\n", SUN, WED, SAT); // 10,13,16// 2. 定義枚舉類型的變量,適合于函數傳參enum Week week;// 初始化week = TUE; // 不能隨便賦值,賦值一定是這個枚舉中定義的元素printf("%d\n", week); // 12// 3. 定義枚舉類型變量的同時賦值enum Week week1 = THU;printf("%d\n", week1);// 14// 4. 可以定義多個枚舉變量enum THU{A, B, C} x, y;// 賦值x = B;y = C;printf("x=%d,y=%d\n", x, y);// 1,2
}void test2()
{// 定義枚舉類型enum CaiQuan{SHI_TOU, JIAN_DAO, BU};printf("請輸入0~2之間的整數:\n0-石頭,1-剪刀,2-布\n");int choice;scanf("%d", &choice);switch (choice) {case SHI_TOU:printf("石頭\n");break;case JIAN_DAO:printf("剪刀\n");break;case BU:printf("布\n");break;}
}int main(int argc, char *argv[])
{test1();test2();return 0;
}

typedef

說明

給類型重命名,不會影響到類型本身。

作用

給已有的類型起別名。

格式

typedef 已有類型名 重命后的類型名;
示例:typedef unsigned long size_t;

使用案例(demo02.c)

#include <stdio.h>int main(int argc, char *argv[])
{// 方式1:先定義數據類型,再重命名// 定義一個結構體struct Student{int id;char *name;char sex;int age;};// 類型重命名typedef struct Student Stu; // 將 struct Student 重命名為Stu// 使用新類型名// 定義結構體實例Stu stu = {1,"張三",'w',21};printf("%d,%s,%c,%d\n", stu.id, stu.name, stu.sex, stu.age);Stu *p = &stu;printf("%d,%s,%c,%d\n", p->id, p->name, p->sex, p->age);// 方式2:定義數據類型的同時重命名typedef struct PersonInfo{int a;double b;} Per;// 定義變量Per per = {2, 4.5};printf("%d,%.2f\n", per.a, per.b);// 定義指針Per *p1 = &per;printf("%d,%.2f\n", p1->a, p1->b);return 0;
}

應用場景

數據類型復雜(結構體、共用體、枚舉、結構體指針、無符號的長整型)時使用。

跨平臺兼容性案例

C語言標準提供:
typedef signed long int_int64_t;
typedef unsigned Long intuint64_t;

常見跨平臺類型重命名:

  1. size_ttypedef unsigned long size_t;
  2. unit_16:類型重命名后的數據類型。

進階案例(demo03.c)

#include <stdio.h>struct Student
{int age;char *name;double scores[3];
};typedef struct Student Stu_t; // 對類型重命名
typedef Stu_t* pStu_t; // 結構體指針重命名void test1()
{Stu_t s1 = {21, "zhangsan", {99, 98, 97}};printf("%d,%s,%.2lf,%.2lf,%.2lf\n", s1.age, s1.name, s1.scores[0], s1.scores[1], s1.scores[2]);Stu_t *p;p = &s1;printf("%d,%s,%.2lf,%.2lf,%.2lf\n", (*p).age, p->name, p->scores[0], p->scores[1], p->scores[2]);
}int main(int argc, char *argv[])
{test1();return 0;
}

綜合案例:斗地主

1. 程序概述

這是一個模擬斗地主游戲發牌過程的C語言程序,實現了撲克牌的初始化、洗牌和發牌功能。

2. 功能需求

2.1 撲克牌定義

使用結構體Card表示一張牌,包含:

  • 花色屬性suit(0-3表示普通花色????,4表示小王,5表示大王);
  • 點數屬性rank(0-12對應3-A、2,-1表示大小王)。
2.2 主要功能
  1. 初始化牌組
    • 創建包含54張牌的牌組(52張普通牌+2張王牌);
    • 普通牌按花色(?,?,?,?)和點數(3-2)排列。
  2. 洗牌功能
    • 使用隨機數對牌組進行隨機排序;
    • 確保每次運行洗牌結果不同(基于時間種子)。
  3. 發牌功能
    • 將洗好的牌發給3個玩家;
    • 每個玩家17張牌;
    • 剩余3張作為底牌。
  4. 顯示功能
    • 打印每個玩家的手牌;
    • 打印底牌。

3. 數據結構

  • suits[]:存儲4種花色符號的字符串數組;
  • ranks[]:存儲13個點數等級的字符串數組;
  • jokers[]:存儲大小王描述的字符串數組;
  • Card結構體:表示單張牌的數據結構;
  • 牌組數組:deck[54]
  • 玩家手牌數組:player1[17]player2[17]player3[17]
  • 底牌數組:bottomCards[3]

4. 用戶交互

程序運行后自動完成以下流程:

  1. 初始化牌組;
  2. 洗牌;
  3. 發牌;
  4. 顯示發牌結果(3個玩家的手牌和底牌)。

5. 輸出格式

  • 普通牌顯示格式:花色+點數(如"? 3");
  • 王牌顯示格式:“小王"或"大王”;
  • 玩家手牌按順序顯示,每張牌用空格分隔;
  • 底牌同樣格式顯示。

6. 源碼


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>#define LEN 54// 定義撲克牌的花色和點數
const char *suits[] = {"?","?","?","?"};
const char *ranks[] = 
{"3","4","5","6","7","8","9","10","J","Q","K","A","2"}; // 點數
const char *jokers[] = {"小王","大王"}; // 大小王typedef struct 
{int suit; // 牌花色int rank; // 底數
} Card;/*** @brief 初始化撲克*/
void initCards(Card *deck);/*** @brief 洗牌*/
void shuffDeck(Card *deck);void dealCard(Card *deck, Card *bottomCards, Card players[3][17]);void printfCard(Card players[3][17]);int main(int argc, char *argv[])
{// 創建一個數組,存放一副牌(54張包括大小王)Card deck[LEN];// 創建三個玩家Card players[3][17];// 創建一個數組用來存放底牌Card bottomCards[3];// 初始化牌initCards(deck);// 洗牌shuffDeck(deck);// 發牌dealCard(deck,bottomCards,players);// 展示牌printfCard(players);return 0;
}/*** @brief 初始化撲克*/
void initCards(Card *deck)
{int index  = 0;for(int i = 0; i < 4; i++){for(int j = 0;i < 13; j++){deck[index].rank = j;deck[index].suit = i;index++;}}// 初始化大小王deck[index].suit = 4; // 小王deck[index].rank = -1;index++;deck[index].suit = 5; // 大王deck[index].rank = -1;	
}// 洗牌
void shuffDeck(Card *deck)
{srand((unsigned)time(NULL));// 洗牌for (int i = 0; i < LEN; i++){int j = rand() % LEN; // 0-53Card temp = deck[i];deck[i] = deck[j];deck[j] = temp;}
}void dealCard(Card *deck,  Card *bottomCards, Card players[3][17])
{int index = 0;for(int i = 0;i < 17; i++){for(int j = 0;j < 3; j++){players[j][i] = deck[index++];}}// 底牌for(int i=0;i< 3; i++){bottomCards[i] = deck[index++];	}}// 打印
void printfCard(Card players[3][17])
{for(int i = 0;i < 3; i++){printf("第%d個玩家的牌是:",i+1);for(int j = 0; j < 17; j++){Card card = players[i][j]; if (card.suit == 4 || card.suit == 5){// 大小王printf("%s ",jokers[card.suit - 4]);}printf("%s %s ",suits[card.suit], ranks[card.rank]);}}}

運行結果

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

預處理

一、C語言的編譯步驟

C語言程序從源代碼到可執行文件,需經歷以下4個核心步驟,預處理是整個流程的第一步:

  1. 預處理:由預處理器處理源文件中的預處理指令(如#define#include等),生成預處理文件(.i格式);
  2. 編譯:編譯器對預處理文件進行詞法分析、語法分析、語義分析及優化,生成匯編代碼文件(.s格式);
  3. 匯編:匯編器將匯編代碼轉換為機器可識別的二進制目標文件(.o.obj格式);
  4. 鏈接:鏈接器將多個目標文件與標準庫(如libc)組合,生成最終可執行文件(如Windows下的.exe、Linux下的無后綴可執行文件)。

編譯流程示意圖

源文件(.c) → 預處理(cpp) → 預處理文件(.i) → 編譯(cc1) → 匯編文件(.s) → 匯編(as) → 目標文件(.o) → 鏈接(ld) → 可執行文件

二、什么是預處理

預處理是在源文件(.c文件)編譯之前,由預處理器自動完成的預備操作。當編譯器對源文件進行編譯時,會先調用預處理器執行預處理操作,只有預處理解析完成后,才能進入后續的編譯過程。

查看預處理結果

通過gcc編譯器的-E選項可查看預處理后的結果,命令格式如下:

gcc 源文件 -E -o 預處理輸出文件名

例如,對demo01.c進行預處理并輸出到demo01.i,命令為:gcc demo01.c -E -o demo01.i

三、預處理核心功能:宏定義

宏定義是預處理階段最常用的功能之一,用于將一個標識符(宏名)定義為一個字符串(宏值),在預處理階段會將代碼中所有宏名替換為對應的宏值(即“宏展開”)。

1. 不帶參數的宏定義

語法
#define 宏名稱 宏值(替換文本)
  • 預處理機制:僅做文本替換,不進行類型檢查;
  • 內存特性:宏定義不占用內存空間,因為編譯前宏名已被替換為宏值;
  • 宏展開:預處理階段將宏名替換為宏值的過程稱為“宏展開”。
案例(demo01.c)
#include <stdio.h>#define PI 3.1415926  // 定義宏PI,宏值為3.1415926int main(int argc, char *argv[])
{float l, s, r;printf("請輸入圓的半徑:\n");scanf("%f", &r);// 計算周長(宏PI會被替換為3.1415926)l = 2.0 * PI * r;// 計算面積(宏PI會被替換為3.1415926)s = PI * r * r;printf("l=%10.4f\ns=%10.4f\n", l, s);return 0;
}
宏展開后的效果

預處理后,代碼中PI會被直接替換為3.1415926,關鍵代碼展開如下:

l = 2.0 * 3.1415926 * r;
s = 3.1415926 * r * r;

2. 帶參數的宏定義

帶參數的宏定義允許宏名接收參數,替換時會將宏體中的參數占位符替換為實際傳入的參數,類似函數但無函數調用開銷(僅文本替換)。

語法
#define 宏名(參數列表) 替換表達式
注意事項(面試高頻考點)

帶參數的宏定義需注意括號的使用,若缺少括號可能導致運算優先級錯誤。例如:

  • 錯誤寫法:#define MULTI(a,b) a * b
    當調用MULTI(7+2, 3)時,展開為7+2*3,結果為13(不符合預期);
  • 正確寫法:#define MULTI(a,b) (a) * (b)
    當調用MULTI(7+2, 3)時,展開為(7+2)*(3),結果為27(符合預期)。
案例(demo02.c)
#include <stdio.h>// 帶參數的宏定義,宏名一般小寫(區分函數名)
#define MULTI_1(a,b) (a) * (b)  // 正確:參數加括號
#define MULTI_2(a,b) a * b      // 錯誤:參數無括號int main(int argc, char *argv[])
{// 調用MULTI_1:(7+2)*(3) = 27int result1 = MULTI_1(7+2, 3); printf("%d\n", result1);// 調用MULTI_2:7+2*3 = 13(結果不符合預期)int result2 = MULTI_2(7+2, 3); printf("%d\n", result2);return 0;
}

3. 宏定義的作用域

  • 默認作用域#define命令需寫在函數外部,宏名的有效范圍從定義處開始,到本源文件結束;
  • 終止作用域:可通過#undef命令主動終止宏定義的作用域,后續代碼中該宏名不再生效。
案例(demo04.c)
#include <stdio.h>#define PI 3.14        // PI的有效范圍:從定義處(第8行)到#undef處(第18行)
#define DAY 29         // DAY的有效范圍:從定義處(第10行)到本源文件結束void func1()
{float r = 4;float s = PI * r * r;  // 預處理后:3.14 * r * r(PI有效)int day = DAY;         // 預處理后:29(DAY有效)
}#undef PI                 // 終止PI的作用域,后續代碼中PI失效
#define PI 3.1415926      // 重新定義PI,作用域從第20行到本源文件結束void func2()
{float r = 4;float s = PI * r * r;  // 預處理后:3.1415926 * r * r(新PI有效)int day = DAY;         // 預處理后:29(DAY仍有效)
}int main(int argc, char *argv[])
{return 0;
}

4. 宏定義的嵌套引用

宏定義支持嵌套,即一個宏的宏值中可以引用其他已定義的宏名,預處理時會逐層展開所有宏。

案例(demo04.c)
#include <stdio.h>#define R 3.0          // 定義宏R(半徑)
#define PI 3.14        // 定義宏PI(圓周率)
#define L 2 * PI * R   // 嵌套引用PI和R,計算周長
#define S PI * R * R   // 嵌套引用PI和R,計算面積// 錯誤示例:宏定義中不應加等號(會導致替換錯誤)
#define P_WIDTH = 800  
#define P_HEIGHT = 480
#define SIZE = P_WIDTH * P_HEIGHTint main(int argc, char *argv[])
{// 預處理后:L展開為2*3.14*3.0,S展開為3.14*3.0*3.0printf("L=%f\nS=%f\n", L, S);return 0;
}

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

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

相關文章

在 vue3 和 vue2 中,v-for 和 v-if 可以一起用嗎,區別是什么

在 Vue 2 和 Vue 3 中&#xff0c;v-for 和 v-if 可以一起使用&#xff0c;但兩者在處理順序和推薦用法上存在明顯區別&#xff0c;主要體現在優先級和最佳實踐上&#xff1a; 1. Vue 2 中的 v-for 與 v-if優先級&#xff1a;v-for 的優先級高于 v-if。 這意味著 Vue 會先循環渲…

Linux-進程相關函數

文章目錄Linux-進程相關函數父子進程關系父子進程地址空間getpid函數 獲取本進程號getppid函數 獲取當前進程的進程的父進程號getpgid函數 獲取進程組號示例fork函數 創建進程區分父子進程exit函數 進程退出wait函數 等待子進程退出waitpid函數Linux-進程相關函數 每個進程都由…

數據挖掘 6.1 其他降維方法(不是很重要)

6.1 Other dimensionality reduction methods 6.1 其他降維方法 其他降維方法前言問題答案流形3 降維大綱3.1 線性方法3.2 非線性方法3.2.1 流形學習方法&#xff08;Manifold Learning&#xff09;3.2.2 概率方法&#xff08;Probabilistic Approaches&#xff09;3.2.3 拓撲數…

Unity中的特殊文件夾

一.工程路徑獲取print(Application.dataPath);只用于游戲開發編輯器模式下&#xff0c;游戲發布后此路徑就不存在了二.Resources 資源文件夾//路徑獲取: //一般不獲取 //只能使用Resources相關API進行加載 //如果硬要獲取 可以用工程路徑拼接print(Application.dataPath "…

Seaborn數據可視化實戰:Seaborn高級使用與性能優化教程

Seaborn最佳實踐與技巧 學習目標 本課程將深入探討Seaborn庫的高級使用技巧&#xff0c;包括性能優化、常見問題解決方法等&#xff0c;旨在幫助學員掌握如何高效地使用Seaborn進行數據可視化&#xff0c;提升圖表的美觀度和信息傳達效率。 相關知識點 Seaborn最佳實踐與技巧 學…

分布式系統與單機系統的優劣勢對比

近期有遇到一個本地部署的需求&#xff0c;他們希望用主備方案&#xff0c;這就涉及到了備用系統怎么收費的問題。我們是單機系統&#xff0c;其他友商是分布式系統&#xff0c;那20坐席的手撥需求到底是選單機系統好&#xff0c;還是選分布式系統好呢&#xff1f;了解了兩者的…

深度學習:從手寫數字識別案例認識pytorch框架

目錄 一、PyTorch 核心優勢與框架定位 二、實戰基礎&#xff1a;核心庫與數據準備 1. 關鍵庫導入與功能說明 2. MNIST 數據集加載與可視化 &#xff08;1&#xff09;數據集下載與封裝 &#xff08;2&#xff09;數據集可視化&#xff08;可選&#xff09; 3. DataLoade…

二分|組合|旋轉數組

lc1976dijk min_pathpq. min_wlcr187同lc1823.約瑟夫環class Solution { public:int iceBreakingGame(int num, int target) {int x0;for(int i2;i<num;i){x(xtarget)%i;} return x;} };lc2972計算數組中可移除的子數組數量先找最長遞增前綴&#xff0c;再結合遞增后綴…

【C語言16天強化訓練】從基礎入門到進階:Day 10

&#x1f525;個人主頁&#xff1a;艾莉絲努力練劍 ?專欄傳送門&#xff1a;《C語言》、《數據結構與算法》、C語言刷題12天IO強訓、LeetCode代碼強化刷題、洛谷刷題、C/C基礎知識知識強化補充、C/C干貨分享&學習過程記錄 &#x1f349;學習方向&#xff1a;C/C方向學習者…

云計算與云原生技術探索

&#x1f31f; Hello&#xff0c;我是蔣星熠Jaxonic&#xff01; &#x1f308; 在浩瀚無垠的技術宇宙中&#xff0c;我是一名執著的星際旅人&#xff0c;用代碼繪制探索的軌跡。 &#x1f680; 每一個算法都是我點燃的推進器&#xff0c;每一行代碼都是我航行的星圖。 &#x…

STM32之ADC詳解

一、ADC概述 ADC&#xff08;模擬量轉數字量轉換器&#xff09;&#xff0c;在 STM32 開發中&#xff0c;利用 ADC 端口的電壓數據&#xff0c;轉換為對應的具體數字量數據內容。可通過 ADC 方式獲取常用數據內容有&#xff1a; 光敏電阻、電池電量、油箱油量 ADC 轉換…

深入理解計算機網絡:從基礎到應用的全面解析

標題&#xff1a;深入理解計算機網絡&#xff1a;從基礎到應用的全面解析 引言 計算機網絡已經滲透到我們生活的方方面面。從家庭Wi-Fi到全球互聯網&#xff0c;我們每天都在通過各種設備進行數據交換。本文將帶領你走進計算機網絡的世界&#xff0c;深入探討網絡的基礎知識、常…

以結構/序列/功能之間的關系重新定義蛋白質語言模型的分類:李明辰博士詳解蛋白質語言模型

上海交通大學第三屆「AI for Bioengineering 暑期學校」于 2025 年 8 月 8—10 日正式開啟。本次暑期學校匯聚了自全球 70 余所高校、 10 余所科研機構及 10 余家行業領軍企業的 200 余位青年才俊、科研學者和產業代表&#xff0c;共同聚焦于人工智能&#xff08;AI&#xff09…

【大語言模型 15】因果掩碼與注意力掩碼實現:深度學習中的信息流控制藝術

【大語言模型 15】因果掩碼與注意力掩碼實現&#xff1a;深度學習中的信息流控制藝術 關鍵詞&#xff1a;因果掩碼、注意力掩碼、下三角掩碼、Padding掩碼、序列建模、GPT解碼器、BERT編碼器、批量處理優化、自回歸語言模型、信息流控制 摘要&#xff1a;在Transformer架構中&a…

大型電動化工程機械設備智能施工試驗場的網絡設計方案

隨著工程機械設備逐步邁向智能化、電動化和無人化&#xff0c;傳統施工試驗場已經難以滿足現代化施工設備的研發、測試和驗證需求。為了適應這一趨勢&#xff0c;建設一個基于高性能網絡架構的大型智能施工試驗場成為關鍵。本文將從網絡架構、設備選型和功能實現等方面&#xf…

SPMI總線協議(一)

1、簡單說明 系統電源管理接口( System Power Management Interface簡稱SPMI)是一種雙線串行接口,用于連接片上系統(SoC)處理器系統的集成電源控制器(PC)與一個或多個電源管理集成電路(PMIC)電壓調節系統。SPMI 使系統能夠使用單個 SPMI 總線動態調整 SoC 內部電壓域的…

數據存儲的思考——從RocketMQ和Mysql的架構入手

數據存儲是后臺服務系統永遠繞不開的知識 筆者希望能夠從宏觀的角度出發&#xff0c;思考數據存儲系統的共性和設計方案&#xff0c;嘗試從Mysql和RocketMQ的角度去思考談談系統存儲架構的設計哲學 前置的知識 什么是RocketMQ、什么是Mysql&#xff0c;他們對于后端系統的主用…

MySQL 面試題系列(二)

目錄1: SQL 中常見的 JOIN 類型有哪些&#xff1f;請分別說明其連接邏輯和適用場景。2: UNION 和 UNION ALL 有什么區別&#xff1f;它們各自的適用場景是什么&#xff1f;3: 什么是視圖 (View)&#xff1f;它的作用和優缺點是什么&#xff1f;4: 什么是索引 (Index)&#xff1…

PostgreSQL診斷系列(2/6):鎖問題排查全攻略——揪出“阻塞元兇”

&#x1f517; 接上一篇《PostgreSQL全方位體檢指南》&#xff0c;今天我們深入數據庫的“神經系統”——鎖機制&#xff0c;解決最令人頭疼的“卡頓”問題。 你是否經歷過&#xff1a; 某個SQL執行著就不動了&#xff1f;應用界面卡在“加載中”&#xff1f;UPDATE 語句遲遲不…

crc16是什么算法

核心概念?CRC16? 是一種循環冗余校驗算法&#xff0c;屬于哈希函數的一種。它的核心目的是檢測數據的錯誤&#xff0c;通常用于數字網絡和存儲設備中&#xff0c;來驗證數據在傳輸或存儲后是否依然完整、無誤。你可以把它想象成一個數據的“指紋”或“摘要”。發送方計算出一…