1. 程式人生 > >SHA1校驗演算法C語言實現

SHA1校驗演算法C語言實現

SHA1 安全雜湊演算法:對於長度小於2^64位的訊息(1M = 1024k,1K = 1024位元組,1BYTE = 8bit 可以想象一下2的63次方位可以表示一個多大的資料檔案),SHA1會產生一個160位的訊息摘要。當接收到訊息的時候,這個訊息摘要可以用來驗證資料的完整性。在傳輸的過程中,資料很可能會發生變化,那麼這時候就會產生不同的訊息摘要。 SHA1有如下特性:不可以從訊息摘要中復原資訊(不可逆性);兩個不同的訊息不會產生同樣的訊息摘要,(但會有1x10 ^ 48分之一的機率出現相同的訊息摘要,一般使用時忽略)。

SHA1和MD5的演算法都是從MD4演算法改進而來的2種演算法,基本思路都是將資訊分成N個分組,每組64個位元組,每個分組都進行摘要運算。當一個分組的摘要運算完畢後,將上一個分組的結果也用於下一個分組的運算。資訊的長度(注意是bit位長度,不是位元組長度)用64位表示,也要參加資訊摘要運算,而且是放在最後一個分組的末尾,所以長度資訊要佔據8個位元組(一個位元組為8位)。如果資訊資料最後一個分組長度小於64個位元組,在後面新增0x80標誌結束,如果此時資料+結束標誌已經<=56個位元組,還可以放入長度資料,就在結束標誌到第56個位元組補0,然後放入長度,如果此時資訊資料+結束標誌已經大於56位元組,那麼這個分組後面補0,進行一次摘要運算,然後再建立一個分組,前面全部補0,最後16個位元組放長度,再進行一次摘要。

需要注意的地方如下:
SHA1是20個位元組(也就是前面說的160位)。SHA1的分組資訊運算,SHA1的演算法認為DWORD是BIG-ENDIAN的。所以在不同位元組序的主機上要進行轉換。放入最後一個分組的長度資訊,是原始資料長度,而且是BIT位長度,其是一個uint64_t,SHA1演算法則要求這個長度是BIG-ENDIAN的。不同的平臺要進行轉換。當然生成的結果,SHA1要求結果是BIG-ENDIAN的,不同的平臺還是要進行轉換。

貼一個摘要處理過程的分組資訊,幫助理解。例如要處理的資料是3個位元組字串”abc”:

這個字串在SHA1演算法中,只有一個分組,如下,十六進位制的18標識24個bit3個位元組

。(0X18表示長度 "abc"三個8位的字元也就是24位,24的十六進位制也就是0X18


61 62 63 80 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18

還是先來體驗以下結果:(網上下載一個生成SHA1校驗碼的軟體,然後和程式生成的校驗碼對比看看是否一樣)

建立一個test.txt檔案,輸入abc三個字元,生成校驗碼:


程式執行生成校驗碼截圖:(輸入的資料也是字元“abc”)


程式的C程式碼也不是我本人寫的,這個也是直接在CSDN上找的資源!這裡借前輩大神寫的程式碼,簡要的分析學習一下。

直接上程式碼(英文註釋寫的比較詳細):

/*
* sha1.h
*
* Description:
* This is the header file for code which implements the Secure
* Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
* April 17, 1995.
*
* Many of the variable names in this code, especially the
* single character names, were used because those were the names
* used in the publication.
*
* Please read the file sha1.c for more information.
*
*/

#ifndef _SHA1_H_
#define _SHA1_H_
#include <stdint.h>
/*
* If you do not have the ISO standard stdint.h header file, then you
* must typdef the following:
* name meaning
* uint32_t unsigned 32 bit integer
* uint8_t unsigned 8 bit integer (i.e., unsigned char)
* int_least16_t integer of >= 16 bits
*
*/
#ifndef _SHA_enum_
#define _SHA_enum_
enum
{
	shaSuccess = 0,
	shaNull,         /* Null pointer parameter */
	shaInputTooLong, /* input data too long */
	shaStateError    /* called Input after Result */
};
#endif
#define SHA1HashSize 20
/*
* This structure will hold context information for the SHA-1
* hashing operation
*/
typedef struct SHA1Context
{
	uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
	uint32_t Length_Low; /* Message length in bits */
	uint32_t Length_High; /* Message length in bits */
	/* Index into message block array */
	int_least16_t Message_Block_Index;
	uint8_t Message_Block[64]; /* 512-bit message blocks */
	int Computed; /* Is the digest computed? */
	int Corrupted; /* Is the message digest corrupted? */
} SHA1Context;


/*
* Function Prototypes
*/

int SHA1Reset( SHA1Context *);
int SHA1Input( SHA1Context *,const uint8_t *,unsigned int);
int SHA1Result( SHA1Context *,uint8_t Message_Digest[SHA1HashSize]);

#endif

SHA1.C
/*
* sha1.c
*
* Description:
* This file implements the Secure Hashing Algorithm 1 as
* defined in FIPS PUB 180-1 published April 17, 1995.
*
* The SHA-1, produces a 160-bit message digest for a given
* data stream. It should take about 2**n steps to find a
* message with the same digest as a given message and
* 2**(n/2) to find any two messages with the same digest,
* when n is the digest size in bits. Therefore, this
* algorithm can serve as a means of providing a
* "fingerprint" for a message.
*
* Portability Issues:
* SHA-1 is defined in terms of 32-bit "words". This code
* uses <stdint.h> (included via "sha1.h" to define 32 and 8
* bit unsigned integer types. If your C compiler does not
* support 32 bit unsigned integers, this code is not
* appropriate.
*
* Caveats:
* SHA-1 is designed to work with messages less than 2^64 bits
* long. Although SHA-1 allows a message digest to be generated
* for messages of any number of bits less than 2^64, this
* implementation only works with messages with a length that is
* a multiple of the size of an 8-bit character.
*
*/

#include "SHA1.h"

#ifdef __cplusplus
extern "C"
{
#endif

/*
* Define the SHA1 circular left shift macro
*/
#define SHA1CircularShift(bits,word) \
	(((word) << (bits)) | ((word) >> (32-(bits))))
/* Local Function Prototyptes */
void SHA1PadMessage(SHA1Context *);
void SHA1ProcessMessageBlock(SHA1Context *);
/*
* SHA1Reset
*
* Description:
* This function will initialize the SHA1Context in preparation
* for computing a new SHA1 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*
*/
int SHA1Reset(SHA1Context *context)//初始化狀態
{
	if (!context)
	{
		return shaNull;
	}
	context->Length_Low = 0;
	context->Length_High = 0;
	context->Message_Block_Index = 0;
	context->Intermediate_Hash[0] = 0x67452301;//取得的HASH結果(中間資料)
	context->Intermediate_Hash[1] = 0xEFCDAB89;
	context->Intermediate_Hash[2] = 0x98BADCFE;
	context->Intermediate_Hash[3] = 0x10325476;
	context->Intermediate_Hash[4] = 0xC3D2E1F0;
	context->Computed = 0;
	context->Corrupted = 0;
	return shaSuccess;
}


/*
* SHA1Result
*
* Description:
* This function will return the 160-bit message digest into the
* Message_Digest array provided by the caller.
* NOTE: The first octet of hash is stored in the 0th element,
* the last octet of hash in the 19th element.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
* Message_Digest: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*
*/
int SHA1Result( SHA1Context *context,uint8_t Message_Digest[SHA1HashSize])
{
	int i;
	if (!context || !Message_Digest)
	{
		return shaNull;
	}
	if (context->Corrupted)
	{
		return context->Corrupted;
	}
	if (!context->Computed)
	{
		SHA1PadMessage(context);
		for(i=0; i<64; ++i)
		{
			/* message may be sensitive, clear it out */
			context->Message_Block[i] = 0;
		}
		context->Length_Low = 0; /* and clear length */
		context->Length_High = 0;
		context->Computed = 1;
	}
	for(i = 0; i < SHA1HashSize; ++i)
	{
		Message_Digest[i] = context->Intermediate_Hash[i>>2]
		>> 8 * ( 3 - ( i & 0x03 ) );
	}
	return shaSuccess;
}


/*
* SHA1Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* message_array: [in]
* An array of characters representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* sha Error Code.
*
*/

int SHA1Input( SHA1Context *context,const uint8_t *message_array,unsigned length)
{
	if (!length)
	{
		return shaSuccess;
	}
	if (!context || !message_array)
	{
		return shaNull;
	}
	if (context->Computed)
	{
		context->Corrupted = shaStateError;
		return shaStateError;
	}
	if (context->Corrupted)
	{
		return context->Corrupted;
	}
	while(length-- && !context->Corrupted)
	{
		context->Message_Block[context->Message_Block_Index++] =
			(*message_array & 0xFF);
		context->Length_Low += 8;
		if (context->Length_Low == 0)
		{
			context->Length_High++;
			if (context->Length_High == 0)
			{
				/* Message is too long */
				context->Corrupted = 1;
			}
		}
		if (context->Message_Block_Index == 64)
		{
			SHA1ProcessMessageBlock(context);
		}
		message_array++;
	}
	return shaSuccess;
}

/*
* SHA1ProcessMessageBlock
*
* Description:
* This function will process the next 512 bits of the message
* stored in the Message_Block array.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the publication.
*
*/

void SHA1ProcessMessageBlock(SHA1Context *context)
{
	const uint32_t K[] = { /* Constants defined in SHA-1 */
		0x5A827999,
		0x6ED9EBA1,
		0x8F1BBCDC,
		0xCA62C1D6
	};
	int t; /* Loop counter */
	uint32_t temp; /* Temporary word value */
	uint32_t W[80]; /* Word sequence */
	uint32_t A, B, C, D, E; /* Word buffers */
	/*
	* Initialize the first 16 words in the array W
	*/
	for(t = 0; t < 16; t++)
	{
		W[t] = context->Message_Block[t * 4] << 24;
		W[t] |= context->Message_Block[t * 4 + 1] << 16;
		W[t] |= context->Message_Block[t * 4 + 2] << 8;
		W[t] |= context->Message_Block[t * 4 + 3];
	}
	for(t = 16; t < 80; t++)
	{
		W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
	}
	A = context->Intermediate_Hash[0];
	B = context->Intermediate_Hash[1];
	C = context->Intermediate_Hash[2];
	D = context->Intermediate_Hash[3];
	E = context->Intermediate_Hash[4];
	for(t = 0; t < 20; t++)
	{
                temp = SHA1CircularShift(5,A) +
                        ((B & C) | ((~B) & D)) + E + W[t] + K[0];
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	for(t = 20; t < 40; t++)
	{
		temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	for(t = 40; t < 60; t++)
	{
		temp = SHA1CircularShift(5,A) +
			((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	for(t = 60; t < 80; t++)
	{
		temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	context->Intermediate_Hash[0] += A;
	context->Intermediate_Hash[1] += B;
	context->Intermediate_Hash[2] += C;
	context->Intermediate_Hash[3] += D;
	context->Intermediate_Hash[4] += E;
	context->Message_Block_Index = 0;
}


/*
* SHA1PadMessage
*
* Description:
* According to the standard, the message must be padded to an even
* 512 bits. The first padding bit must be a ’1’. The last 64
* bits represent the length of the original message. All bits in
* between should be 0. This function will pad the message
* according to those rules by filling the Message_Block array
* accordingly. It will also call the ProcessMessageBlock function
* provided appropriately. When it returns, it can be assumed that
* the message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad
* ProcessMessageBlock: [in]
* The appropriate SHA*ProcessMessageBlock function
* Returns:
* Nothing.
*
*/

void SHA1PadMessage(SHA1Context *context)
{
	/*
	* Check to see if the current message block is too small to hold
	* the initial padding bits and length. If so, we will pad the
	* block, process it, and then continue padding into a second
	* block.
	*/
	if (context->Message_Block_Index > 55)
	{
		context->Message_Block[context->Message_Block_Index++] = 0x80;
		while(context->Message_Block_Index < 64)
		{
			context->Message_Block[context->Message_Block_Index++] = 0;
		}
		SHA1ProcessMessageBlock(context);
		while(context->Message_Block_Index < 56)
		{
			context->Message_Block[context->Message_Block_Index++] = 0;
		}
	}
	else
	{
		context->Message_Block[context->Message_Block_Index++] = 0x80;
		while(context->Message_Block_Index < 56)
		{
			context->Message_Block[context->Message_Block_Index++] = 0;
		}
	}

	/*
	* Store the message length as the last 8 octets
	*/
	context->Message_Block[56] = context->Length_High >> 24;
	context->Message_Block[57] = context->Length_High >> 16;
	context->Message_Block[58] = context->Length_High >> 8;
	context->Message_Block[59] = context->Length_High;
	context->Message_Block[60] = context->Length_Low >> 24;
	context->Message_Block[61] = context->Length_Low >> 16;
	context->Message_Block[62] = context->Length_Low >> 8;
	context->Message_Block[63] = context->Length_Low;
	SHA1ProcessMessageBlock(context);
}


#ifdef __cplusplus
}
#endif

除去註釋,程式碼並不多!程式碼還是很精練的!

Makefile檔案

# -fPIC : position-independent code,suitable for dynamic linking and avoiding any limit on the size of the global offset table.

#arm-hisiv100nptl-linux-

CROSS_COMPILE=
CC = $(CROSS_COMPILE)gcc
STRIP = $(CROSS_COMPILE)strip
#LIBS = -L. -lstdc++  #移植到開發板上需要主動連結libstdc++.so 這個動態庫,暫時沒有更好的解決方法,只能手動連結
CFLAGS = -Wall -g -Os
INCLUDE = -I ./


OBJ := SHA1.o 
OBJ += test.o 

TARGET = sha 

all: $(OBJ)
	$(CC) $(CFLAGS) $(OBJ) -o $(TARGET) $(LIBS) $(INCLUDE)
	$(STRIP) --strip-unneeded $(TARGET)

# --strip-unneeded 

SHA1.o : SHA1.c 
	$(CC) $(CFLAGS) -c $< -o [email protected] 

test.o : test.c 
	$(CC) $(CFLAGS) -c $< -o [email protected] 

clean:
	rm -rf $(OBJ)
測試檔案test.c

/*
* sha1test.c
*
* Description:
* This file will exercise the SHA-1 code performing the three
* tests documented in FIPS PUB 180-1 plus one which calls
* SHA1Input with an exact multiple of 512 bits, plus a few
* error test checks.
*
* Portability Issues:
* None.
*
*/

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "SHA1.h"
/*
* Define patterns for testing
*/
#define TEST1 "abc"
#define TEST2a "abcdbcdecdefdefgefghfghighijhi"

#define TEST2b "jkijkljklmklmnlmnomnopnopq"
#define TEST2 TEST2a TEST2b
#define TEST3 "a"
#define TEST4a "01234567012345670123456701234567"
#define TEST4b "01234567012345670123456701234567"
/* an exact multiple of 512 bits */
#define TEST4 TEST4a TEST4b
char *testarray[4] =
{
	TEST1,
	TEST2,
	TEST3,
	TEST4
};

long int repeatcount[4] = { 1, 1, 1000000, 10 };
char *resultarray[4] =
{
	"A9 99 3E 36 47 06 81 6A BA 3E 25 71 78 50 C2 6C 9C D0 D8 9D",
	"84 98 3E 44 1C 3B D2 6E BA AE 4A A1 F9 51 29 E5 E5 46 70 F1",
	"34 AA 97 3C D4 C4 DA A4 F6 1E EB 2B DB AD 27 31 65 34 01 6F",
	"DE A3 56 A2 CD DD 90 C7 A7 EC ED C5 EB B5 63 93 4F 46 04 52"
};

int main()
{
	SHA1Context sha;
	int i, j, err;
	uint8_t Message_Digest[20];
	/*
	* Perform SHA-1 tests
	*/
	for(j = 0; j < 4; ++j)
	{
		//printf( "\nTest %d: %ld, ’%s’\n",j+1,repeatcount[j],testarray[j]);
		err = SHA1Reset(&sha);
		//if (err)
		//{
			//fprintf(stderr, "SHA1Reset Error %d.\n", err );
			//break; /* out of for j loop */
		//}
		for(i = 0; i < repeatcount[j]; ++i)
		{
			err = SHA1Input(&sha,
				(const unsigned char *) testarray[j],
				strlen(testarray[j]));
			if (err)
			{
				fprintf(stderr, "SHA1Input Error %d.\n", err );
				break; /* out of for i loop */
			}
		}
		err = SHA1Result(&sha, Message_Digest);
		if (err)
		{
			fprintf(stderr,
				"SHA1Result Error %d, could not compute message digest.\n",
				err );
		}
		else
		{
			printf("\t");
			for(i = 0; i < 20 ; ++i)
			{
				printf("%02X ", Message_Digest[i]);
			}
			printf("\n");
		}
		printf("Should match:\n");
		printf("\t%s\n", resultarray[j]);
	}

	/* Test some error returns */
	err = SHA1Input(&sha,(const unsigned char *) testarray[1], 1);
	printf ("\nError %d. Should be %d.\n", err, shaStateError );
	err = SHA1Reset(0);
	printf ("\nError %d. Should be %d.\n", err, shaNull );
	return 0;
}



相關推薦

SHA1演算法C語言實現

SHA1 安全雜湊演算法:對於長度小於2^64位的訊息(1M = 1024k,1K = 1024位元組,1BYTE = 8bit 可以想象一下2的63次方位可以表示一個多大的資料檔案),SHA1會產生一個160位的訊息摘要。當接收到訊息的時候,這個訊息摘要可以用來驗證資料的

Luhn algorithm(附信用卡演算法C語言實現

From Wikipedia, the free encyclopedia The Luhn algorithm or Luhn formula, also known as the "modulus 10" or "mod 10" algorithm, is a simple checksum formu

CRCC語言實現

ins rcc param into phoenix 兩個 The ide align 文章轉自 循環冗余校驗(CRC)算法入門引導 - Ivan 的專欄 - 博客頻道 - CSDN.NET http://blog.csdn.net/liyuanbhu/article/de

CRC32演算法C語言版(查表法)

最近用到CRC校驗演算法,就找了些資料,學習了一下,網上關於CRC32的資料也多,但感覺不是很完整,或者太高深。 CRC演算法查表法很常見,但表是怎麼來的,有些資料說得不很清楚。 我來說一下我的看法:

crc32c語言實現

最近在做軟體升級,需要對升級檔案進行crc校驗,就學習了crc的實現原理 crc就是一個數值,該數值用於檢驗資料的正確性,crc校驗的原理就是將需要作校驗的資料與一個數據模2相除,得到的餘數即為校驗值。       模2相除就是在除的過程中用模2加,模2加實際上就是異或

CRC16C語言實現

一、目的 闡述CRC16的原理,並以C語言程式碼實現。二、 校驗碼的作用 校驗碼用於校驗資料的有效性/正確性。 校驗碼用原資料生成,並伴隨原資料一起傳送/儲存,使用者拿到傳送/儲存的資料序列後,取出原資料部分,根據校驗碼生成規則生成校驗碼,與拿到的校驗碼進行比較即可判斷資料是

SHA-1演算法C語言實現

> 程式碼轉載自:https://blog.csdn.net/testcs_dn/article/details/25771377?locationNum=13&fps=1 > 感謝博主分享 #include<stdio.h> void creat_w(uns

頁面置換演算法——最近最久未使用演算法(c語言實現)

作業系統實驗:用C語言程式設計實現最近最久未使用置換演算法(LRU) 最近最久未使用置換演算法(LRU),全稱Least Recently Used,是一種頁面置換演算法。 對於在記憶體中但又不用的資料塊(記憶體塊)叫做LRU,作業系統會根據哪些資料屬於LRU而將其移出記憶體而騰出空間來載入另外

建立雙向連結串列的演算法——C語言實現

建立雙向連結串列的演算法——C語言實現 雙向連結串列也叫雙鏈表,是連結串列的一種,它的每個節點包含兩個指標,分別指向直接後繼和直接前驅(頭節點的前驅指空,尾節點的後繼指空)。所以,從雙向連結串列中的任意一個非前驅非後繼節點開始,都能很方便地訪問它的前驅和後繼節點。 實際上如果熟練掌握了單向連

差分進化演算法 C語言實現

之前的一篇中貼出了自己研究生期間C實現的基本粒子群演算法,執行速度顯然要比其他的高階語言快,這也是各個程式語言之間的差別,現在對於曾經輝煌過的差分進化演算法進行C語言實現。變異策略採用DE/rand/1,這個是最常見的。有錯誤之處請之處。 /***************D

氣泡排序演算法C語言實現

第一部分   排序方法介紹 常用的排序方法:氣泡排序,選擇排序,插入排序及希爾排序等。        氣泡排序是常用的一種排序方法,其基本方法就是逐次比較。即一次比較兩個數,若它們的順序錯誤,則交換;重複進行,知道沒有需要交換為止。 以升序排序為例:        1.

MD5加密演算法C語言實現

md5.h #ifndef MD5_H #define MD5_H typedef struct { unsigned int count[2]; unsigned int state[4]; unsigned char buffe

磁碟排程演算法C語言實現

最短尋道時間優先(SSTF)演算法。要求訪問的磁軌,與當前磁頭所在的磁軌距離最近,以使每次的尋道時間最短。掃描排程(SCAN)演算法。該演算法不僅考慮到欲訪問的磁軌與當前磁軌間的距離,更優先考慮的是磁頭當前的移動方向。例如,當磁頭正在自裡向外移動時,SCAN演算法所考慮的下一

非常值得一看—九種濾波演算法C語言實現

關注“嵌入式軟體開發學習圈”免費獲取更多學習教程 今天帶著大家學習濾波演算法c語言(九種濾波演算法)實現,以及程式碼,大家可以學習瞭解下。。。。 1.限幅濾波演算法(程式判斷濾波演算法) 方法解析: 根據經驗判斷,確定兩次取樣允許的最

作業排程之先來先服務演算法C語言實現

程式碼如下 /*    @author WellsLiu    @url liuyanzhao.com*/#include"stdio.h"#include"stdlib.h"typedef st

處理機排程演算法C語言實現(註釋得當!!)

/* created by herbert on 10 Nov */ #include <iostream> #include <queue> #include <algorithm> #include <c

最短路徑之Dijkstra演算法 C語言實現

Dijkstra演算法(單源點路徑演算法,要求:圖中不存在負權值邊): 步驟: a.  初始時,S只包含源點,即S={v},v的距離為0。U包含除v外的其他頂點,即: U={其餘頂點},若v與U中頂點u有邊,則u的距離設定為相應的權值,若u v之間不存在邊,則    

SHA-256演算法 C語言實現

#include <stdio.h> #include <stdlib.h> #define SHA256_ROTL(a,b) (((a>>(32-b))&(0x7fffffff>>(31-b)))|(a<<

哈夫曼壓縮演算法C語言實現——步驟,詳細註釋原始碼

哈夫曼壓縮演算法的詳細實現步驟: 1、定義哈夫曼樹節點,用結構體。 2、利用C語言檔案讀寫,統計字元個數。 3、根據字元個數建立哈夫曼樹(不懂haffman資料結構的自己查下資料,我這裡就不再重複了) 4、根據哈夫曼樹為每個出現的字元編碼 5、壓縮:這裡涉及到位操作,用ch