指针有两个属性:指向变量/对象的地址和长度。
深度详解:
如果指针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 *必须强制类型转换才能赋给其他类型的指针。因为“无类型”能包容“有类型”,而“有类型”则不能包容“无类型”。
1.void
《C程序设计语言(第2版新版)》
2.void*
《C程序设计语言(第2版新版)》
《C语言入门经典(第四版)》
《21天学通C语言(第6版)》
如果函数的参数能是任意类型指针,那么应声明其参数为void *。
3.指针转换
《C程序设计语言(第2版新版)》
4.指针运算
《21天学通C语言(第6版)》
例子:直接给void*指针运算会报错--“void*:未知的大小”
可以将空指针强制转换即可: (unsigned char*)k=(unsigned char*)k+1;
按照ANSI(American National Standards Institute)标准,不能对void指针进行算法操作,即下列操作都是不合法的:
void * pvoid;
pvoid++; //ANSI:错误
pvoid += 1; //ANSI:错误
ANSI标准之所以这样认定,是因为他坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。例如:
int *pint;
pint++; //ANSI:正确
pint++的结果是使其增大sizeof(int)。
不过GNU则指定void *的算法操作和char *一致。GNU下的void *p++相当于char *p++ 也就是移动一个字节。
因此下列语句在GNU编译器中皆正确:
pvoid++; //GNU:正确
pvoid += 1; //GNU:正确
pvoid++的执行结果是其增大了1。
在实际的程式设计中,为迎合ANSI标准,并提高程式的可移植性,我们能这样编写实现同样功能的代码:
void * pvoid;
(char *)pvoid ++; //ANSI:正确;GNU:正确
(char *)pvoid += 1; //ANSI:错误;GNU:正确
[JOEY 评:此处尚有疑问,(char *) pvoid ++,VS2005编译无法通过,提示:“void *”:未知的大小。]
GNU和ANSI更有一些差别,总体而言,GNU较ANSI更“开放”,提供了对更多语法的支持。不过我们在真实设计时,还是应该尽可能地迎合ANSI标准。
5.Nginx源码中的用法
声明内存池的结构,是使用u_char *定义last和end指针
typedef struct { u_char *last; u_char *end; ngx_pool_t *next; }ngx_pool_data_t;
初始化
ngx_pool_t *ngx_create_pool(size_t size) { ngx_pool_t *p; p=(ngx_pool_t*)malloc(size); p->d.last=(u_char *)p+sizeof(ngx_pool_t); p->d.end=(u_char *)p+size; size = size - sizeof(ngx_pool_t); p->max =size; p->current = p; return p; }
void *palloc(ngx_pool_t *pool,size_t size) { u_char *m; ngx_pool_t *p; if (size <= pool->max){ p = pool->current; do{ m = p->d.last; if ((size_t) (p->d.end - m) >= size) { (u_char *)p->d.last+=size; return m; } p = p->d.next; }while(p);
以上的正常的使用没问题的。
其实可以将u_char *定义last指针改为void*指针,但是如果在指针运算使用的时候不进行强制转换会报错。这个和前面的void * k直接运算是一样的问题。
代码做下修改即可:
do{ m = p->d.last; if ((size_t) (p->d.end - m) >= size) { (u_char *)p->d.last+=size; return m; } p = p->d.next; }while(p);
注意:void*空指针运算这个问题在Linux下用gcc编译不报错
下图是Nginx内存池图:
参考:指针的本质1--u_char*指针在Nginx源码中的应用及原因