走进C的世界-那些年我们常犯的错---strcpy及memcpy函数
strcpy和memcpy函数是项目中常常常使用到的函数。可能因为使用不当造成数据错误或引发程序段错误等等。以下我们就来细细分析这两个很重要的函数。
/*File : strcpy_memcpy.c
*Auth : sjin
*Date : 20141019
*Mail : 413977143@qq.com
*/
/* 主要针对strcpy及与memcpy函数的差别
* strcpy 函数在使用过程中的注意事项等等。在面试时及平时
* 代码编写中常常遇到的问题。
*/
#include <stdio.h>
/*strcpy函数提供了字符串的复制,即仅仅用于字符串的复制。而且它不仅复制
* 字符串内容之外。还会复制字符串的结束符。遇到结束符则停止复制
*函数原型char *strcpy(char *dest,const char *src)
*/
char * mystrcpy(char *dest,const char *src)
{
if((dest == NULL) || (src == NULL)){
return NULL;
}
char *strdest = dest; //保存目标字符串的首地址
while((*strdest ++ = *src++) != '\0');
return dest;
}
/*memcpy函数提供了一般内存的复制,即对须要复制的内容没有限制
* 函数原型 void *memcpy(void * dest,const void * src,size_t count);
*
* 对于地址重叠情况 。该函数的是没有定义的
*/
void * mymemcpy1(void *dest,const void *src,size_t count)
{
/*未考虑内存重叠情况*/
if((dest == NULL) || (src == NULL)){
return NULL;
}
char *strdest = (char *)dest;
char *strsrc = (char *)src;
while(count-- > 0){
*strdest++ = *strsrc++;
}
return dest;
}
void * mymemcpy2(void *dest,const void *src,size_t count)
{
char *strdest = NULL;
char *strsrc = NULL;
/*内存重叠情况*/
if((dest == NULL) || (src == NULL)){
return NULL;
}
if((src < dest) && (((char *)src + count) > (char *)dest)){
strdest = (char *)dest + count - 1;
strsrc = (char *)src + count - 1;
while(count--){
*strdest-- = *strsrc--;
}
}else{
strdest = (char *)dest;
strsrc = (char *)src;
while(count-- > 0){
*strdest++ = *strsrc++;
}
}
return dest;
}
/*strcpy 与memcpy差别:
*1 复制的内容不同。strcpy 仅仅能复制字符串。而memcpy复制随意内容。
*2 复制的方法不同,strcpy不须要指定长度,它遇到字符串结尾符结束。
* 所以easy溢出。
memcpy则是依据第三參数聚丁复制的长度
*3 用途不同。通常在复制字符串时用strcpy,而须要复制其它数据类型数据时则一般用memcpy
*4 内存重叠时,strcpy会出现段错误。而memcpy不会
*/
int main()
{
char buf[16] = "abcdefghijk";
char buf1[16] = "abcdefghijk";
char buf2[16] = "abcdefghijk";
char buf3[16] = "abcdefghijk";
//strcpy(buf+2,buf); ###程序崩溃,段错误
memcpy(buf + 2,buf,5);
memmove(buf1+2,buf1,5);
mymemcpy1(buf2 + 2,buf2,5);
mymemcpy2(buf3 + 2,buf3,5);
printf(" end of memcpy(buf +2,buf,5), buf is :%s\n",buf);
printf(" end of memmove(buf1 +2,buf1,5), buf1 is :%s\n",buf1);
printf(" end of mymemcpy1(buf2 +2,buf2,5), buf2 is :%s\n",buf2);
printf(" end of mymemcpy2(buf3 +2,buf3,5), buf3 is :%s\n",buf3);
return 0;
}
遇到结束符则停止复制 *函数原型char *strcpy(char *dest,const char *src) */ char * mystrcpy(char *dest,const char *src) { if((dest == NULL) || (src == NULL)){ return NULL; } char *strdest = dest; //保存目标字符串的首地址 while((*strdest ++ = *src++) != '\0'); return dest; } /*memcpy函数提供了一般内存的复制,即对须要复制的内容没有限制 * 函数原型 void *memcpy(void * dest,const void * src,size_t count); * * 对于地址重叠情况 。该函数的是没有定义的 */ void * mymemcpy1(void *dest,const void *src,size_t count) { /*未考虑内存重叠情况*/ if((dest == NULL) || (src == NULL)){ return NULL; } char *strdest = (char *)dest; char *strsrc = (char *)src; while(count-- > 0){ *strdest++ = *strsrc++; } return dest; } void * mymemcpy2(void *dest,const void *src,size_t count) { char *strdest = NULL; char *strsrc = NULL; /*内存重叠情况*/ if((dest == NULL) || (src == NULL)){ return NULL; } if((src < dest) && (((char *)src + count) > (char *)dest)){ strdest = (char *)dest + count - 1; strsrc = (char *)src + count - 1; while(count--){ *strdest-- = *strsrc--; } }else{ strdest = (char *)dest; strsrc = (char *)src; while(count-- > 0){ *strdest++ = *strsrc++; } } return dest; } /*strcpy 与memcpy差别: *1 复制的内容不同。strcpy 仅仅能复制字符串。而memcpy复制随意内容。
*2 复制的方法不同,strcpy不须要指定长度,它遇到字符串结尾符结束。 * 所以easy溢出。
memcpy则是依据第三參数聚丁复制的长度 *3 用途不同。通常在复制字符串时用strcpy,而须要复制其它数据类型数据时则一般用memcpy *4 内存重叠时,strcpy会出现段错误。而memcpy不会 */ int main() { char buf[16] = "abcdefghijk"; char buf1[16] = "abcdefghijk"; char buf2[16] = "abcdefghijk"; char buf3[16] = "abcdefghijk"; //strcpy(buf+2,buf); ###程序崩溃,段错误 memcpy(buf + 2,buf,5); memmove(buf1+2,buf1,5); mymemcpy1(buf2 + 2,buf2,5); mymemcpy2(buf3 + 2,buf3,5); printf(" end of memcpy(buf +2,buf,5), buf is :%s\n",buf); printf(" end of memmove(buf1 +2,buf1,5), buf1 is :%s\n",buf1); printf(" end of mymemcpy1(buf2 +2,buf2,5), buf2 is :%s\n",buf2); printf(" end of mymemcpy2(buf3 +2,buf3,5), buf3 is :%s\n",buf3); return 0; }
输出结果:
end of memcpy(buf +2,buf,5), buf is :ababcdehijk
end of memmove(buf1 +2,buf1,5), buf1 is :ababcdehijk
end of mymemcpy1(buf2 +2,buf2,5), buf2 is :abababahijk
end of mymemcpy2(buf3 +2,buf3,5), buf3 is :ababcdehijk
关于内存重叠,对我来说是个新名词。所谓的内存重叠个人理解就是拷贝的目的地址在源地址的范围内。
通过上面的打印结果我们能够看到。
memcpy函数,本身是支持内存重叠的(有些资料或博客中说不支持的,有待考证)。mymemcpy1函数是不支持的,而mymemcpy2函数是支持的,所以说。mymemcpy2才是memcpy的详细实现。
关于strcpy并不支持内存重叠,编译过程中会出现段错误的。因此我总结了strcpy和memcpy函数的差别,有以下4条:
1 复制的内容不同,strcpy 仅仅能复制字符串,而memcpy复制随意内容。
2 复制的方法不同,strcpy不须要指定长度,它遇到字符串结尾符结束。
所以easy溢出。
memcpy则是依据第三參数聚丁复制的长度。
3 用途不同。
通常在复制字符串时用strcpy。而须要复制其它数据类型数据时则一般用memcpy。
4 内存重叠时。strcpy会出现段错误,而memcpy不会。
关于strcpy函数,再看以下的代码
/*File : strcpy.c *Auth : sjin *Date : 20141019 *Mail : 413977143@qq.com */ /*关于strcpy的一些重要的面试题 * */ #include <stdio.h> #include <string.h> int main() { char str[10] = {'\0'}; char str1[10] = {'\0'}; int i = 0; //for(i = 0; i < 10; i++){ //上面的将会出现段错误。未考虑字符串的结束符 for(i = 0; i < 9; i++){ str1[i] = 'a'; } strcpy(str,str1); printf("str is :%s\n",str); return 0; }