让你提前认识软件开发(10):字符串处理函数及异常保护

简介: 第1部分 重新认识C语言字符串处理函数及异常保护         在软件开发项目中,经常有程序要对字符串进行操作。为此,C函数库中提供了一些用来对字符串进行处理的函数,使用起来非常的方便。

第1部分 重新认识C语言

字符串处理函数及异常保护

 

        在软件开发项目中,经常有程序要对字符串进行操作。为此,C函数库中提供了一些用来对字符串进行处理的函数,使用起来非常的方便。但由于字符串都有长度,如果随意对不同的字符串进行连接和拷贝等操作,就可能出现意想不到的后果。

       因此,在实际开发过程中,十分强调对字符串处理函数进行异常保护。本文详细介绍如何正确运用字符串处理函数进行C程序设计。

 

       1. strcatstrncat函数

        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. strcpystrncpy函数

         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. strcmpstrcnmp函数

         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语言的过程中,并没有对字符串处理函数的异常保护进行过多的强调,这是学校教育的失误。在实际的软件开发项目中,很重视对代码的异常保护,这不仅涉及到产品质量,也关系到公司的声誉。因此,在编写程序的过程中,我们要时刻将异常保护牢记心中,这样才能写出健壮的、高质量的代码。


 

(欢迎访问南邮BBS:http://bbs.njupt.edu.cn/)
(欢迎访问重邮BBS:http://bbs.cqupt.edu.cn/nForum/index)

(本系列文章每周更新两篇,敬请期待!本人新浪微博:http://weibo.com/zhouzxi?topnav=1&wvr=5,微信号:245924426,欢迎关注!)

目录
相关文章
|
8月前
|
PHP 开发者 UED
PHP编程中的错误处理与异常管理
【8月更文挑战第27天】在PHP编程的世界中,错误和异常是开发者常遇到的两大挑战。本文旨在通过浅显易懂的方式,引导读者理解如何在PHP代码中妥善处理错误和异常。我们将从基础的错误处理讲起,逐步深入到异常管理的高级技巧,确保你的代码在遇到问题时能够优雅地处理,而不是崩溃。文章将用实例说明如何捕获、记录和处理这些事件,以保障应用的稳定性和用户体验。
八股day02_程序流程控制和数据类型
八股day02_程序流程控制和数据类型
|
9月前
|
C++ 开发者 运维
开发与运维传递问题之指针传递如何解决
开发与运维传递问题之指针传递如何解决
65 8
|
9月前
|
编译器 运维
开发与运维函数问题之声明一个简单的函数模板,函数模板工作如何解决
开发与运维函数问题之声明一个简单的函数模板,函数模板工作如何解决
36 5
|
11月前
|
存储 Java 关系型数据库
定时任务配置技巧:将表达式配置在业务员代码之外的方法
该文档介绍了三种不同的方法来定义和管理Java中的定时任务。首先,通过在数据库中创建一个表来存储定时任务的执行周期,并在Spring Boot应用中使用`@Scheduled`注解配合数据库查询来动态执行定时任务。其次,将定时任务的配置移动到Apollo配置中心,利用Apollo的配置能力来控制定时任务的执行。最后,使用Quartz框架并结合Apollo配置文件,动态地管理定时任务的触发间隔和执行时间。此外,还提到了在多机器环境中,可以使用分布式锁来避免任务重复,并推荐了xxl-JOB和elastic-job作为更专业的定时任务解决方案。
138 2
|
11月前
|
安全 编译器 C#
C#中的可空引用类型:减少空引用异常的利器
【1月更文挑战第9天】C# 8.0中引入的可空引用类型特性,它通过在编译时提供更精确的静态分析,帮助开发者减少运行时的空引用异常。文章详细阐述了可空引用类型的工作原理、如何配置项目以使用此特性,以及在实际编码中如何利用可空引用类型提升代码的健壮性和可读性。
|
11月前
|
算法 编译器 C++
【C++11保姆级教程】深入浅出异常说明符、异常操作符和lambda表达式
【C++11保姆级教程】深入浅出异常说明符、异常操作符和lambda表达式
162 0
|
存储 安全 数据库连接
27.从入门到精通:Python异常处理 抛出异常 用户自定义异常 定义清理行为 预定义的清理行为
27.从入门到精通:Python异常处理 抛出异常 用户自定义异常 定义清理行为 预定义的清理行为
|
C语言 C++
【异常机制高级讲解】异常与多态综合案例
【异常机制高级讲解】异常与多态综合案例
126 0
|
JavaScript 前端开发
0308-变量声明提前
变量会先声明后赋值 test(); function test() { console.
777 0