我理解使用void指针实现malloc的用法。
void* malloc ( size_t size );
有没有人能提出其他原因或者提供一些实际应用场景。
谢谢
使用 void*
的一个好的情况是当你想要实现任何通用的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
指针可以向用户明确设计意图,让他们知道函数不关心任何数据格式。通常,函数使用char *
处理内存块,即使函数实际上与数据内容无关。
在C++中,我发现void*指针最吸引人的用途是提供给代码一个选项,在已经使用的对象上存储任意的"用户数据"。
假设你编写了一个表示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*。
第一种方法是根据行李箱内容对象的类型模板化您的类:
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 派生一个新类,为行李箱内容添加一个数据成员和访问器:
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”(即应用程序定义的类型和目的的数据)。
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);
void *
实际上是C语言的一种机制,它允许C做一些在其他情况下无法合理完成的事情。
char *
不能被用于任何可移植的事情,因为不同的平台可以生成不同类型的指针。一个char *
不一定会被处理成和void *
相同(甚至不是相同的大小)的指针。
所以当数据类型在C中不确定(或者是多态的或者其他动态的情况),那么void *
允许您生成正确的底层指针类型 - 能正确指向任何东西的指针类型。
在C ++中,void *
通常不会出现,除非涉及与遗留的C代码进行交互的一种形式或另一种形式。
通常在数值代码中使用,例如C语言的根求解器函数可能如下所示:
double find_root(double x0, double (*f)(double, void*), void* params)
{
/* stuff */
y = f(x, params);
/* other stuff */
}
params
被f
转换为一些它知道的结构,但find_root
不知道。
void*
是回调函数和其他一些需要向函数传递参数但不需要知道参数类型的情况下很好的选择。 - Phil Miller另一个使用void *实现的C语言“泛型”的例子是标准的qsort函数:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
int a;
int *x= &a;
现在,'x'存储了整数变量的地址。
但是这个失败了:
float f;
int *x = &f;
因为整数指针变量只能存储整数变量地址。同样的方式适用于其他数据类型。
当您使用void *指针时,它可以存储任何类型变量的地址。
void *pointer = &i;
void *pointer = &f;
在检索时,必须进行解引用。
*((int*)pointer)
因此,请小心使用空指针。
这可能对您有所帮助,谢谢。
void*
代替char*
提供了一定的类型安全性。这样告诉编译器:“我不应该被允许引用这些指针中的任何一个。” - Phil Miller