何时使用void指针?

时间:2009-06-22 05:31:43

标签: c++ c pointers

我理解使用void指针进行malloc实现。

void* malloc  ( size_t size );

任何人都可以提出其他原因或提供一些在实践中有用的方案。

由于

13 个答案:

答案 0 :(得分:30)

一个好的方案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 ++,我没有任何具体有用的例子。

答案 1 :(得分:12)

如果您正在与C代码接口并需要通过C ++对象,但C库只接受通用指针,那么当您检索指针时,需要将其重新转换为正确的类型。

Void指针可能不应该经常使用,但是当你尝试使用适用于任意指针的库函数时,它们可以提供帮助,而且并不真正关心那些内存表示的数据。

答案 2 :(得分:9)

只要数据块的内容不重要,就应该使用

void个指针。例如,在复制数据时,复制存储区的内容,但数据的格式并不重要。

对于使用void指针操作内存块而不需要了解内容的函数,可以向用户说明设计,以便他们知道函数不关心任何数据格式。 当函数实际上是内容不可知时,函数经常被编码以使char *处理内存块。

答案 3 :(得分:9)

在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派生一个新类,为中继内容添加数据成员和访问器:

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 *指针被称为&#34; userData&#34; (即,由班级用户理解),&#34; blindData&#34; (即班级对其所携带的物品的内容视而不见)或&#34; applicationData&#34; (即由申请定义的类型和目的的数据)。

答案 4 :(得分:3)

了解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);

如果您不想观看免费的斯坦福课程,我建议使用谷歌搜索无效指针并阅读那里的所有资料。

答案 5 :(得分:3)

使用void *实现的此类C“泛型”的另一个示例是标准qsort函数:

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

你可以对任何类型的数组进行排序:int,long,double,char *或一些struct指针......

答案 6 :(得分:2)

它通常用在数字代码中,例如C根求解器函数可能看起来像:

double find_root(double x0, double (*f)(double, void*), void* params)
{
/* stuff */
y = f(x, params);
/* other stuff */
}

paramsf投射到它所知道的某个结构,但find_root没有。

答案 7 :(得分:2)

在与C接口之后,我发现自己只需要使用void指针来调试/跟踪某些代码并想知道某个指针的地址。

SomeClass * myInstance;
// ...
std::clog << std::hex << static_cast< void* >(myInstance) << std::endl;

会打印类似

的内容
0x42A8C410

而且,在我看来,很好地记录了我正在尝试做的事情(知道指针地址,而不是关于实例的任何内容)

答案 8 :(得分:2)

void *实际上是一个C-ism,允许C做一些其他事情无法合理的事情。

char *无法用于任何事情,因为不同的平台可以生成不同类型的指针 - char *不一定与{{void *处理相同(或甚至相同的大小) 1}}。

因此,当C中未知数据类型(或多态或其他动态)时,void *允许您生成正确的基础指针类型 - 可以指向任何正确的指针类型。

在C ++ void *中,除了在以一种或另一种形式与传统C代码接口的上下文中外,通常不应该出现。{/ p>

答案 9 :(得分:1)

当您编写需要在多个操作系统上运行并且需要与底层框架API完全无关的代码时,Void指针非常有用。

例如,OS X,Windows和Linux都具有窗口对象的基本概念,但它们都非常不同。所以我有一些公共代码将它们作为void *传递给它们,然后将特定于平台的实现转换为本机类型(HWND等)。

但是,是的,正如其他人在这个帖子中所说的那样,除非必要,否则肯定要避免这种事情。

答案 10 :(得分:1)

看看sqlite3_exec()。您启动SQL查询并希望以某种方式处理结果(将它们存储到容器中)。您调用sqlite3_exec()并将回调指针和void *指针传递给您想要的任何对象(包括容器)。当sqlite3_exec()运行时,它会为每个检索到的行调用回调并将该void *指针传递给它,以便回调可以转换指针并执行您想要的任何操作。

重要的是sqlite3_exec()不关心回调的作用以及你传递的指针。 void *正是这样的指针。

答案 11 :(得分:1)

使用void指针有很大的优势。 指针变量是一个存储另一个变量地址的变量。 例如:

int a;
int *x= &a;

现在'x'存储整数变量的地址。

但是这个失败了:

float f;
int *x = &f;

因为Integer指针变量只能存储整数变量地址。 以同样的方式适用于其他数据类型。

当你使用void *指针时,它给出了存储任何TYPE变量地址的边缘。

void *pointer = &i;
void *pointer = &f;

检索它时必须进行修改。

*((int*)pointer)

因此,请小心使用void指针。

这可能对您有所帮助,谢谢。

答案 12 :(得分:-3)

int (*f) (void);
f =(void*) getprocaddress(dll,"myfunction");

让编译器开心