• memcpy、memmove、memset及strcpy函数实现和理解


    memcpy、memmove、memset及strcpy函数实现和理解

    关于memcpy

    memcpy是C和C++ 中的内存拷贝函数,在C中所需的头文件是#include<string.h>, 在C++中需要包含的头文件是#include其函数原型如下:
    void *memcpy(void *dest, const void *src, size_t n);
    其功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
    库函数中的memcpy不能处理src和dest有重叠的情况。

    实现思路

    如果是自己实现memcpy的话,最好还是把地址有重叠的情况考虑进去,这样才能更好的体现编码的严谨。
    为了更好的理解源地址和目的地址有重叠的时候,正向复制会出现的问题,下面以图形方式进行解释:
    enter description here
    再给一个更加直接的例子,如下图:
    enter description here

    memcpy实现代码

    void *memcpy(void *dest,const void *src,size_t count)
    {
        char *pDest=static_cast<char *>(dest);
        const char *pSrc=static_cast<const char *>(src);
        //参数检查
        if ( NULL == dest || NULL == src || count<=0 )
        {
            return NULL;
        }
        if( pDest>pSrc && pDest<pSrc+count )
        {
            for(size_t i=count-1; i>=0; i--)
            {
                pDest[i] = pSrc[i];
            }
        }
        else
        {
            for(size_t i=0; i<count; i++)
            {
                pDest[i] = pSrc[i];
            }
        }
        return pDest;
    }
    
    

    关于memmove

    memmove和memcpy函数一样,使用时需要包含C++的#include头文件。它与memcpy的功能相似,都是将src所指的n个字节复制到dest所指的内存地址起始位置中,但该函数可以处理src和dest有重叠的情况。实际上,memcpy可以看作是memmove的子集。其代码如下(由于对memcpy做了优化,让memmove的实现和memcpy一样了。。。)

    void * memmove( void * const dest, const char * const src, size_t count )
    {
        // 参数检查
        if( NULL == dest || NULL == src || count<=0 )
        {
            return NULL;
        }
    
        char * pSrc = (char *)src;
        char * pDest = (char *)dest;
        if(pDest > Psrc && pDest < Psrc + count )
        {
           for( size_t i = count-1; i>=0; i--)
           {
              pDest[i] = pSrc[i];
           }
        }
        
        else
        {
            for(size_t i=0; i<count; i++)
            {
                pDest[i]=pSrc[i];
            }
        }
        return pDest;
    }
    
    

    关于memset

    函数原型如下:
    void *memset(void *s, int ch, size_t n);
    该函数的功能是将s中的前n个字节用ch替换,并返回s
    memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。
    在程序中给数组初始化时,常常会用到memset函数,一般的写法如下:

        int a[2];
        memset(a, 0, sizeof(a));
        memset(a, 0, 2*sizeof(int));
    

    需要注意的是,赋值是逐字节进行的:
    (1)若s指向char型地址,ch可为任意字符值;

    (2)若s指向非char型,如int型地址,要想赋值正确,ch的值只能是-1或0,因为-1和0转化成二进制后每一位都是一样的
    错误的赋值例子如下:

        int a[2];
        memset(a, 1, 2*sizeof(int));
    

    输出数组a的结果如下图:
    enter description here
    很明显,这不是我们希望看到的,因此在用memset给数组幅初始值的时候,一般都是幅为0.
    实现代码如下:

    void *my_memset(void* s, int ch, size_t n)
    {
        //参数检查
        if ( NULL == s || n<= 0)
        {
            return NULL;
        }
    
        char *pS = (char *)s;
        for ( size_t i = 0; i<n; i++)
        {
            pS[i] = ch;
        }
    
        return pS;
    }
    

    关于strcpy

    该函数在C的标准库函数,包含在头文件#include<string.h>和#include<stdio.h>中
    函数原型如下:
    char strcpy(char *dest, const char *src);
    函数功能是把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间,返回指向dest的指针
    说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
    代码实现如下:

    char *strcpy( char *dest, const char* src)
    {
        //参数检查
        if ( NULL == dest || NULL == src )
        {
            return NULL;
        }
    
        char *pDest = dest;
        while ( *src != '')
        {
            *pDest++ = *src++;
        }
        *pDest = '';
    
        return pDest;
    }
    

    strcpy和memcpy主要有以下3方面的区别。

    1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
    2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符""才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
    3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

    总结

    这一系列的函数都和指针的操作密切相关,如果要更好的理解C++,必须要正式指针,了解指针的特性,学会灵活的应用指针,让指针为自己的编码服务!

    对于内存操作的一些列函数来说,其参数涵返回类型都是void*,而不是某种具体的类型,这样是为了任何类型的指针都可以传入memcpy和memset中,都能正确赋值。这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。

    最后,很重要的一点是,如果对指针的位置不清楚的时候,可以借助画图来帮助理解,图形化的解释能够更好的帮助理解。

  • 相关阅读:
    2,SFDC 管理员篇
    1,SFDC 管理员篇
    0,SFDC 管理员篇
    Java控制台中输入中文输出乱码的解决办法
    struts1和struts2线程安全问题
    PL/SQL Developer使用技巧、快捷键
    SpringMVC前传--从Struts 1.x-2.x MVC-Spring 3.0 MVC
    H5元素拖拽使用事件数据传输
    js实现拼图小游戏
    js实现简单轮播图效果
  • 原文地址:https://www.cnblogs.com/scut-linmaojiang/p/5283838.html
Copyright © 2020-2023  润新知