走进C的世界-那些年我们常犯的错---strcpy及memcpy函数

时间:2020-11-27 07:14:16

   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不需要指定长度,它遇到字符串结尾符结束。
 *  所以容易溢出。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不需要指定长度,它遇到字符串结尾符结束。
所以容易溢出。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;
}

为了能使用编译后的程序可以正常运行,所以修改了代码,看注释。这里考察了字符串的基本常识,关于更多的介绍,可以看我以前的另外一篇博客LinuxC面试题(内存管理) 。

   有更深入的理解,或者我这里有错误的地方,请指正 ,共同进步。 

注:转载请注明出处http://blog.csdn.net/sjin_1314 

   GITHUB地址:https://github.com/jinshaohui/C_Error_problem ,欢迎各位查看,有问题及时说明。有其他好的建议欢迎给我留言或私信。