无类型指针的缺陷是无法对类型进行解释。(不能计算无类型的大小)
如,定义一个无类型指针:void *p
不能进行++p或者*p的操作,因为系统没有此地址指向对象大小的信息。
无内存重叠(从低地址开始复制)
头文件string.h中定义的memcpy()函数可以实现泛型的拷贝。
函数memcpy()
原型:void* memcpy(void *dest,void *src,unsigned int n)
作用:由src所指内存区域复制n个字节到dest所指内存区域。
说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
现在我们编写自己的函数来实现泛型的拷贝。
void* my_memcpy(void *dst,const void *src,int size)
{
if(di==NULL||si==NULL)
return;
void *ret=dst;
char *d=(char *)dst;
const char *s=(const char *)src;
for(int i=0;i<size;i++)
{
d[i]=s[i];
}
return ret;
}
参数size表示字节个数。
之所以将无类型指针强转为char型指针,只是因为char类型的大小是1字节,这样声明可以实现一个字节一个字节地复制。
主函数中调用这个函数:
int ar[10]={12,23,34,45,56,67,78,89,90,100};
int br[10];
float dr[10]={1.2,2.3,3.4,4.5,6.7,8.9,9.0,10.0};
float xr[10];
my_memcpy(br,ar,sizeof(ar)); //40
my_memcpy(xr,dr,sizeof(dr)); //80
进一步,还可以通过泛型拷贝来实现泛型的交换函数。
#include<stdio.h>
struct Student
{
int id;
char name[20];
int age;
};
void Swap(void *a,void *b,int const n)
{
if(a==NULL||b==NULL)
{
return;
}
char temp;
for(int i=0;i<n;++i)
{
temp=((char *)a)[i];
((char *)a)[i]=((char *)b)[i];
((char *)b)[i]=temp;
}
}
void main()
{
char ca='a',cb='x';
int ia=12,ib=23;
double da=12.23,db=34.34;
Student sa={2017,"yhping",18};
Student sb={2018,"xcving",15};
Swap(&ca,&cb,sizeof(ca));
Swap(&da,&db,sizeof(da));
Swap(&sa,&sb,sizeof(sa));
}
有内存重叠(从高地址开始复制)
当有内存重叠时,源地址src会被覆盖,因此有了memcpy()函数
的改进版memmove()函数。
可以根据判断源地址和目的地址的大小,决定是从低地址开始拷贝还是从高地址开始拷贝。
void* my_memmove(void *dst,const void *src,int size)
{
if(dst==NULL||src==NULL)
return;
void * ret = dst;
if(dst <= src || (char *)dst >= ((char *)src + size)) //没有内存重叠,从低地址开始复制
{
while(size--)
{
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
else //有内存重叠,从高地址开始复制
{
src = (char *)src + len - 1;
dst = (char *)dst + len - 1;
while(size--)
{
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return ret;
}
相对全面的测试用例如下:
void Test()
{
char p1[256] = "hello,world!";
char p2[256] = {0};
my_memmove(p2,p1,strlen(p1)+1);
printf("%s\n",p2);
my_memmove(NULL,p1,strlen(p1)+1);
my_memmove(p2,NULL,strlen(p1)+1);
my_memmove(p1+1,p1,strlen(p1)+1);
printf("%s\n",p1);
my_memmove(p1,p1+1,strlen(p1)+1);
printf("%s\n",p1);
}