指针的本质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
《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源碼中的應用及原因
總結
以上是生活随笔為你收集整理的指针的本质2-void和void*及其应用在nginx中的应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: strcpy,memcpy和memmov
- 下一篇: nginx源码分析--内存对齐处理