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的话,最好还是把地址有重叠的情况考虑进去,这样才能更好的体现编码的严谨。
为了更好的理解源地址和目的地址有重叠的时候,正向复制会出现的问题,下面以图形方式进行解释:

再给一个更加直接的例子,如下图:

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的结果如下图:

很明显,这不是我们希望看到的,因此在用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 != '\0')
{
*pDest++ = *src++;
}
*pDest = '\0'; return pDest;
}

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

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

总结

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

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

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

memcpy、memmove、memset及strcpy函数实现和理解的更多相关文章

  1. 走进C标准库(7)——"string.h"中函数的实现memcmp,memcpy,memmove,memset

    我的memcmp: int memcmp(void *buf1, void *buf2, unsigned int count){ int reval; while(count && ...

  2. 逆向 string.h 函数库 memset、strcpy、strcmp 函数

    memset 函数 函数原型:void *memset(void *str, int c, size_t n) 主要功能:复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符 ...

  3. 自己实现内存操作函数memset(),memcmp(),memcpy(),memmove()

    1.memset()内存设置函数(初始化) void *my_memset(void* dest, int c, size_t count) { assert(dest != NULL); char  ...

  4. strcpy和memcpy,memmove函数的区别

    strcpy和memcpy的区别 strcpy和memcpy都是标准C库函数,它们有下面的特点. strcpy提供了字符串的复制.即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制 ...

  5. strcpy函数;memcpy函数;memmove函数

    strcpy函数实现: char* strcpy(char* des,const char* source) { char* r=des; assert((des != NULL) && ...

  6. 自己实现的库函数2(memset,memcmp,memcpy,memmove)

    memset,memcmp,memcpy,memmove是对内存进行管理的库函数,为了更好的理解和使用这几个函数,自己用C语言实现一下~ //内存设置函数void* my_memset(void* d ...

  7. strcpy、memcpy和memset的区别

    strcpy 原型:extern char *strcpy(char *dest,char *src); 用法:#include <string.h> 功能:把src所指由NULL结束的字 ...

  8. strcpy,memcpy,memmove和内存重叠分析

    一:strcpy函数用法和实现: /* GNU-C中的实现(节选): */ char* strcpy(char *d, const char *s) { char *r=d; while((*d++= ...

  9. memset memcmp memcpy memmove 自己实现

    memset memcmp memcpy memmove 自己实现 memset #include <stdio.h> #include <memory.h> #include ...

随机推荐

  1. lintcode : 空格替换

    题目: 空格替换 设计一种方法,将一个字符串中的所有空格替换成 %20 .你可以假设该字符串有足够的空间来加入新的字符,且你得到的是“真实的”字符长度. 样例 对于字符串"Mr John S ...

  2. 【Linux高频命令专题(17)】head

    概述 head 与 tail 就像它的名字一样的浅显易懂,它是用来显示开头或结尾某个数量的文字区块,head 用来显示档案的开头至标准输出中,而 tail 想当然尔就是看档案的结尾. 命令格式 hea ...

  3. 关于Tomcat 6的热部署和热加载

    转载:http://blog.csdn.net/tianlincao/article/details/7263840 之前每当修改了类文件,保存后tomcat 都需要自动重启,今天作了后面的设置后,可 ...

  4. nginx的健康检查功能将挂掉的Tomcat舍弃

    1.Ngninx自带健康检查功能,能将挂掉的服务器舍弃,不在访问路径里 2.Nginx还有URL重写功能,能将接收到的请求,进行改写,再将新的URL分发到后端服务器上

  5. C++:对象的赋值和复制

    3.6.1 对象赋值语句 如同基本类型赋值语句一样,同类型的对象之间也可以进行赋值,即一个对象的值可以赋给另一个对象.这里所指的对象的赋值是指对其中的数据成员赋值,而不对成员函数赋值.例如:A和B是同 ...

  6. [Linux 命令]df -h

    查看目前磁盘空间和使用情况 以更易读的方式显示

  7. Android 在Intent中传递接口

    总结:在Activity中不能用intent传递匿名接口,原因如下:Activity A中生成了匿名接口M, 这个接口的引用就在组Activity A中,Activity A会禁止接口M 序列化.因为 ...

  8. 一个小应用的dbcp和c3p0配置实例

    以下是一个小应用的数据库连接池配置,包括DBCP和C3P0的配制方法 因为是小应用,完全不涉及访问压力,所以配置上采取尽量节约数据库资源的方式 具体要求如下:初始化连接数为0连接不够,需要新创建时,每 ...

  9. 整理了一份招PHP高级工程师的面试题

    1. 基本知识点 HTTP协议中几个状态码的含义:1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码. 代码   说明 100   (继续) 请求者应当继续提出请求. 服务器返回此代码 ...

  10. Socket programming in C on Linux | tutorial

    TCP/IP socket programming This is a quick guide/tutorial to learning socket programming in C languag ...