【阅读笔记】《C程序员 从校园到职场》第五章 内存操作

参考:  

让你提前认识软件开发(8):memset()与memcpy()函数  https://blog.csdn.net/zhouzxi/article/details/22478081

让你提前认识软件开发(10):字符串处理函数及异常保护 https://blog.csdn.net/zhouzxi/article/details/22976307

一、memset  &  memcpy

1. memset()函数

(1)函数原型

        void *memset( void *dest, int c, size_t count )。

(2)函数作用

在MSDN中,将该函数的作用描述为:Sets buffers to a specified character,

即:将缓存设定为一个专门的字符。简单地说,就是将已开辟内存空间dest的首count个字节的值设为值c。

  (3)重要应用

除了对指定位置设定值之外,该函数还可以用于内存空间初始化、内存拷贝、清空一个结构类型的变量或数组等。

(4)应用举例

/***************************************************************
*版权所有 (C)2014, Zhou Zhaoxiong。
*
*文件名称:memset.c
*内容摘要:用于测试memset函数
***************************************************************/

#include <memory.h>
#include <stdio.h>

typedef signed char INT8;                       //重定义数据类型
typedef signed int  INT32;                     //重定义数据类型

/**********************************************************************
 *功能描述:主函数
 *输入参数:无
 *输出参数:无
 *返回值:无
 *其它说明:无
 *修改日期           版本号          修改人        修改内容
 * ----------------------------------------------------------------------
 * 20140329            V1.0            周兆熊          创建
 ***********************************************************************/
INT32 main(void)
{
    INT8 szTestStr[100] = "AAAAAAAAAA";     

    printf("The original string is: %s\n", szTestStr);
    memset(szTestStr, ‘B‘, 5);     //调用memset函数
    printf("The changed string is: %s\n", szTestStr);
    return 0;
}

程序结果:(将原字符串的前5个字符设为B)

The original string is: AAAAAAAAAA
The changed string is: BBBBBAAAAA

 2. memcpy()函数

(1)函数原型

void *memcpy( void *dest, const void *src, size_t count );

(2)函数作用

在MSDN中,将该函数的作用描述为:Copies characters between buffers,即:在缓存之间拷贝字符。也就是说,该函数用来拷贝src所指的内存内容前count个字节到dest所指的内存地址上。

  (3) 重要说明

src和dest所指内存区域不能重叠,函数返回指向dest的指针。

  (4) 应用举例

  程序结果:(将原字符串的第二至第四个字符设为B)

  The original string is: AAAAAAAAAA
  The changed string is: ABBBAAAAAA
/***************************************************************
*版权所有 (C)2014, Zhou Zhaoxiong。
*
*文件名称:memcpy.c
*内容摘要:用于测试memcpy函数
***************************************************************/

#include <memory.h>
#include <stdio.h> 

typedef signed char INT8;                       //重定义数据类型
typedef signed int  INT32;                     //重定义数据类型

/**********************************************************************
 *功能描述:主函数
 *输入参数:无
 *输出参数:无
 *返回值:无
 *其它说明:无
 *修改日期           版本号          修改人        修改内容
 * --------------------------------------------------------------------------
 * 20140329            V1.0            周兆熊          创建
 ***********************************************************************/

INT32 main(void)
{
    INT8 szTestStr[100] = "AAAAAAAAAA";
    INT8 szCopyStr[100] = "BBBBBBBBBB";      

    printf("The original string is: %s\n", szTestStr);     

    memcpy(szTestStr+1, szCopyStr+5, 3);     //调用memcpy函数    

    printf("The changed string is: %s\n", szTestStr);
   return 0;
}

  在软件开发项目中,经常有程序要对字符串进行操作。为此,C函数库中提供了一些用来对字符串进行处理的函数,使用起来非常的方便。

  但由于字符串都有长度,如果随意对不同的字符串进行连接和拷贝等操作,就可能出现意想不到的后果。

因此,在实际开发过程中,十分强调对字符串处理函数进行异常保护

      下面将介绍  strcat  &  strncat    、strcpy  &  strncpy  、strcmp  &  strncmp 的使用方法。

二、strcat  &  strncat

 1、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函数
***************************************************************/

#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函数进行字符串的连接操作时,异常就出现了。

2、为了解决这个问题,在使用strcat函数之前,需要先对字符串和字符数组的长度进行比较,让字符串函数进行安全的连接操作

  即保证最终字符串的长度不超过目的字符数组的长度。修改后的代码如下所示:

/***************************************************************
*版权所有 (C)2014, Zhou Zhaoxiong。
*文件名称:StrcatTest.c
*内容摘要:用于测试strcat函数
***************************************************************/

#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;}

如上代码所示,当源字符串太长时,程序便异常退出了,不会执行后面的字符串连接操作。

虽然可以保证程序的正常运行,但毕竟没有完成我们想要的功能。

有不有其他办法呢?既能保证连接操作的正常进行,又不会让程序弹出异常消息框。

3、strncat函数

  strncat函数只将strSource数组中的前count个字符拼接到strDest数组的后面。

  因此,不管strSource数组中的字符串有多长,只要count控制得当,都不会出现字符串的越界错误。

  鉴于strcat函数的缺陷和strncat函数的优点,实际的软件开发项目中,均推荐用strncat函数代替strcat函数来实现字符串的连接。

  用strncat函数替换strcat函数后的代码如下:

#include <stdio.h>
#include <string.h>

typedef signed char INT8;                        //重定义数据类型
typedef signed int  INT32;                       //重定义数据类型

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;
}

三、strcpy  &  strncpy

1、strcpy函数 

  将一个字符数组中的字符串拷贝到另外一个字符数组中。

  在MSDN中,其定义为:

        char *strcpy( char *strDestination, const char *strSource );

        strcpy函数将strSource字符串拷贝到strDestination数组中,最后的返回值是拷贝完成之后的字符串strDestination。

  这里有一个问题,如果字符串strSource的长度大于了strDestination数组的长度,就会出现数组越界的错误,程序就会崩溃。执行代码后弹出的异常框如下图所示。

2、 为了解决这个问题,在使用strcpy函数之前,需要先对字符串和字符数组的长度进行比较,让字符串函数进行安全的拷贝操作,即保证被拷贝字符串的长度不超过目的字符数组的长度。

#include <stdio.h>
#include <string.h> 

typedef signed char INT8;                        //重定义数据类型
typedef signed int  INT32;                       //重定义数据类型

 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;
}

如上代码所示,当源字符串太长时,程序便异常退出了,不会执行后面的字符串拷贝操作。虽然可以保证程序的正常运行,但毕竟没有完成我们想要的功能,有不有其他办法呢?既能保证拷贝操作的正常进行,又不会让程序弹出异常消息框。

3、为了解决strcpy函数可能出现的问题,C语言函数库提供了一个叫做strncpy的函数

在MSDN中,其定义为:

       char *strncpy( char *strDest, const char *strSource, size_t count );

      strncpy函数只将strSource数组中的前count个字符拷贝到strDest数组中。因此,不管strSource数组中的字符串有多长,只要count控制得当,都不会出现字符串的越界错误。用strncpy函数替换strcpy函数后的代码如下:

#include <stdio.h>
#include <string.h> 

typedef signed char INT8;                        //重定义数据类型
typedef signed int  INT32;                       //重定义数据类型

 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函数来实现字符串的拷贝。

四、strcmp  &  strncmp

1、 strcmp函数    进行字符串的比较。

  在MSDN中,其定义为:

         int strcmp( const char *string1, const char *string2 );

         strcmp函数进行字符串的比较时,如果前者大于后者,则返回值大于1;如果前者等于后者,则返回值等于0;如果前者小于后者,则返回值小于0。如下代码所示:

#include <stdio.h>
#include <string.h> 

typedef signed char INT8;                        //重定义数据类型
typedef signed int  INT32;                       //重定义数据类型

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;
}

3、strncmp函数  用于字符串的比较

  在MSDN中,其定义为:

        int strncmp( const char *string1, const char *string2, size_t count );

        strncmp函数只比较两个字符串数组的前count个字符,如果返回值小于0,则前一个字符串要小;如果返回值等于0,则两个字符串相等;如果返回值大于0,则前一个字符串要大。用strncmp函数替换strcmp函数后的代码如下:

#include <stdio.h>
#include <string.h> 

typedef signed char INT8;                        //重定义数据类型
typedef signed int  INT32;                       //重定义数据类型

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

 

 

原文地址:https://www.cnblogs.com/hhxxgdd/p/8796937.html

时间: 2024-10-27 02:39:58

【阅读笔记】《C程序员 从校园到职场》第五章 内存操作的相关文章

【阅读笔记】《C程序员 从校园到职场》第二章 学校到职场

一.代码规范: 1.变量命名(让人一眼看它是什么意思,要做什么操作),定义并初始化 2.函数命名规范(函数的功能)在主函数之前进行声明. 在实际项目中,一般不在函数调用者的内部来对被调函数进行声明,而是将声明放在外部(新建一个.h文件,对程序中出现的函数进行声明) 3.代码排版(主要涉及缩进.空格.空行.括号位置) 4.程序注释(版本信息.修改记录),函数注释(函数的功能.输入输出值.返回值.修改记录). 5.关于输入输出,开发中会有专门的消息处理流程(不一定非有 printf.scanf.ma

《C程序员从校园到职场》勘误

(本人正在参加2015博客之星评选,诚邀你来投票,谢谢:http://vote.blog.csdn.net/blogstar2015/candidate?username=zhouzxi) 自从<C程序员从校园到职场>一书上市以来,受到了大家的欢迎,很多读者通过各种途径向我反馈了读书心得,在此表示感谢.但"金无赤足,人无完人",书中的内容也存在一些纰漏.本文用于持续收集目前发现的本书的一些问题,也希望大家将对本书的一些意见或改进建议及时告知我. 本书的勘误如下: 1.P5,

【阅读笔记】《C程序员 从校园到职场》第三章 程序的样式(大括号)

参考: https://blog.csdn.net/zhouzhaoxiong1227/article/details/22820533 一..初始化数组变量 在实际的软件开发项目中,变量在使用前应初始化,防止未经初始化的变量被引用. 对于数组变量来说,恰当的初始化样例如下:           char szString[100] = {0}; 定义了一个名为szString的包含100个字节的数组变量,并将该数组中每个变量的初始值赋为0. 二. 定义和初始化结构体变量 结构体变量的定义方法的

【阅读笔记】《C程序员 从校园到职场》第六章 配置文件,makefile 文件 (Part 2)

 Contents: 1.配置文件(通常以 ini 结尾) 2.makefile文件 (Linux) PS: 这篇文章的内容,不太理解. 一.配置文件 本文以一个实际的小软件为例,介绍了C语言中配置文件的读取方法和重要的文件操作函数的使用方法,为相关软件开发项目提供了有益的参考 参考链接:让你提前认识软件开发(12):配置文件读取及文件操作 CSDN博客 https://blog.csdn.net/zhouzhaoxiong1227/article/details/23552667 2.4本文中

【阅读笔记】《C程序员 从校园到职场》第七章 指针和结构体

原文地址:让你提前认识软件开发(13):指针及结构体的使用 CSDN博客 https://blog.csdn.net/zhouzhaoxiong1227/article/details/23872995 [文章摘要] 指针在C语言中占有很重要的地位,同时也是学习C语言的难点所在.结构体属于用户自己建立的数据类型,在实际的软件开发项目中应用很广泛. 本文以实际的例子介绍了C语言中指针和结构体的使用方法,为进一步的学习和应用提供了有益的参考. [关键词] C语言 指针  结构体 文件 1.指针和结构

【阅读笔记】《C程序员 从校园到职场》第六章 常用文件操作函数 (Part 1)

参考链接:https://blog.csdn.net/zhouzhaoxiong1227/article/details/24926023 让你提前认识软件开发(18):C语言中常用的文件操作函数总结及使用方法演示代码 - CSDN博客 Contents: 1. C语言中常用的文件操作函数总结 (1) fopen   作用:打开文件 (2) fclose  作用:关闭文件 (3) fgetc   作用:由文件中读取一个字符. (4) fgets   作用:由文件中读取一字符串. (5) fflu

C语言编程标准 ----自《C程序员从校园到职场》

/*************************************************** * 版权所有   公司或者个人名称 * * 文件名称: * 内容摘要: * 其他说明: * 当前版本: * 作     者: * 完成日期: * * 修改记录1: *    修改日期: *    版 本  号: *    修 改  人: *    修改内容: * * 修改记录2: *    修改日期: *    版 本  号: *    修 改  人: *    修改内容: ********

C 内存操作 ---自《C程序员从校园到职场》

1.  memset  and memcpy /**********************************************/ 2. strcat and strncat /*********************************************/ 3.strcpy and strncpy /*********************************************/ 4.strcmp and strncmp

[阅读笔记]程序员修炼之道

一.注重实效的哲学 1.负责.准备告诉别人什么做不到前,先演练一遍,他人可能会说:试过这个吗?提供选择和解决方案, 而不是借口,需要重构,建立原型,测试,别的资源?提出要求和寻求帮助 2.软件的熵.杜绝破窗户,一个破窗会让优秀的系统加速腐烂. 3.石头汤的故事,设计合理的需求目标系统愿景,团结一切力量,大家都觉得参与正在发生的更容易成功. “我们要增加点...可能更好”并假装这不重要. 防止被温水煮青蛙,时刻关注周围的变化. 4.质量与需求之间的权衡.让用户及早的反馈并改良软件. 5.投资自己,