mem系列函数是面试的时候常考的知识点,我们需要熟练掌握这三个函数的原理和代码实现,要能准确无误的写出代码。

memcpy、memset和memset三个函数在使用过程中,均需包含以下头文件:

//在C中
#include <string.h>
//在C++中
#include <cstring>

memcpy

memcpy函数是C/C++中的内存拷贝函数,它的功能是从源src所指的内存地址的起始位置开始,拷贝n个字节到目标dst所指的内存地址的起始位置中。

研究函数功能最好的办法就是研究其源代码,这里在网上找了一份,如下:

void * __cdecl memcpy ( void * dst,const void * src,size_t count)
{
    void * ret = dst;
    while (count--)
    {
        // 注意, memcpy函数没有处理dst和src区域是否重叠的问题
        *(char *)dst = *(char *)src;
        dst = (char *)dst + 1;
        src = (char *)src + 1;
    }
    return(ret);
}

源代码比较简单,定义一个计数,然后从头到尾一次将src指向的值拷贝给dst,库函数中的memcpy不能处理dst和src中存在重叠部分这种情况。

那么处理重叠部分的话,我们可以采用从后往前依次拷贝的方法,下面给出我修改过的函数代码:

void * __cdecl memcpy ( void * dst,const void * src,size_t count)
{
    char *pDst = static_cast<char *> dst;
    const char *pSrc = static_cast<const char *> src;
    //检查参数
    if(pDst==NULL || pSrc== NULL || count <=0){
        return NULL;
    }
    //判断有是否存在重叠部分
    if(pDst > pSrc && pDst < 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 pDst;
}

memset

memset一般用于对内存初始化,在这里需要注意的是,memset函数是对内存的每个字节(按字节)设置成c的值。其函数原型如下:

void memset(void *s, int c, size_t n)
{
    const unsigned char uc = c;//将int转换成char,截去c的高24位,留下低8位
    unsigned char *su;
    for (su = s; 0 < n; ++su, --n)
        *su = uc;
    return s;
}

注意,这里有一个坑,memset一般用于将内存清零,你要是想将这段内存初始化为1而写下下面的代码:

int num[10];
memset(num,1,sizeof(int)*10);

这里并不会如你所愿,num的每一个数都被初始化为16843009,原因就是上述提到的会截去c的高24位。

使用memset初始化比用for循环初始化要快很多,所以在初始化基本类型数据,结构体等的时候尽量选择memset,memset可以方便的清空一个结构类型的变量或数组。

memmove

它与memcpy的功能相似,都是将src所指的n个字节复制到dst所指的内存地址的起始位置,不同的是它处理了src和dst有重叠的情况。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。(与上述修改过得memcpy基本一致)

所以基本原则就是,如果你能确保两段内存没有重叠的部分,就使用memcpy来进行拷贝;如果你不能确定,为了保证复制的正确性,必须用memmove。

其实现代码如下:

void* memmove(void* dest, void* src, size_t count)
{
    void* ret = dest;
    if (dest <= src || dest >= (src + count))
    {
        //Non-Overlapping Buffers
        //copy from lower addresses to higher addresses
        while (count --)
            *dest++ = *src++;
    }
    else
    {
        //Overlapping Buffers
        //copy from higher addresses to lower addresses
        dest += count - 1;
        src += count - 1;
        while (count--)
            *dest-- = *src--;
    }
    return ret;
}

strcpy

strcpy是C语言的标准库函数,使用strcpy需要包含以下头文件:

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

其函数功能是把从src地址开始且含有NULL结束符的字符串复制到dst开始的地址空间,返回指向dst的指针。其函数代码如下:

char* strcpy(char* dst , char* src){
    if(dst==NULL||src==NULL) return NULL;// --1

    if(dst==src) return dst;    //--2
    char* address = dst;    //--3
    while((*dst++ = *src++)!='\0') //--4
    return address;     //--5
}

图中标出来的都是考点,下面一一说明:

  • 1、需要判断参数的正确性,这里也可以抛出一个异常
  • 2、如果指向了同一块内存,不用复制直接返回即可
  • 3、这里需要保存原始的dst指针,用作返回值
  • 4、这里有一个技巧,如果写成以下两种,面试的时候会大大扣分!
//第一种
while(*dst++ = *src++)  //直接越界访问,没有检查指针的有效性
//第二种
while(*src!='\0'){*dst++ = *src++;}//考虑了src的边界问题,没有在dst的后面加'\0',会导致dst的长度未知引起错误
  • 5、函数返回dst的原始值是为了能够支持链式表达式,增加了函数的附加性。

上述第5点可以用如下测试代码来说明:

int length = strlen(strcpy(strA,strB));//如果不支持链式表达式,这里会报错。

那么有时候也会问为什么不返回src的原始值,错误原因有以下三点:

  • 源字符串本来就已知,返回没有什么意义
  • 不能支持形如char * strA = strcpy(new char[10],strB) 这样的表达式
  • 为了保护源字符串,使用const限定了src所指的内容,把const char作为char的返回值,类型不符,编译器会报错。

strcpy和memcpy的不同点

这个也是常见的考点,主要分为以下三点不同:

  • 复制内容不同:strcpy只能复制字符串,而memcpy可以复制任何内容,例如字符数组,整型,结构体等
  • 复制的方法不同:strcpy不需要指定长度,它遇到字符串结束符’\0’才结束,所以容易溢出。memcpy则需要传入第三个参数来指定长度
  • 用途不同:通常在复制字符串的时候用strcpy,而需要复制其他数据类型的时候则一般用memcpy。

C/C++的mem函数和strcpy函数的区别和应用的更多相关文章

  1. strcmp函数和strcpy函数

    (一)strcmp函数 strcmp函数是比較两个字符串的大小,返回比較的结果.一般形式是: i=strcmp(字符串,字符串); 当中,字符串1.字符串2均可为字符串常量或变量:i   是用于存放比 ...

  2. strlen函数,strcat函数,strcpy函数,strncpy函数,strcmp函数

    strcpy函数: char *strcpy(char *Dest , const char *Src) { assert((Dest != NULL) && (Src != NULL ...

  3. 编写函数模拟strcpy()函数功能

    strcpy(字符数组1,字符串2) strcpy( )用于将字符串2复制到字符数组1中 /* strcpy(字符数组1,字符串2) strcpy( )用于将字符串2复制到字符数组1中 模拟strcp ...

  4. strcpy函数的实现

    strcpy函数的实现 大家一般认为名不见经传strcpy函数实现不是很难,流行的strcpy函数写法是: char *my_strcpy(char *dst,const char *src) { a ...

  5. strcpy函数实现

    1,strcpy最简便实现 char * strcpy_to (char *dst, const char *src) { char *address = dst; assert((dst != NU ...

  6. strcpy函数和strncpy函数的区别

    strcpy函数和strncpy函数的原型介绍在我的另一篇文章中介绍了,见strcpy,strncpy,strlen等函数原型 strcpy:字串复制 原型:char *strcpy(char *de ...

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

    memcpy.memmove.memset及strcpy函数实现和理解 关于memcpy memcpy是C和C++ 中的内存拷贝函数,在C中所需的头文件是#include<string.h> ...

  8. strlen() 和 strcpy()函数

    strlen() 和 strcpy()函数的区别,这两个一个是返回一个C风格字符串的长度,一个是对一个C风格字符串的拷贝,两个本来功能上是不同的,此外,他们还有一些细小的区别:strlen(" ...

  9. strcpy函数的C/C++实现

    2013-07-05 14:07:49 本函数给出了几种strcpy与strncpy的实现,有ugly implementation,也有good implementation.并参考标准库中的imp ...

随机推荐

  1. [HNOI2014]画框

    题目描述 小T准备在家里摆放几幅画,为此他买来了N幅画和N个画框.为了体现他的品味,小T希望能合理地搭配画与画框,使得其显得既不过于平庸也不太违和. 对于第 幅画与第 个画框的配对,小T都给出了这个配 ...

  2. [SDOI2016]排列计数

    Description 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的.序列恰好有 m 个数是 ...

  3. ●Joyoi 绿豆蛙的归宿

    题链: http://www.joyoi.cn/problem/tyvj-1933题解: 期望dp,拓扑序 定义dp[i]表示从i点到N点的期望距离. 令cnt[u]表示u的出度. 显然$$dp[u] ...

  4. ●BZOJ 2618 [Cqoi2006]凸多边形

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2618 题解: 计算几何,半平面交. 给出一些凸包,求面积交. 把所有边都取出来,直接办平面交 ...

  5. 2015 多校联赛 ——HDU5350(huffman)

    Problem Description MZL is a mysterious mathematician, and he proposed a mysterious function at his ...

  6. HDU 4787 GRE Words Revenge

    Description Now Coach Pang is preparing for the Graduate Record Examinations as George did in 2011. ...

  7. bzoj 1875: [SDOI2009]HH去散步

    Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又 ...

  8. 移动端开发,文字增加,字体自动放大(font boosting)

    问题缘由:做文章详情页的时候,文字多了一点字体就放大了,真的是奇了怪了. 问题重现 一段文字的时候 两段文字的时候 很明显,字体放大了很多. 疑点 meta标签缩放的问题 最近正好遇到处理retain ...

  9. sqlserver 截取字符串(转)

    SQL Server 中截取字符串常用的函数: 1.LEFT ( character_expression , integer_expression ) 函数说明:LEFT ( '源字符串' , '要 ...

  10. easyui datagrid 得到冻结列跟非冻结列

    var head = $('#ClassManagement').datagrid("options").columns; var fhead=$('#ClassManagemen ...