void指针是一个特殊指针,表示为“无类型指针”,在ANSI C中用作通用指针的类型,而不是“char*”。 void指针没有特定类型,因此可以指向任何类型的数据。 也就是说,任何类型的指针都可以直接分配给void指针,而无需执行其他相关的强制类型转换,如下面的代码示例所示。
void *p1;
int *p2;
.
p1=p2;
然而,这并不意味着可以将void指针直接指定给其他类型的指针,而无需强制类型转换。 因为“空类型”包含“有类型”,“有类型”不包含“空类型”。 我们可以说“男人女人都是人”,但不要说“人是男人”或“人是女人”。 因此,以下示例代码编译失败,VC 2010显示错误消息“avalueoftype ' void * ' cannotbeassignedtoanentityoftype ' int *”
void *p1;
int *p2;
.
p2=p1;
由此可见,要将void指针指定给其他类型的指针,必须强制执行类型转换。 如下所示:
void *p1;
int *p2;
.
P2=(int* ) p1;
避免计算void指针
ANSI C标准规定,执行算法操作的指针必须知道数据类型的大小,也就是说,必须知道内存目标地址的准确值。 如下所示:
char a[20]='qwertyuiopasdfghjkl ';
int*p=(int* ) a;
p;
printf('%s ',p );
在上面的示例代码中,指针变量p的类型初始化为" int* ",指向的类型初始化为int,指向整数变量a。
执行语句“p”时,编译器将指针p的值加上“sizeof(int )() 32位系统中int占4字节,这里是加上4 () ),并将p 但是,由于char型的长度为1字节,所以语句“printf(%s ),p”输出“tyuiopasdfghjkl”。
对于void指针,编译器不知道指向的对象的大小,因此不能对void指针进行算术运算,如以下代码示例所示。
void * p;
p; //ANSI :错误
p=1; //ANSI :错误
上面的代码在VC 2010中显示错误消息“expressionmustbeapointertoacompleteobjecttype”。
但值得注意的是,GNU并不这么认为。 指定" void* "的算法操作与" char* "匹配。 因此,以下语句在GNU编译器中也是正确的。
void * p;
p; //GUN :正确
p=1; //GUN :正确
下面的代码示例演示如何在GCC上执行void指针自增长操作。
#包含
输入主(语音) )。
{
void * p='ILoveC ';
p;
printf(%s(n ),p );
}
执行结果如下。
洛韦克
由此可见,GNU和ANSI还有一些区别,而GNU比ANSI更“开放”,支持更多的语法。 但在实际设计环境中,应尽量遵循ANSI标准,避免void指针的算术操作。
如果函数的参数是任意类型的指针,请将该参数转换为void*
如上所述,void指针可以指向任何类型的数据。 此外,任何类型的指针都可以直接分配给void指针,而无需执行其他相关的强制类型转换。 因此,在编程中,如果函数的参数是任意类型的指针,则必须使用void指针作为函数的参数。 这样,函数就可以接受任何数据类型的指针作为参数。
典型的函数包括内存操作函数memcpy和memset,如以下代码所示。
void*memset(void*buffer,int b,size_t size ) )。
{
资产(缓冲区!=NULL;
char*retaddr=(char* ) buffer;
wile (大小----0 ) ) ) )。
{
*(retaddr )=)=(char ) b;
}
返回重做;
}
(void*memcpy(void*dst,const void *src,size_t size ) ) ) ) ) ) ) )
{
宏碁
t((dst!=NULL) && (src!=NULL));char *temp_dest = (char *)dst;
char *temp_src = (char *)src;
char* retAddr = temp_dest;
size_t i = 0;
/* 解决数据区重叠问题*/
if ((retAddr>temp_src) && (retAddr
{
for (i=size-1; i>=0; i--)
{
*(temp_dest++) = *(temp_src++);
}
}
else
{
for (i=0; i
{
*(temp_dest++) = *(temp_src++);
}
}
*(retAddr+size)=' ';
return retAddr;
}
这样,任何类型的指针都可以传入 memcpy 函数和 memset 函数中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。memcpy 函数的调用示例如下面的代码所示:
char buf[]="abcdefg";
// buf+2(从c开始,长度3个,即cde)
memcpy(buf, buf+2 ,3);
printf("%sn", buf);
或者进行如下形式的调用:
int dst[100];
int src[100];
memcpy(dst, src, 100*sizeof(int));
因为参数类型是 void*,所以上面的调用都是正确的。现在假设 memcpy 函数的参数类型不是 void*,而是 char*,如下面的代码所示:
char *memcpy(char* dst, const char* src, size_t size)
{
assert((dst !=NULL) && (src != NULL));
char *retAddr = dst;
size_t i = 0;
if ((retAddr>src) && (retAddr
{
for (i=size-1; i>=0; i--)
{
*(dst++)= *(src++);
}
}
else
{
for (i=0; i
{
*(dst++) = *(src++);
}
}
*(retAddr+size)=' ';
return retAddr;
}
现在继续执行如下形式的调用:
int dst[100];
int src[100];
memcpy(dst, src, 100*sizeof(int));
由于类型不匹配,编译器就会报错,如图 1 所示。
图 1
由此可见,这样的函数同时也失去了通用性。