[英]When to use a void pointer?
我了解 malloc 實現中使用 void 指針。
void* malloc ( size_t size );
任何人都可以提出其他原因或提供一些在實踐中有用的場景。
謝謝
一個好的場景void*
use是你想要實現任何通用的ADT,以防你不知道它要保留和處理的數據類型。 例如,鏈接列表如下所示:
typedef struct node_t node;
struct
{
void* data;
node* prev, next;
} node_t;
typedef struct list_t list;
typedef void* (func)(void*) cpy_func;
typedef void (func)(void*) del_func;
struct
{
node* head, tail, curr;
cpy_func copy;
del_func delete;
} list_t;
initializeLinkedList(cpy_func cpy, del_func del);
//here you keep going defining an API
例如,您將初始化函數指針傳遞給其他函數,這些函數將能夠將您的數據類型復制到列表中並在之后釋放它。 因此,通過使用void*
您可以使列表更通用。
我認為void*
僅僅因為向后兼容而保留在C ++中,因為在C ++中你有更安全和復雜的方法來實現相同的結果,比如模板,仿函數等,而你在編寫C ++時不需要使用malloc。
關於C ++,我沒有任何具體的有用示例。
如果您正在與C代碼接口並需要通過C ++對象,但C庫只接受通用指針,那么當您檢索指針時,需要將其重新轉換為正確的類型。
可能不應經常使用Void指針,但是當您嘗試使用適用於任意指針的庫函數時,它們可以提供幫助,而且並不真正關心該內存表示哪些數據。
只要數據塊的內容不重要,就應該使用void
指針。 例如,在復制數據時,復制存儲區的內容,但數據的格式並不重要。
對於在內存塊上運行而不需要使用void
指針理解內容的函數,可以向用戶說明設計,以便他們知道函數不關心任何數據格式。 當函數實際上是內容不可知時,通常編碼的函數采用char *
來處理內存塊。
在C ++中,我發現void *指針最引人注目的用例是為代碼提供在他們已經使用的對象上存儲任意“用戶數據”的選項。
假設您已經編寫了一個代表Car
的類,用於在Car
對象(交通模擬,租車清單等等)中做有用的軟件。 現在讓我們假設您發現自己的應用程序想要跟蹤Car
后備箱的任意內容。 存儲在主干中的內容的細節對於Car
類並不重要,可能是任何東西 - 它實際上取決於使用Car類的應用程序的目的。 輸入void *指針。
class Car
{
public:
// Existing methods of your Car class
void setContentsOfTrunk(void* contentsOfTrunk);
void* contentsOfTrunk() const;
private:
void* m_contentsOfTrunk;
}
現在,任何使用Car
類的應用程序都可以選擇將任意數據對象附加到現有的Car
對象,以便可以從具有Car
對象的任何代碼中獲取它。 無論在代碼中的哪個位置,主干的內容都“與Car
對象一起旅行”。
在這種情況下,使用void *有兩種選擇。
第一種是根據trunk內容對象的類型來模板化你的類:
template <class TrunkContentsType>
class Car
{
public:
// Existing methods of your Car class
void setContentsOfTrunk(TrunkContentsType contentsOfTrunk);
TrunkContentsType contentsOfTrunk() const;
private:
TrunkContentsType m_contentsOfTrunk;
}
這似乎是不必要的侵入性。 中繼的內容類型僅對應用程序很重要。 使用Car對象的算法和數據結構並不關心主干中的內容。 通過模板化類,您強制使用類的應用程序為中繼內容選擇類型,但在許多情況下,應用程序也不關心中繼內容。
第二種方法是從Car派生一個新類,它為trunk內容添加一個數據成員和訪問器:
class Car
{
public:
// Existing methods of your Car class
// No methods having anything to do with trunk contents.
private:
// No data member representing trunk contents.
}
class CarWithTrunkContents
{
public:
// Existing methods of your Car class
void setContentsOfTrunk(TrunkContentsType contentsOfTrunk);
TrunkContentsType contentsOfTrunk() const;
private:
TrunkContentsType m_contentsOfTrunk;
}
新的CarWithTrunkContents
類是一個特定於應用程序的類,它添加了應用程序在汽車上存儲中繼內容所需類型的數據成員。 這似乎也是不必要的重量級。 為什么必須派生一個全新的類來添加一個不影響類行為的額外數據? 如果使用Car
類的應用程序想要存儲中繼內容是相當常見的,為什么強制每個應用程序為其特定類型的中繼內容派生一個新類?
最后,雖然我設計的主干內容示例可能描繪了隨Car
對象傳播的任意主干內容的生動畫面,但實際上您可能會提供一種更通用的機制來將特定於應用程序的數據附加到Car
:
class Car
{
public:
// Existing methods of your Car class
void setUserData(void* userData);
void* userData() const;
private:
void* m_userData;
}
這樣,應用程序可以附加表示主干內容的對象,或表示駕駛執照和注冊的對象,或表示租賃協議的對象,或其他任何內容。 我已經看到這種void *指針被稱為“userData”(即由類的用戶理解),“blindData”(即該類對其攜帶的對象的內容是盲目的)或“applicationData”(即由申請定義的類型和目的的數據。
學習void *和其他C主題的一個很好的方法是觀看iTunes-U上夢幻般的Stanford“Programming Paradigms”的前半部分。 它真的很好地解釋了void *(C泛型)和指針! 它肯定幫助我更好地學習C ...
如果您希望能夠在函數中接受不同類型的數據,則最大的用途之一是使用void *。 (以下是一個例子: http : //142.132.30.225/programming/node87.html )
以下是您可以使用它們的另一個示例:
int i;
char c;
void *the_data;
i = 6;
c = 'a';
the_data = &i;
printf("the_data points to the integer value %d\n", *(int*) the_data);
the_data = &c;
printf("the_data now points to the character %c\n", *(char*) the_data);
如果你不想看免費的stanford課程,我建議使用googling void指針並閱讀那里的所有材料。
使用void *實現的此類C“泛型”的另一個示例是標准qsort函數:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
您可以對任何類型的數組進行排序:int,long,double,char *或一些struct指針......
當您編寫需要在多個操作系統上運行並且需要與底層框架API完全無關的代碼時,Void指針非常有用。
例如,OS X,Windows和Linux都具有窗口對象的基本概念,但它們都非常不同。 所以我有一些公共代碼將它們作為void *傳遞給它們,然后將特定於平台的實現轉換為本機類型(HWND等)。
但是,是的,正如其他人在這個帖子中所說的那樣,除非必要,否則肯定要避免這種事情。
它通常用在數字代碼中,例如C根求解器函數可能看起來像這樣:
double find_root(double x0, double (*f)(double, void*), void* params)
{
/* stuff */
y = f(x, params);
/* other stuff */
}
params
是由投f
到它所知道的一些結構,但find_root
沒有。
接下來與C接口,當我需要調試/跟蹤某些代碼並且想知道某個指針的地址時,我發現自己只使用了void指針。
SomeClass * myInstance;
// ...
std::clog << std::hex << static_cast< void* >(myInstance) << std::endl;
會印出類似的東西
0x42A8C410
並且,在我看來,很好地記錄了我正在嘗試做的事情(知道指針地址,而不是關於實例的任何內容)
void *
實際上是一個C-ism,並允許C做一些其他事情無法合理的事情。
char *
不能用於任何東西,因為不同的平台可以生成不同類型的指針 - char *
不一定與void *
處理相同(或甚至相同的大小)。
因此,當C中未知數據類型(或者是多態或其他動態)時, void *
允許您生成正確的底層指針類型 - 可以正確指向任何內容的指針類型。
在C ++中,除了在以一種或另一種形式與傳統C代碼接口的上下文中外,通常不應該出現void *
。
看看sqlite3_exec() 。 您啟動SQL查詢並希望以某種方式處理結果(將它們存儲到容器中)。 您調用sqlite3_exec()並將回調指針和void *指針傳遞給您想要的任何對象(包括容器)。 當sqlite3_exec()運行時,它會為每個檢索到的行調用回調並將該void *指針傳遞給它,以便回調可以轉換指針並執行您想要的任何操作。
重要的是sqlite3_exec()不關心回調的作用以及你傳遞的指針。 void *正是這樣的指針。
使用void指針有很大的優勢。 指針變量是一個存儲另一個變量地址的變量。 例:
int a;
int *x= &a;
現在'x'存儲整數變量的地址。
但是這個失敗了:
float f;
int *x = &f;
因為Integer指針變量只能存儲整數變量地址。 以同樣的方式,它適用於其他數據類型。
當您使用void *指針時,它給出了存儲任何TYPE變量地址的邊。
void *pointer = &i;
void *pointer = &f;
檢索它必須是尊重的。
*((int*)pointer)
所以,仔細利用void指針。
謝謝你,這可能會對你有所幫助。
當您想要將 memory 視為 memory 的一個塊而不是任何特定類型的 object 時,請使用void*
。
當您想要取消引用時,請確保將其類型轉換回原始類型,因為取消引用void*
是非法的,因為編譯器不知道要讀取多少 memory。
int (*f) (void);
f =(void*) getprocaddress(dll,"myfunction");
讓編譯器開心
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.