目录
一、问题由来
二、动手实现你的memcpy
1.单字节拷贝
2.四字节拷贝(提升效率)
三、动手实现你的strcpy
四、测试结果
一、问题由来
C库中已提供有原生的memcpy、memmove以及strcpy等函数,位于<string.h>头文件中。
void *memcpy(void *dst, const void *src, size_t n);
上面是C库中原生的memcpy声明,该memcpy在dst与src两者的内存地址不互相重叠时,可以得到正确的处理结果,而两者的内存地址有重叠时,该接口并不保证处理结果的正确性。
接着则是memmove在string.h中的声明,memmove比memcpy更先进的原因则在于:它解决了内存重叠时的拷贝情形。但memmove在不同编译器上的实现会有所不同,故memmove在内存不重叠时,不一定会比memcpy更快。
void *memmove(void *dst, const void *src, size_t count)
再观之strcpy的函数原型,发现其声明与memcpy有几分相似,从中也可以看出:memcpy和memmove比strcpy的适用范围更大,strcpy只是它们的特殊情形。
char *strcpy(char *dest, const char *src)
说到此,不论是探索的需求,还是面试的需求,想必读者已经迫不及待地想要手撕一遍memcpy和strcpy的实现了,here we go! (但先说好了,咱们只是原理上的实现,而不是探索极致的效率,真正的源码情况会考虑到更多的情况。)
二、动手实现你的memcpy
1.单字节拷贝
//-------------------------按单字节拷贝(基础实现)-------------------//
void* my_memcpy_byte(void* dest, const void* src, size_t n)
{
if(dest == NULL || src == NULL) return NULL;
char *pdst = (char*)dest;
const char *psrc = (char*)src;
if( pdst > (psrc + n) || pdst < psrc) { //不重叠 || 虽重叠但dst < src
printf("no-overlap: \n");
while(n--) *pdst++ = *psrc++; //顺序copy
}
else { //重叠, 但src < dest
printf("overlap: \n");
pdst = dest + n - 1; //定位到前n个字节的末尾
psrc = src + n - 1;
while(n--) *pdst-- = *psrc--; //逆序copy
}
return dest;
}
2.四字节拷贝(提升效率)
//----------------------按4字节拷贝(提升效率)-------------------//
void* my_memcpy(void* dest, const void* src, size_t n)
{
if(dest == NULL || src == NULL) return NULL;
int *pdst = (int*)dest;
int *psrc = (int*)src;
char *byte1, *byte2;
int cnt1 = n / 4; //多少个4字节
int cnt2 = n % 4; //余出多少个单字节
//pdst > psrc + n 这样判断会有问题,虽然按4字节进行拷贝,但内存重叠情况还是按单个字节来比较
if( (char*)pdst > ((char*)psrc + n) || pdst < psrc) {
printf("no-overlap: \n");
while(cnt1--) *pdst++ = *psrc++; //4字节拷贝
byte1 = (char*)pdst;
byte2 = (char*)psrc;
while(cnt2--) *byte1++ = *byte2++; //单字节拷贝
}
else {
printf("overlap: \n");
byte1 = (char*)pdst + n - 1;
byte2 = (char*)psrc + n - 1;
while(cnt2--) *byte1-- = *byte2--; //单字节拷贝
byte1++, byte2++;
pdst = (int*)byte1 - 1;
psrc = (int*)byte2 - 1;
while(cnt1--) *pdst-- = *psrc--; //4字节拷贝
}
return dest;
}
三、动手实现你的strcpy
char* my_strcpy(char* dest, const char* src)
{
if(dest == NULL || src == NULL) return NULL;
char *psrc = (char*)src;
int n = 0;
while(*psrc) { //得到长度
n++;
psrc++;
}
my_memcpy_byte(dest, src, n);
return dest;
}
四、测试结果
附上整体代码,以供读者参考。本例完全脱离于<string.h>而实现,可放心食用。
#include<stdio.h>
//-------------------------按单字节拷贝(基础实现)-------------------//
void* my_memcpy_byte(void* dest, const void* src, size_t n)
{
if(dest == NULL || src == NULL) return NULL;
char *pdst = dest;
const char *psrc = src;
if( pdst > (psrc + n) || pdst < psrc) { //不重叠 || 虽重叠但dst < src
printf("no-overlap: \n");
while(n--) *pdst++ = *psrc++; //顺序copy
}
else { //重叠且src < dest
printf("overlap: \n");
pdst = dest + n - 1;
psrc = src + n - 1;
while(n--) *pdst-- = *psrc--; //逆序copy
}
return dest;
}
//----------------------按4字节拷贝(提升效率)-------------------//
void* my_memcpy(void* dest, const void* src, size_t n)
{
if(dest == NULL || src == NULL) return NULL;
int *pdst = (int*)dest;
int *psrc = (int*)src;
char *byte1, *byte2;
int cnt1 = n / 4; //多少个4字节
int cnt2 = n % 4; //余出多少个单字节
//pdst > psrc + n 这样判断有问题,虽然按4字节进行拷贝,但内存重叠情况还是按单个字节来描述
if( (char*)pdst > ((char*)psrc + n) || pdst < psrc) {
printf("no-overlap: \n");
while(cnt1--) *pdst++ = *psrc++; //4字节拷贝
byte1 = (char*)pdst;
byte2 = (char*)psrc;
while(cnt2--) *byte1++ = *byte2++; //单字节拷贝
}
else {
printf("overlap: \n");
byte1 = (char*)pdst + n - 1;
byte2 = (char*)psrc + n - 1;
while(cnt2--) *byte1-- = *byte2--; //单字节拷贝
byte1++, byte2++;
pdst = (int*)byte1 - 1;
psrc = (int*)byte2 - 1;
while(cnt1--) *pdst-- = *psrc--; //4字节拷贝
}
return dest;
}
char* my_strcpy(char* dest, const char* src)
{
if(dest == NULL || src == NULL) return NULL;
char *psrc = (char*)src;
int n = 0;
while(*psrc) { //得到长度
n++;
psrc++;
}
my_memcpy_byte(dest, src, n);
return dest;
}
int main()
{
char a[] = "12345678900000000000000000";
char b[] = "12345678900000000000000000";
char buf[100] = {'0'};
char *p = a + 3;
char *p1 = b + 10;
printf("%s\n", my_memcpy(p, a, 7)); //overlap: p所指位于a[0]~a[6]的范围内
printf("%s\n", my_memcpy(p1, b, 7)); //p1所指不在b[0]~b[6]的范围内, 即无重叠
printf("%s\n", my_strcpy(buf, a)); //独立的空间, 无重叠
return 0;
}
[程序结果及分析]