【问题标题】:Why does bsearch return a void *?为什么 bsearch 返回一个 void *?
【发布时间】:2011-12-17 19:24:51
【问题描述】:
void * bsearch ( const void * key,
                 const void * base,
                 size_t num,
                 size_t size,
                 int ( * comparator ) ( const void *, const void * ) );

如果我传入const void * basebsearch 不应该也返回const void * 结果吗?

【问题讨论】:

    标签: c pointers constants void bsearch


    【解决方案1】:

    我认为这是 C 类型系统中最大和最烦人的缺陷。另一个例子是strchr,这个函数有完全相同的问题:它返回一个指向用户传入资源的指针。这个函数需要对 const 和非 const 输入参数都有用。你看到的是一种妥协。

    我觉得这样的访问者最烦人:

    const struct list *list_next(const struct list *x) { return x->next; }
    

    对于内部使用,宏是一个很好的“多态”实现

    #define LIST_NEXT(x) ((x)->next)
    

    但对于外部使用,您必须使用bsearch 妥协或两个单独的函数list_nextlist_next_const

    【讨论】:

      【解决方案2】:

      将限定符添加到指向类型是一种隐式转换,而删除限定符需要显式转换。

      bsearch() 的原型的编写方式允许以下两种用法,而无需显式转换:

      int needle = 0xdeadbeef;
      
      int foo[42] = { ... };
      int *p = bsearch(&needle, foo, 42, sizeof *foo, cmpi);
      
      const int bar[42] = { ... };
      const int *q = bsearch(&needle, bar, 42, sizeof *bar, cmpi);
      

      但是,这意味着可以使用 bsearch() - 以及许多其他 libc 函数 - 在没有警告的情况下删除 const-qualification,例如,如果我们编写了

      int *q = bsearch(&needle, bar, 42, sizeof *bar, cmpi);
      

      这是完全合法的:只有在我们实际使用 q 来修改 bar 时才会发生未定义的行为。

      您还应该记住,对指针参数进行 const 限定只会影响哪些参数在没有强制转换的情况下被接受,但并不能保证函数不会修改指向的对象。这只是几乎所有现有代码都遵循的约定,但语言语义并未强制执行。

      特别是,编译器不能在调用代码中使用此信息进行优化 - 编译器需要查看函数体,因为从指针中删除 const 限定并修改指向的对象是合法的本身并未声明为const

      过去,我认为额外限制限定指针参数会强制执行不变性,但仔细重新阅读第 6.7.3.1 节让我相信情况并非如此:对指向的对象施加的约束-to by restrict-qualified pointers 只有在指针实际用于访问对象时才生效,但调用代码不能仅从原型做出该假设...

      【讨论】:

      • 如果const不限制对象的修改,也不利于编译器优化,那么const到底是什么意思?
      • @Andrey: const 确实限制了对象的修改,但const * 没有;在某种程度上,标记指针参数const 只是一个语法考虑:它允许在没有强制转换的情况下传入 const 限定对象的地址,但没有真正的语义差异;但是,const *restrict 参数在语义上是不同的,但restrict 只影响被调用而不影响调用代码
      • restrict 指针只要求对指向对象的所有访问都直接或间接地使用该指针的值。这和 constness 有什么关系吗?
      • @Andrey:这是一个简化;更正式地说:如果(1)通过基于限制限定指针的表达式访问对象,并且(2)对象被修改,则(a)指针不能是 const 限定和(b)所有访问(包括修改)必须基于该指针;特别是,一个由限制限定的指向常量的指针指向的对象根本不能被修改(在该指针的生命周期内),但只有当指针用于访问该对象时,即修改是合法的只要指针从未使用过(!); (更多...)
      • (...) (a) 是必要的,因为如果它不存在,您可以获取一个限制限定的指向 const 的指针,将 const 扔掉并合法地使用该表达式来修改指向的对象;这意味着const *restrict 是比const **restrict 语义组合更强的约束
      【解决方案3】:

      当您搜索某些内容时,您可以在找到该内容后对其进行修改,这是一个有效的请求。如果搜索功能不允许您这样做,那就太严格了。当然,这样的修改可能会破坏后续搜索,但那是另一回事。

      参数是const,作为bsearch自身不会修改的promise,这是合理的。

      【讨论】:

      • 如果那个东西是作为const void* 给你的呢?您已承诺不修改它。您不应该请求修改的能力。然而,由于 C 不能重载函数,我想这是你必须接受的妥协。
      • @R.MartinhoFernandes 即使在 C++ 中,你也不能仅仅通过返回类型来重载。
      • @xanatos 但您也会超载参数! const void * bsearch ( const void * key, ...)void * bsearch ( void * key, ...).
      • 考虑到void* 需要被转换为有任何用途的东西,我认为这不是一个非常有力的论据:-) 但是例如将它与 strstr 进行比较(即做一些类似于 bsearch 的事情)我们可以看到这个论点的“智能”(他们想要统一)
      • @R.MartinhoFernandes 重载base 的常量会更有意义;)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-24
      • 2011-03-09
      • 1970-01-01
      • 1970-01-01
      • 2014-12-29
      • 2019-10-08
      相关资源
      最近更新 更多