指针的本质2-void和void*及其应用在nginx中的应用

指针本质论

指针有两个属性:指向变量/对象的地址长度

但是指针只存储地址,长度则取决于指针的类型,
编译器根据指针的类型从指针指向的地址向后寻址,
指针类型不同则寻址范围也不同,比如:
int*从指定地址向后寻找4字节作为变量的存储单元,
double*从指定地址向后寻找8字节作为变量的存储单元 ,

1.void指针是一种特别的指针
   void *vp
  //说它特别是因为它没有类型
  //或者说这个类型不能判断出指向对象的长度

2.任何指针都可以赋值给void指针
  type *p;
  vp=p;
  //不需转换
  //只获得变量/对象地址而不获得大小

3.void指针赋值给其他类型的指针时都要进行转换
   type *p=(type*)vp;
   //转换类型也就是获得指向变量/对象大小
转:http://icoding.spaces.live.com/blog/cns!209684E38D520BA6!130.entry

4.void指针不能复引用
  *vp//错误
  因为void指针只知道,指向变量/对象的起始地址
  而不知道指向变量/对象的大小(占几个字节)所以无法正确引用

5.ANSI下void指针不能参与指针运算,除非进行转换,GNU允许
   (type*)vp++;
  //vp==vp+sizeof(type)

深度详解

如果指针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

指针的本质2-void和void*及其应用在nginx中的应用_第1张图片

《C程序设计语言(第2版新版)》

2.void*

指针的本质2-void和void*及其应用在nginx中的应用_第2张图片

《C程序设计语言(第2版新版)》

指针的本质2-void和void*及其应用在nginx中的应用_第3张图片

《C语言入门经典(第四版)》

指针的本质2-void和void*及其应用在nginx中的应用_第4张图片

《21天学通C语言(第6版)》

如果函数的参数能是任意类型指针,那么应声明其参数为void *。


3.指针转换

指针的本质2-void和void*及其应用在nginx中的应用_第5张图片

指针的本质2-void和void*及其应用在nginx中的应用_第6张图片

《C程序设计语言(第2版新版)》

4.指针运算

指针的本质2-void和void*及其应用在nginx中的应用_第7张图片

指针的本质2-void和void*及其应用在nginx中的应用_第8张图片

指针的本质2-void和void*及其应用在nginx中的应用_第9张图片

《21天学通C语言(第6版)》


例子:直接给void*指针运算会报错--“void*:未知的大小

指针的本质2-void和void*及其应用在nginx中的应用_第10张图片


可以将空指针强制转换即可:       (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直接运算是一样的问题。

指针的本质2-void和void*及其应用在nginx中的应用_第11张图片

代码做下修改即可:

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内存池图:

指针的本质2-void和void*及其应用在nginx中的应用_第12张图片

参考:指针的本质1--u_char*指针在Nginx源码中的应用及原因

你可能感兴趣的:(指针的本质2-void和void*及其应用在nginx中的应用)