2020/11/13 memcpy_s与memcpy函数的深入理解
memcpy函数原型:extern void *memcpy(void *dest, const void *src,size_t n);Linux下memcpy源码实现void *memcpy(void *to, const void *from, size_t n){void *xto = to;size_t temp, temp1;if (!n)return xto;if ((long)to
memcpy函数
原型:extern void *memcpy(void *dest, const void *src,size_t n);
这是一个函数指针 接收的是一个地址 dest是接收地址的首地址,src是源首地址,count是接收目标的大小单位为字节 位于cstring或memcpy.h头文件中。
Linux下memcpy源码实现
void *memcpy(void *to, const void *from, size_t n)
{
void *xto = to;
size_t temp, temp1;
if (!n)
return xto;
if ((long)to & 1) {
char *cto = to;
const char *cfrom = from;
*cto++ = *cfrom++;
to = cto;
from = cfrom;
n--;
}
if (n > 2 && (long)to & 2) {
short *sto = to;
const short *sfrom = from;
*sto++ = *sfrom++;
to = sto;
from = sfrom;
n -= 2;
}
temp = n >> 2;
if (temp) {
long *lto = to;
const long *lfrom = from;
#if defined(CONFIG_M68000) || defined(CONFIG_COLDFIRE)
for (; temp; temp--)
*lto++ = *lfrom++;
#else
asm volatile (
" movel %2,%3\n"
" andw #7,%3\n"
" lsrl #3,%2\n"
" negw %3\n"
" jmp %%pc@(1f,%3:w:2)\n"
"4: movel %0@+,%1@+\n"
" movel %0@+,%1@+\n"
" movel %0@+,%1@+\n"
" movel %0@+,%1@+\n"
" movel %0@+,%1@+\n"
" movel %0@+,%1@+\n"
" movel %0@+,%1@+\n"
" movel %0@+,%1@+\n"
"1: dbra %2,4b\n"
" clrw %2\n"
" subql #1,%2\n"
" jpl 4b"
: "=a" (lfrom), "=a" (lto), "=d" (temp), "=&d" (temp1)
: "0" (lfrom), "1" (lto), "2" (temp));
#endif
to = lto;
from = lfrom;
}
if (n & 2) {
short *sto = to;
const short *sfrom = from;
*sto++ = *sfrom++;
to = sto;
from = sfrom;
}
if (n & 1) {
char *cto = to;
const char *cfrom = from;
*cto = *cfrom;
}
return xto;
}
windows下的实现
void* __cdecl memcpy(void* dst,const void* src,size_t count)
{
void*ret=dst;
{
extern void RtlMoveMemory(void *,const void *,size_t count);
RtlMoveMemory(dst,src,count);
}
while(count--){
*(char *)dst = *(char *)src;
dst = (char *)dst+1;
src = (char *)src+1;
}
return (ret);
}
//改进之后的memcpy
void *my_memcpy_byte(void *dst, const void *src, int n)
{
if (dst == NULL || src == NULL || n <= 0)
return NULL;
char * pdst = (char *)dst;
char * psrc = (char *)src;
if (pdst > psrc && pdst < psrc + n)
{
pdst = pdst + n - 1;
psrc = psrc + n - 1;
while (n--)
*pdst-- = *psrc--;
}
else
{
while (n--)
*pdst++ = *psrc++;
}
return dst;
}
使用注意事项
- src地址是否为空
- src和dest地址是否重合,是安全重合还是部分重合
- 当处理大块数据的时候可能存在效率低下的问题。
memcpy_s函数
函数 memcpy_s( void *dest, size_t numberOfElements, const void *src, size_t count )
函数原型
errno_t memcpy_s(
void *dest,
size_t numberOfElements,
const void *src,
size_t count
);
dest:目标的地址
numberOfElements:目标的size
src:源地址
count:要拷贝的字节数
先申请了一个缓冲区大小,为numberofelements,当count超过这个缓冲区的时候就开始溢出
memcpy_s详解
第一个参数为目标内存地址,第二个参数为目标内存缓冲大小,第三个参数为源内存地址,第四个为源内存缓冲的大小。返回值是一个错误码。
为什么这个返回值是错误码呢?因为这个版本中加入了基本的错误检测。如果源缓冲大小为0,即count为0,函数返回0,什么也不做。此函数没有对目标指针为NULL的情况,不做检查,所以你自己要注意检查。如果指针有值,但是是无效值,函数也没办法检查是否是有效内存,只是会搜集这些信息,在程序崩溃时提供调试需要的信息。
然后检查源地址是否为空或count是否大于sizeInBytes,两个条件有一个满足,函数中先将目标内存以sizeInBytes指定的大小调用memset函数清0.这里可以看出,如果sizeInBytes传入的大小超出目标缓冲区的大小,也是会带来隐患的,一旦清除了其他进程的或者其他线程的内存,都是带来问题,也很可能导致内存操作违规。所以,第二个参数的大小不能超过目标缓冲的大小,以字节为单位。然后程序搜集错误信息,最后返回错误码,并不会执行内存复制的过程。前面说的这是两个条件任意一个进入都会导致失败,所以在搜集信息时,程序会对进入的条件进行判断,然后进行搜集。
只有这些检查通过,才会调用memcpy函数执行内存复制过程。而第二个参数只是内部用来检测和清除目标内存而使用的。
函数memcpy_s执行完毕后返回0,所以检查返回值是否为0不能判断是否成功。但是返回值为非零那就是失败了。
虽然说加上_s版本的函数是安全版本,但是也是会出现问题的,使用时也要注意,即使有时候不崩溃,但是一旦改写了其他的线程的内存,必然导致程序运行不正常,因为线程的数据被破坏,至少逻辑会出错。
不过,_s版本函数提供了一定的检测,并且在release版本也可以报错,可以进一步调试问题,而memcpy则在release版本中不会报错
更多推荐
所有评论(0)