在我们看程序的时候,通常会遇到void *的问题。尤其是void *后面跟着一个常数的时候,就更不明白了。以下是我在百度里搜出来的解释: (void*)0是把void指针指向的空间地址改为全为0,而(void*)-1就是把void指针指向的空间地址改为全为1,也就是FFFFFFFF(占4字节即32位)。其实,void指针的本身空间地址并没有改变,改变的只是void本身空间存储的一个地址,也就是改为00000000或FFFFFFFF。 如下例子: void *a,*b; a=(void*)0; b=(void*)-1; printf("%X %X ",&a,&b); printf("%X %X ",a,b); PS:http://zhidao.baidu.com/question/418197547.html 以下是我根据上面的解释,写的c语言程序: #include<stdio.h> void *TestFunction(void *arg) { printf("This is the TestFunction! "); return (void *)1; } main() { void *p; p=TestFunction(NULL); printf("This is the main Function! "); printf("the return data is:%d ",(int *)p); return 0; } 运行结果: This is the TestFunction! This is the main Function! the return data is:1 请按任意键继续. . . 从这个程序中可以看出,上面的解释是正确的。但是,注意一下,对于一个void *的指针,系统分配的是4个内存单元,对于int *的指针,系统分配的也是4个内存单元。因此,在程序中可以直接(int *)p,但是,当指针分配的内存单元不同的时候,强制转换可能会造成错误的结果。 (转载)(void*)0 的理解 一般把(void*)0定义为NULL 表示这是个空指针 void的含义 void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。 众所周知,如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋值;如果p1和p2指向不同的数据类型,则必须使用强制类型 转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。 例如: float *p1; int *p2; p1 = p2; 其中p1 = p2语句会编译出错,提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为: p1 = (float *)p2; 而void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换: void *p1; int *p2; p1 = p2; 但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包 容“无类型”。道理很简单,我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”。下面的语句编译出错: void *p1; int *p2; p2 = p1; 提示“'=' : cannot convert from 'void *' to 'int *'”。 在C语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。但是许多程序员却误以为其为void类型。例如: add ( int a, int b ) { return a + b; } int main(int argc, char* argv[]) { printf ( "2 + 3 = %d", add ( 2, 3) ); } 程序运行的结果为输出: 2 + 3 = 5 这说明不加返回值说明的函数的确为int函数。 理解*(void**)b #include <stdio.h> void main() { int *p; int a=2; unsigned long b=1245048; p=&a; printf("%d/n",*p); printf("%p/n",&a); printf("%d/n",&a); printf("%d/n",(void*)b); //printf("%d/n",*(void*)b); // 被注释的一行,运行此行会提示错误。 printf("%d/n",*(int*)b); printf("%d/n",*(void**)b); } P 为指向整型变量的指针。将 a 的地址赋予 p ,打印出 *p (解引用,即取该地址的值)为 2 。 再打印出 &a ,即 a 地址,为 0012FF78 (参数 p 表示用指针的格式,即内存地址,打印出来)。 再打印出 &a 的地址的十进制数字为 1245048( 参数 d 表示为十进制 ) 。 定义一个整型变量 b ,赋值为 1245048 ( 即 unsigned long b=1245048;) 。 如此, printf("%d/n",(void*)b); 表示:将 b 强制转换为一个指针,并打印出来。结果是 1245048 。 为什么如此呢?因为此时 b 虽然转换为一个指针,但 printf 的时候却没加上 * 号,故此没进行解引用(也即是取该地址的值),因而打印出来的仍然是该指针的值。(注意区别指针的值,和该指针指向的地址的值) 那么,加上 * 号进行解引用再打印出来,可否?实验结果不行,出现语法错误。概因 b 只是强制转换为指针,并没有指明是什么样类型(其实是 void 型)的指针。 那么,强制转换的时候便声明该指针类型,可以吗?可以看看 printf("%d/n",*(int*)b); ,将 b 强制转换为 int* 类型的指针,解引用成功,输出 2 。 最后来到我们的问题, *(void**)b 究竟是什么?相信看到这里你大概知道了。 (void**) 代表的是指向指针的指针,如此,先 (void**)b ,即,将 b 强制转换为指向指针的指针,然后再给它加上一个 * 解引用,如此,便取得了该指针指向的地址的值, 2 。 你可能会说,这里不也是声明为 void 型的指针了吗?这里行为什么上边不行呢?不,这里其实是不一样的。上边是 void 型指针,本质即指针,而这里是 void 型的指针的指针,本质是指针的指针。 void 型指针,不知道指向的地址内容要怎么去引用它,而 void 型的指针的指针,却知道其指向的地址内容是一个地址。 当然,我们也可以 printf("%d/n",*(int**)b); 输出结果是一样的。因为指向指针的指针本来就是指针(恩,有点拗口),只要类型正确,决定该指针输出什么的是其指向哪里的值,而不是指针本身。 我们可以做个试验, printf("%d/n",*(int****)b); ,看起来很晕吧,将 b 转换为指向指针的指针的指针的指针,结果呢?一样都是 2 。 void pointer 2009-12-25 16:55:17| 分类: C |举报|字号 订阅 void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。 小心使用void指针类型 众所周知,如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋值;如果p1和p2指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。 例如: float *p1; int *p2; 1 = p2; 其中p1 = p2语句会编译出错,提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为: p1 = (float *)p2; 而void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换: void *p1; int *p2; p1 = p2; 但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”。 按照ANSI(American National Standards Institute)标准,不能对void指针进行算法操作,即下列操作都是不合法的: void * pvoid; pvoid++; //ANSI:错误 pvoid += 1; //ANSI:错误 //ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。 //例如: int *pint; pint++; //ANSI:正确 pint++的结果是使其增大sizeof(int)。 但是大名鼎鼎的GNU(GNU's Not Unix的缩写)则不这么认定,它指定void *的算法操作与char *一致。 因此下列语句在GNU编译器中皆正确: void++; //GNU:正确 void += 1; //GNU:正确 void++的执行结果是其增大了1。 在实际的程序设计中,为迎合ANSI标准,并提高程序的可移植性,我们可以这样编写实现同样功能的代码: void * pvoid; (char *)pvoid++; //ANSI:正确;GNU:正确 (char *)pvoid += 1; //ANSI:错误;GNU:正确 GNU和ANSI还有一些区别,总体而言,GNU较ANSI更“开放”,提供了对更多语法的支持。但是我们在真实设计时,还是应该尽可能地迎合ANSI标准。 如果函数的参数可以是任意类型指针,那么应声明其参数为void * 典型的如内存操作函数memcpy和memset的函数原型分别为: void * memcpy(void *dest, const void *src, size_t len); void * memset ( void * buffer, int c, size_t num ); 这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。如果memcpy和memset的参数类型不是void *,而是char *,那才叫真的奇怪了!memcpy和memset函数返回的也是void *类型. void的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中“抽象基类”的概念,也很容易理解void数据类型。正如不能给抽象基类定义一个实例,我们也不能定义一个void(让我们类比的称void为“抽象数据类型”)变量。 关于void 2008-12-28 11:18:30 分类: C/C++ void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。 void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变量,让我们试着来定义: void a; 这行语句编译时会出错,提示“illegal use of type 'void'”。不过,即使void a的编译不会出错,它也没有任何实际意义。 void真正发挥的作用在于: (1) 对函数返回的限定; (2) 对函数参数的限定。 众所周知,如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋值;如果p1和p2指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。 例如: float *p1; int *p2; p1 = p2; 其中p1 = p2语句会编译出错,提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为:p1 = (float *)p2; 而void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换: void *p1; int *p2; p1 = p2; 但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”。道理很简单,我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”。下面的语句编译出错: void *p1; int *p2; p2 = p1; 提示“'=' : cannot convert from 'void *' to 'int *'”。 此外对于void指针类型 ANSI C标准规定用动态存储分配函数时返回void指针,它可以用来指向一个抽象的类型的数据,再将它的值赋给另一指针变量时要进行强制类型转换使之适合于被赋值的变量的类型,如: char *p1; void *p2; ... p1=(char *)p2; 或:p2=(void *)p1; 也可以将一个函数定义为void *类型,如: void *fun(char ch1,char ch2) 表示函数fun返回的是一个地址,它指向“空类型”,如需要引用此地址,也需要根据情况对之进行类型转换,如: p1=(char *)fun(ch1,ch2); 关于delete void*可能出错的情况 代码如下: class deleteVoid { void* data; const int size; const char id; public: deleteVoid(int sz,char c): size(sz),id(c) { data = new char[size]; cout<<"Construct deleteVoid"<<id<<","<<"size="<<size<<endl; } ~deleteVoid() { cout<<"Destruct deleteVoid"<<id<<","<<"size="<<size<<endl; delete []data; } }; int main() { deleteVoid* a = new deleteVoid(40, 'a'); delete a; void* b = new deleteVoid(30, 'b'); delete b; return 0; } 输出如下: Construct deleteVoida,size=40 Destruct deleteVoida,size=40 Construct deleteVoidb,size=30 因为delete a知道a指向一个deleteVoid对象,所以析构函数会调用,从而释放了被分配的data内存,但是,正如在进行delete b的操作中,如果通过void* 类型的指针对一个对象进行操作,则只会释放deletVoid对象的内存,而不会调用析构函数,也就不会释放data所指向的内存,编译这个程序时,编译器会认为我们知道所做的一切,于是我们不会看到任何警告信息。但因此我们会丢失大量内存。 C/C++中的void和void* 一、void void关键字表示“空类型”的概念。但是,这里的“空类型”不表示“任意类型”,而是表示不存在的意思,也就是说C/C++不允许你写语句void a,不存在类型为void的东西. void表示“不存在”的意思,可以从void的两个应用中加以体现: 1、void作为函数的返回值类型时,表示函数返回值不存在,即函数没有返回值。 例如: void FunctionNoReturn_0(std::string strName) { std::cout << strName << std::endl; return; } void FunctionNoReturn_1(std::string strName) { std::cout << strName << std::endl; } FunctionNoReturn_1函数体中虽然没有显式的Return;语句。但是,有隐式的Return;表示函数不存在返回值。 FunctionNoReturnType(void) { return 10; } 在C语言中,凡是不加返回值类型限定的函数,就会被编译器作为返回整型值处理,而不是没有返回值。所以,FunctionNoReturnType函数返回10是正确的。 在C++中,每个函数必须限定返回值类型,不允许不加返回值限定。所以,C++编译器会对FunctionNoReturnType报错。 2、void作为函数的参数的限定时,表示函数形参不存在,即函数没有形参。 例如: void FunctionNoArgument_0(void) { return; } void FunctionNoArgument_1() { return; } 注意:void FunctionNoArgument_1();这也表示没有形参。 在C语言中,FunctionNoArgument_1(10);是合法的。编译器不会报错。 在C语言中,FunctionNoArgument_0(10);是不合法的。编译器会报错。 在C++语言中,FunctionNoArgument_1(10);和FunctionNoArgument_0(10); 都是不合法的。编译器会报错。 C语言中不报错,也没什么关系的。因为,参数10对于函数的执行的结果没有影响。但是,对于代码的维护可能会造成隐藏的危害,可能会给别人造成误解。 说明:既然提供了void的这两种用法,就去运用。即函数没返回值就将其返回值类型写为void,函数没有形参就将其形参写为void。不了解编译器默认操作时,不要依赖。即使了解其默认操作,也别依赖,因为肯定有人不了解的,这样别人就看不懂你的代码了。 二、void* void*表示“空类型指针”,与void不同,void*表示“任意类型的指针”或表示“该指针与一地址值相关,但是不清楚在此地址上的对象的类型”。(为什么不用void表示任意类型的数据呢?大家都知道,C/C++是静态类型的语言,定义变量就会分配内存,然而,不同类型的变量所占内存不同,如果定义一个任意类型的变量,如何为其分配内存呢?所以,C、C++中没有任意类型的变量。但是,所有指针类型的变量,无论是int*、char*、string*、Student*等等,他们的内存空间都是相同的,所以可以定义“任意类型的指针”)。 C++/ANSI C: void*指针只支持几种有限的操作:与另一个指针进行比较;向函数传递void指针或从函数返回void*指针;给另一个void*指针赋值。不允许使用void*指针操作它所指向的对象,例如,不允许对void*指针进行解引用。不允许对void*指针进行算术操作。 GNU C: GNU C指定void*指针的算术操作与char*一致。 void*表示“任意类型的指针”,主要运用于内存操作函数的形参类型和返回值类型(内存操作与内存中数据类型无关,即任意类型都可以)。 memcpy 原型:extern void *memcpy(void *dest, void *src, unsigned int count); 用法:#include 功能:由src所指内存区域复制count个字节到dest所指内存区域。 说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。 注意:与strcpy相比,memcpy并不是遇到'