【阅读笔记】《C程序员 从校园到职场》第五章 内存操作
参考:
让你提前认识软件开发(8):memset()与memcpy()函数 https://blog.csdn.net/zhouzxi/article/details/22478081
让你提前认识软件开发(10):字符串处理函数及异常保护 https://blog.csdn.net/zhouzxi/article/details/22976307
Contents:
一、memset & memcpy
二、strcat & strncat
三、strcpy & strncpy
四、strcmp & strncmp
一、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函数进行字符串的比较时,如果前者大于后者,则返回值大于0;如果前者等于后者,则返回值等于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语言的过程中,并没有对字符串处理函数的异常保护进行过多的强调,这是学校教育的失误。在实际的软件开发项目中,很重视对代码的异常保护,这不仅涉及到产品质量,也关系到公司的声誉。因此,在编写程序的过程中,我们要时刻将异常保护牢记心中,这样才能写出健壮的、高质量的代码。