【问题标题】:Comparing values pointed at by void pointers in C比较 C 中 void 指针指向的值
【发布时间】:2014-11-03 14:26:42
【问题描述】:

我需要比较 C 中两个 void 指针的值。在下面的代码中,我在控制台打印出它们是不同的。好像比较的是这两个void指针所包含的地址。

如何使代码比较void 指针的值(即:1 == 1)并打印"same"?

int main(void)
{
    int i = 1;
    int j = 1;
    void *pi = &i;
    void *pj = &j;
    if (pi == pj) {
        printf("same.\n");
    } else {
        printf("different.\n");
    }
    return 0;
}

【问题讨论】:

  • 你没有 void 指针然后你有 int 指针然后 *pi == *pj。但是,虽然它是一个 void 指针,但它只是一个指针,然后尝试比较指向的内容是非常糟糕的做法
  • 使用memcmp 例如if(memcmp(pi, pj, sizeof(int))==0){
  • 更具体地说,我正在尝试实现一个链表,其中节点存储通用 void 值。在 delete 方法中,我想传递一个 void 项,该项与要删除的链表中的值进行比较。但是当我尝试比较两个 void 指针时,即使它们指向的值相同,比较也会返回 false。所以,我想知道是否有办法比较指针的值?
  • @jl987:如果不明确告诉编译器如何比较它们,就不可能比较void * 指针指向的值。编译器不知道它。只有你知道如何做到这一点。在最简单的情况下,您必须通过在强制转换中指定确切类型来告诉编译器这些值具有哪些类型。或者,您可以为 delete 方法提供通用比较函数(您将提供),就像使用标准 bsearch 或 qsort 函数一样。

标签: c pointers void-pointers


【解决方案1】:

您必须知道void 指针真正指向的数据类型,强制指针指向该类型,然后取消引用强制指针(并假设使用== 可以比较类型):

if (*(int *)pi == *(int *)pj)
    printf("same.\n");
else
    printf("different.\n");

只比较原始变量就容易多了:

if (i == j)
    …

或使用正确类型的指针:

int *pi = &i;
int *pj = &j;
if (*pi == *pj)
    …

在我不知道数据类型的情况下会发生什么(假设用户总是会传递相同类型的数据与我的链表删除方法进行比较)?

然后生活变得艰难。理想情况下,您可以让程序员向您传递一个比较器函数 — 参见 bsearch() 和 qsort() 来自标准 C 库的示例。

否则,您需要知道目标类型的大小,并希望memcmp() 能够很好地完成这项工作。这远不能保证 - 不清楚将两个 double 值与 memcmp() 进行比较总是会产生正确的答案(例如,它可能声称 NaN 的不同表示是不同的),并且将 struct 类型与填充进行比较是令人担忧,如果您传递了节点,它对于您的链表数据类型将无法正常工作(如果您从两个节点传递数据字段可能没问题)。

如果您需要进行排序而不仅仅是相等(例如二叉搜索树),那么情况会更糟——在这种情况下,需要程序员提供比较器函数。

【讨论】:

  • 在不知道数据类型的情况下会发生什么(假设用户总是会传递相同类型的数据与我的链表删除方法进行比较。)?
【解决方案2】:

指针是不同的。如果你想比较指针指向的整数,那么你想要

if ( *(int*)pi == *(int*)pj ) {
    //some code...
}

【讨论】:

    【解决方案3】:

    你必须将 void 指针类型转换为 int* 类型

    由于编译器不知道指针指向的地址类型,它不知道从指针地址读取多少字节并进行比较。试试这个

        int i = 2;
        int j = 1;
        void *pi = &i;
        void *pj = &j;
        if ( (*(int*)(pi)) == (*(int*)(pj)) ) 
        {
            printf("same.\n");
        } else 
        {
            printf("different.\n");
        }
        return 0;
    

    【讨论】:

    • 那么是不是不能比较两个指针(假设它们是同一个类型,但是你不知道是哪个类型,因此不能转换成它们各自的类型)?
    • 你怎么会不知道指针是什么类型.. 你正在编写程序。正如乔纳森指出的那样,如果您只需要使用一种类型的指针,则不需要 void 类型,只需从该类型(int *)开始。但是如果你想不断改变指针的类型以指向不同的值,那么你需要在每次比较时显式地进行类型转换。
    【解决方案4】:

    在您的 cmets 中,您正在尝试实现一个链表,但不知道指针目标的数据类型。您的设计是错误的:使用void* 指针,您根本无法(合理或可靠地)在不知道类型的情况下比较数据。你需要以不同的方式处理它。最好的方法是将链表实现为模板类。你的节点类看起来像

    template <typename T> class Node
    {
        private:
            T *m_pValue;
            Node *m_prev;
            Node *m_next;
        ...
    }
    

    但请注意,您需要非常小心内存管理。您需要决定谁将拥有和管理这些对象。在上面的类中,用户必须完全管理对象。如果您想减轻用户的负担,请使用自动指针,这样您的节点就会变成类似

    template <typename T> class Node
    {
        private:
            std::auto_ptr<T> m_pValue;
            Node *m_prev;
            Node *m_next;
        ...
    }
    

    在这两种情况下,您都可以通过

    比较值
    Node rhs, lhs;
    *(lhs.m_pValue) == *(rhs.m_pValue);
    

    这只是假设T类型可以通过==进行比较,这在大多数情况下应该是正确的。

    请注意,使用内存比较之类的建议很危险——这种比较完全忽略了底层数据类型的结构和语义。

    好的,鉴于这必须在 C 中,让你的节点像

    struct node
    {
        void * m_pValue;
        node * m_pPrev;
        node * m_pNext;
    }
    

    并要求您的链表的用户必须为您提供指向比较函数的指针

    int (comparitor*)(void* lhs, void* rhs);
    

    这正是 Jonathan Leffler 在下面提出的建议。究竟如何构建所有这些取决于您如何构建链表。

    【讨论】:

    • 感谢 C++ 中的建议!但是,我需要在 c 中实现这一点。
    • 糟糕!错过了那个小问题......当然,我想这就是创建 C++ 的原因,因为这样的问题。
    猜你喜欢
    • 2012-07-01
    • 2010-09-29
    • 2014-09-08
    • 1970-01-01
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多