第1部分 重新認識C語言
字元串處理函數及異常保護
C函數庫中提供了一些用來對字元串進行處理的函數,使用起來非常的友善。但由于字元串都有長度,如果随意對不同的字元串進行連接配接和拷貝等操作,就可能出現意想不到的後果。
C程式設計。
1. strcat和strncat函數
strcat函數的作用是連接配接兩個字元數組中的字元串。在MSDN中,其定義為:
char *strcat( char *strDestination, const char *strSource );
Remarks: The strcat function appends strSource to strDestination and terminates the resulting string with a null character. The initial character of strSource overwrites the terminating null character of strDestination. It returns the destination string (strDestination).
strcat函數将strSource字元串拼接到strDestination後面,最後的傳回值是拼裝完成之後的字元串strDestination。
strSource的長度大于了strDestination數組的長度,就會出現數組越界的錯誤,程式就會崩潰。如下代碼所示:
/***************************************************************
*版權所有 (C)2014, Zhou Zhaoxiong。
*
*檔案名稱:StrcatTest.c
*内容摘要:用于測試strcat函數
*其它說明:無
*目前版本:V1.0
*作 者:周兆熊
*完成日期:20140405
*
*修改記錄1: //修改曆史記錄,包括修改日期、版本号、修改人及修改内容等
* 修改日期:
* 版本号:
* 修改人:
* 修改内容:
***************************************************************/
#include <stdio.h>
#include <string.h>
typedef signed char INT8; //重定義資料類型
typedef signed int INT32; //重定義資料類型
/**********************************************************************
*功能描述:主函數
*輸入參數:無
*輸出參數:無
*傳回值:無
*其它說明:無
*修改日期 版本号 修改人 修改内容
* ------------------------------------------------------------------------------------------
* 20140405 V1.0 周兆熊 建立
***********************************************************************/
INT32 main(void)
{
INT8 szStrDestination[10] = "Hello";
INT8 szStrSource[10] = "Hello123";
//先列印源字元串和目的字元串
printf("The source string is: %s\n", szStrSource);
printf("The destination string is: %s\n", szStrDestination);
strcat(szStrDestination, szStrSource); //調用strcat函數
//列印拼裝完成之後的字元串
printf("The changed destination string is: %s\n", szStrDestination);
return 0;
}
szStrDestination數組的長度小于szStrSource中字元串的長度,當利用strcat函數進行字元串的連接配接操作時,異常就出現了。執行代碼後彈出的異常框如下圖所示。
strcat函數之前,需要先對字元串和字元數組的長度進行比較,讓字元串函數進行安全的連接配接操作,即保證最終字元串的長度不超過目的字元數組的長度。修改後的代碼如下所示:
/***************************************************************
*版權所有 (C)2014, Zhou Zhaoxiong。
*
*檔案名稱:StrcatTest.c
*内容摘要:用于測試strcat函數
*其它說明:無
*目前版本:V1.0
*作 者:周兆熊
*完成日期:20140405
*
*修改記錄1: //修改曆史記錄,包括修改日期、版本号、修改人及修改内容等
* 修改日期:
* 版本号:
* 修改人:
* 修改内容:
***************************************************************/
#include <stdio.h>
#include <string.h>
typedef signed char INT8; //重定義資料類型
typedef signed int INT32; //重定義資料類型
/**********************************************************************
*功能描述:主函數
*輸入參數:無
*輸出參數:無
*傳回值:無
*其它說明:無
*修改日期 版本号 修改人 修改内容
* -------------------------------------------------------------------------------------------
* 20140405 V1.0 周兆熊 建立
***********************************************************************/
INT32 main(void)
{
INT8 szStrDestination[10] = "Hello";
INT8 szStrSource[10] = "Hello123";
//先列印源字元串和目的字元串
printf("The source string is: %s\n", szStrSource);
printf("The destination string is: %s\n", szStrDestination);
//對目的字元數組的長度進行異常保護 begin
if (sizeof(szStrDestination) - strlen(szStrDestination) < strlen(szStrSource))
{
printf("The source string is too long!\n"); //列印異常消息
return 1; //異常傳回
}
//對目的字元數組的長度進行異常保護 end
strcat(szStrDestination, szStrSource); //調用strcat函數
//列印拼裝完成之後的字元串
printf("The changed destination string is: %s\n", szStrDestination);
return 0;
}
如上代碼所示,當源字元串太長時,程式便異常退出了,不會執行後面的字元串連接配接操作。雖然可以保證程式的正常運作,但畢竟沒有完成我們想要的功能,有不有其他辦法呢?既能保證連接配接操作的正常進行,又不會讓程式彈出異常消息框。
strcat函數可能出現的問題,C語言函數庫提供了一個叫做strncat的函數。在MSDN中,其定義為:
char *strncat( char *strDest, const char *strSource, size_t count );
Remarks: The strncat function appends, at most, the first count characters of strSource to strDest. The initial character of strSource overwrites the terminating null character of strDest. If a null character appears in strSource before count characters are appended, strncat appends all characters from strSource, up to the null character. If count is greater than the length of strSource, the length of strSource is used in place of count. The resulting string is terminated with a null character.
strncat函數隻将strSource數組中的前count個字元拼接到strDest數組的後面。是以,不管strSource數組中的字元串有多長,隻要count控制得當,都不會出現字元串的越界錯誤。用strncat函數替換strcat函數後的代碼如下:
/***************************************************************
*版權所有 (C)2014, Zhou Zhaoxiong。
*
*檔案名稱:StrncatTest.c
*内容摘要:用于測試strncat函數
*其它說明:無
*目前版本:V1.0
*作 者:周兆熊
*完成日期:20140405
*
*修改記錄1: //修改曆史記錄,包括修改日期、版本号、修改人及修改内容等
* 修改日期:
* 版本号:
* 修改人:
* 修改内容:
***************************************************************/
#include <stdio.h>
#include <string.h>
typedef signed char INT8; //重定義資料類型
typedef signed int INT32; //重定義資料類型
/**********************************************************************
*功能描述:主函數
*輸入參數:無
*輸出參數:無
*傳回值:無
*其它說明:無
*修改日期 版本号 修改人 修改内容
* ----------------------------------------------------------------------------------------
* 20140405 V1.0 周兆熊 建立
***********************************************************************/
INT32 main(void)
{
INT8 szStrDestination[10] = "Hello";
INT8 szStrSource[10] = "Hello123";
//先列印源字元串和目的字元串
printf("The source string is: %s\n", szStrSource);
printf("The destination string is: %s\n", szStrDestination);
//調用strncat函數
strncat(szStrDestination, szStrSource,sizeof(szStrDestination)-strlen(szStrDestination));
//列印拼裝完成之後的字元串
printf("The changed destination string is: %s\n", szStrDestination);
return 0;
}
strcat函數的缺陷和strncat函數的優點,實際的軟體開發項目中,均推薦用strncat函數代替strcat函數來實作字元串的連接配接。
2. strcpy和strncpy函數
strcpy函數的作用是将一個字元數組中的字元串拷貝到另外一個字元數組中。在MSDN中,其定義為:
char *strcpy( char *strDestination, const char *strSource );
Remarks: The strcpy function copies strSource, including the terminating null character, to the location specified by strDestination. It returns the destination string.
strcpy函數将strSource字元串拷貝到strDestination數組中,最後的傳回值是拷貝完成之後的字元串strDestination。
這裡有一個問題,如果字元串strSource的長度大于了strDestination數組的長度,就會出現數組越界的錯誤,程式就會崩潰。如下代碼所示:
/***************************************************************
*版權所有 (C)2014, Zhou Zhaoxiong。
*
*檔案名稱:StrcpyTest.c
*内容摘要:用于測試strcpy函數
*其它說明:無
*目前版本:V1.0
*作 者:周兆熊
*完成日期:20140405
*
*修改記錄1: //修改曆史記錄,包括修改日期、版本号、修改人及修改内容等
* 修改日期:
* 版本号:
* 修改人:
* 修改内容:
***************************************************************/
#include <stdio.h>
#include <string.h>
typedef signed char INT8; //重定義資料類型
typedef signed int INT32; //重定義資料類型
/**********************************************************************
*功能描述:主函數
*輸入參數:無
*輸出參數:無
*傳回值:無
*其它說明:無
*修改日期 版本号 修改人 修改内容
* -----------------------------------------------------------------------------------------------------
* 20140405 V1.0 周兆熊 建立
***********************************************************************/
INT32 main(void)
{
INT8 szStrDestination[5] = {0}; //定義的同時,對變量進行初始化
INT8 szStrSource[10] = "Hello1234";
//先列印源字元串和目的字元串
printf("The source string is: %s\n", szStrSource);
printf("The destination string is: %s\n", szStrDestination);
strcpy(szStrDestination, szStrSource); //調用strcpy函數
//列印拷貝完成之後的字元串
printf("The changed destination string is: %s\n", szStrDestination);
return 0;
}
szStrDestination數組的長度小于szStrSource中字元串的長度,當利用strcpy函數進行字元串的拷貝操作時,異常就出現了。執行代碼後彈出的異常框如下圖所示。
strcpy函數之前,需要先對字元串和字元數組的長度進行比較,讓字元串函數進行安全的拷貝操作,即保證被拷貝字元串的長度不超過目的字元數組的長度。修改後的代碼如下所示:
/***************************************************************
*版權所有 (C)2014, Zhou Zhaoxiong。
*
*檔案名稱:StrcpyTest.c
*内容摘要:用于測試strcpy函數
*其它說明:無
*目前版本:V1.0
*作 者:周兆熊
*完成日期:20140405
*
*修改記錄1: //修改曆史記錄,包括修改日期、版本号、修改人及修改内容等
* 修改日期:
* 版本号:
* 修改人:
* 修改内容:
***************************************************************/
#include <stdio.h>
#include <string.h>
typedef signed char INT8; //重定義資料類型
typedef signed int INT32; //重定義資料類型
/**********************************************************************
*功能描述:主函數
*輸入參數:無
*輸出參數:無
*傳回值: 無
*其它說明:無
*修改日期 版本号 修改人 修改内容
* -----------------------------------------------------------------------------------------------------
* 20140405 V1.0 周兆熊 建立
***********************************************************************/
INT32 main(void)
{
INT8 szStrDestination[5] = {0}; //定義的同時,對變量進行初始化
INT8 szStrSource[10] = "Hello1234";
//先列印源字元串和目的字元串
printf("The source string is: %s\n", szStrSource);
printf("The destination string is: %s\n", szStrDestination);
//對目的字元數組的長度進行異常保護 begin
if (sizeof(szStrDestination) < strlen(szStrSource))
{
printf("The source string is too long!\n"); //列印異常消息
return 1; //異常傳回
}
//對目的字元數組的長度進行異常保護 end
strcpy(szStrDestination, szStrSource); //調用strcpy函數
//列印拷貝完成之後的字元串
printf("The changed destination string is: %s\n", szStrDestination);
return 0;
}
如上代碼所示,當源字元串太長時,程式便異常退出了,不會執行後面的字元串拷貝操作。雖然可以保證程式的正常運作,但畢竟沒有完成我們想要的功能,有不有其他辦法呢?既能保證拷貝操作的正常進行,又不會讓程式彈出異常消息框。
strcpy函數可能出現的問題,C語言函數庫提供了一個叫做strncpy的函數。在MSDN中,其定義為:
char *strncpy( char *strDest, const char *strSource, size_t count );
Remarks: The strncpy function copies the initial count characters of strSource to strDest and returns strDest. If count is less than or equal to the length of strSource, a null character is not appended automatically to the copied string. If count is greater than the length of strSource, the destination string is padded with null characters up to length count. It returns strDest.
strncpy函數隻将strSource數組中的前count個字元拷貝到strDest數組中。是以,不管strSource數組中的字元串有多長,隻要count控制得當,都不會出現字元串的越界錯誤。用strncpy函數替換strcpy函數後的代碼如下:
/***************************************************************
*版權所有 (C)2014, Zhou Zhaoxiong。
*
*檔案名稱:StrncpyTest.c
*内容摘要:用于測試strncpy函數
*其它說明:無
*目前版本:V1.0
*作 者:周兆熊
*完成日期:20140405
*
*修改記錄1: //修改曆史記錄,包括修改日期、版本号、修改人及修改内容等
* 修改日期:
* 版本号:
* 修改人:
* 修改内容:
***************************************************************/
#include <stdio.h>
#include <string.h>
typedef signed char INT8; //重定義資料類型
typedef signed int INT32; //重定義資料類型
/**********************************************************************
*功能描述:主函數
*輸入參數:無
*輸出參數:無
*傳回值:無
*其它說明:無
*修改日期 版本号 修改人 修改内容
* ---------------------------------------------------------------------------------------------------
* 20140405 V1.0 周兆熊 建立
***********************************************************************/
INT32 main(void)
{
INT8 szStrDestination[5] = {0}; //定義的同時,對變量進行初始化
INT8 szStrSource[10] = "Hello1234";
//先列印源字元串和目的字元串
printf("The source string is: %s\n", szStrSource);
printf("The destination string is: %s\n", szStrDestination);
//調用strncpy函數,并進行長度判斷 begin
if (sizeof(szStrDestination) < strlen(szStrSource))
{
strncpy(szStrDestination, szStrSource, sizeof(szStrDestination));
}
else
{
strncpy(szStrDestination, szStrSource, strlen(szStrSource));
}
//調用strncpy函數,并進行長度判斷 end
//列印拷貝完成之後的字元串
printf("The changed destination string is: %s\n", szStrDestination);
return 0;
}
strcpy函數的缺陷和strncpy函數的優點,實際的軟體開發項目中,均推薦用strncpy函數代替strcpy函數來實作字元串的拷貝。
3. strcmp和strcnmp函數
strcmp函數的作用是進行字元串的比較。在MSDN中,其定義為:
int strcmp( const char *string1, const char *string2 );
Remarks: The strcmp function compares string1 and string2 lexicographically and returns a value indicating their relationship. If return value < 0, string1 less than string2; if return value = 0, string1 identical to string2; if return value > 0, string1 greater than string2.
strcmp函數進行字元串的比較時,如果前者大于後者,則傳回值大于0;如果前者等于後者,則傳回值等于0;如果前者小于後者,則傳回值小于0。如下代碼所示:
/***************************************************************
*版權所有 (C)2014, Zhou Zhaoxiong。
*
*檔案名稱:StrcmpTest.c
*内容摘要:用于測試strcmp函數
*其它說明:無
*目前版本:V1.0
*作 者:周兆熊
*完成日期:20140405
*
*修改記錄1: //修改曆史記錄,包括修改日期、版本号、修改人及修改内容等
* 修改日期:
* 版本号:
* 修改人:
* 修改内容:
***************************************************************/
#include <stdio.h>
#include <string.h>
typedef signed char INT8; //重定義資料類型
typedef signed int INT32; //重定義資料類型
/**********************************************************************
*功能描述:主函數
*輸入參數:無
*輸出參數:無
*傳回值:無
*其它說明:無
*修改日期 版本号 修改人 修改内容
* -----------------------------------------------------------------------------------------------
* 20140405 V1.0 周兆熊 建立
***********************************************************************/
INT32 main(void)
{
INT8 szStrCmp1[10] = "Hello";
INT8 szStrCmp2[10] = "Hello";
INT32 iRetVal = 0; //定義的同時,對變量進行初始化
//先列印源字元串和目的字元串
printf("The first string is: %s\n", szStrCmp1);
printf("The second string is: %s\n", szStrCmp2);
iRetVal = strcmp(szStrCmp1, szStrCmp2); //調用strcmp函數
if (iRetVal < 0)
{
printf("string1 is less than string2\n");
}
else if (iRetVal == 0) //注意:是“==”,而不是“=”
{
printf("string1 is identical to string2\n");
}
else
{
printf("string1 is greater than string2\n");
}
return 0;
}
strcmp函數之外,C語言函數庫還提供了一個叫做strncmp的函數用于字元串的比較。在MSDN中,其定義為:
int strncmp( const char *string1, const char *string2, size_t count );
Remarks: The strncmp function lexicographically compares, at most, the first count characters in string1 and string2 and returns a value indicating the relationship between the substrings.
strncmp函數隻比較兩個字元串數組的前count個字元,如果傳回值小于0,則前一個字元串要小;如果傳回值等于0,則兩個字元串相等;如果傳回值大于0,則前一個字元串要大。用strncmp函數替換strcmp函數後的代碼如下:
/***************************************************************
*版權所有 (C)2014, Zhou Zhaoxiong。
*
*檔案名稱:StrncmpTest.c
*内容摘要:用于測試strncmp函數
*其它說明:無
*目前版本:V1.0
*作 者:周兆熊
*完成日期:20140405
*
*修改記錄1: //修改曆史記錄,包括修改日期、版本号、修改人及修改内容等
* 修改日期:
* 版本号:
* 修改人:
* 修改内容:
***************************************************************/
#include <stdio.h>
#include <string.h>
typedef signed char INT8; //重定義資料類型
typedef signed int INT32; //重定義資料類型
/**********************************************************************
*功能描述:主函數
*輸入參數:無
*輸出參數:無
*傳回值:無
*其它說明:無
*修改日期 版本号 修改人 修改内容
* ----------------------------------------------------------------------------------------------------
* 20140405 V1.0 周兆熊 建立
***********************************************************************/
INT32 main(void)
{
INT8 szStrCmp1[10] = "Hello";
INT8 szStrCmp2[10] = "Hello";
INT32 iRetVal = 0; //定義的同時,對變量進行初始化
//先列印源字元串和目的字元串
printf("The first string is: %s\n", szStrCmp1);
printf("The second string is: %s\n", szStrCmp2);
//調用strncmp函數,第三個參數也可以是strlen(szStrCmp2)
iRetVal = strncmp(szStrCmp1, szStrCmp2, strlen(szStrCmp1));
if (iRetVal < 0)
{
printf("string1 is less than string2\n");
}
else if (iRetVal == 0) //注意:是“==”,而不是“=”
{
printf("string1 is identical to string2\n");
}
else
{
printf("string1 is greater than string2\n");
}
return 0;
}
count個字元相同,而後面的字元不一樣,那用strncmp函數判斷出來的結果就不準确了。确實是這樣的。在實際的軟體開發項目中,strncmp函數可用于僅需判斷某幾位字元是否相等的流程中,strncmp函數不能替代strcmp函數。
C語言的過程中,并沒有對字元串處理函數的異常保護進行過多的強調,這是學校教育的失誤。在實際的軟體開發項目中,很重視對代碼的異常保護,這不僅涉及到産品品質,也關系到公司的聲譽。是以,在編寫程式的過程中,我們要時刻将異常保護牢記心中,這樣才能寫出健壯的、高品質的代碼。