【问题标题】:void* or char* for generic buffer representation?void* 或 char* 用于通用缓冲区表示?
【发布时间】:2011-04-18 07:23:32
【问题描述】:

我正在设计一个 Buffer 类,其目的是表示一块内存。

我的底层缓冲区是char*(嗯,实际上是boost::shared_array<char>,但这并不重要)。

我一直在决定为我的构造函数选择什么原型:

我应该去:

Buffer(const void* buf, size_t buflen);

或与:

Buffer(const char* buf, size_t buflen);

还是别的什么?

通常会做什么,为什么?

【问题讨论】:

  • @Luca Matteis:我打算提供内存的隐式共享。但这完全是题外话。
  • 有一个不幸的变形——由 Unicode 提示——char 正在成为octet 的同义词,与“字符”几乎没有关系。这个问题是这种变化的征兆;我有时很想typedef char octet; 以减少代码的误导性。
  • @msw:除了与字符串文字的类型兼容性之外,还有什么理由使用“char”,而不是“signed char”或“unsigned char”?这对我来说似乎是一种相当无用的类型。
  • @supercat:我同意你的观点;如果我这些天更多地使用 C,我可能会很在意断言“签名字符”至少会从语言中删除。换句话说,我的意思是typedef unsigned char octet,如果我想得更多的话。
  • @msw:您为什么要消除一种有用且必不可少的类型? 'char' 类型是最没用的,因为它的有用范围只有 0..127。

标签: c++ arrays buffer


【解决方案1】:

API 接口对用户来说更清晰,如果 buffer 是 void* 类型,而 string 是 char* 类型。比较 memcpy 和 strcpy 函数定义。

【讨论】:

  • 调用char* 暗示它是签名数据或字符数据。如果数据不是字符,或者被解释为有符号字节,那么你不应该使用char*来指向它。
【解决方案2】:

对于构造函数和其他 API 函数,void* 的优点是它允许调用者传入指向任何类型的指针,而无需进行不必要的强制转换。如果调用者能够传入任何类型是有意义的,那么void* 是更可取的。如果只有调用者能够传入char* 才有意义,请使用该类型。

【讨论】:

    【解决方案3】:

    C++17

    C++17 专门为此引入了std::byte

    它的定义其实很简单:enum class byte : unsigned char {};


    我通常使用unsigned char 作为底层结构(我不希望签名弄乱我的缓冲区,因为我知道是什么原因)。但是我通常将其键入:

    // C++11
    using byte = unsigned char;
    
    // C++98
    typedef unsigned char byte;
    

    然后将其称为 byte*,在我看来,它巧妙地传达了含义,至少比 char*void* 更好。

    【讨论】:

    • 我不能 +1 这么难。这消除了所有带有char 的错误,例如char buf[42]; ... if(buf[i] == 0xFF) // *never true*
    • 您需要注意一件事 - 如果缓冲区包含任何空字节,它将被视为 C 字符串的空终止符并减小“缓冲区”由 sizeof 返回。
    • sizeofstrlen 不同,不考虑空终止符
    • 这应该是公认的答案。 char 和 unsigned char 的东西真的很烦人,而且完全违反直觉。
    • @JohnZ.Li:感谢您的评论,我注意到我的回答已经过时了。 C++17 引入enum class byte : unsigned char {};,比 typedef 还要好!
    【解决方案4】:

    我更喜欢char*,因为对我个人而言,它更适合作为“缓冲区”。 void* 似乎更像是“指向我不知道什么的指针”。此外,无论如何,这就是你的基础。

    【讨论】:

    • A void* 不是“指向我不知道在哪里的指针”,而是“指向我不知道什么的指针”:-)
    • @pax:是的,我想这听起来更好(虽然最初我认为它听起来相当),所以我把它改成了“什么”
    • 但是一个通用缓冲区就是这样:一个“我不知道什么”的缓冲区。
    • “指向我不知道在哪里的指针”将是 uninitialized pointer
    【解决方案5】:

    我推荐 uint8_t,它在 stdint.h 中定义。它与“typedef unsigned char byte;”基本相同。其他人一直在推荐,但它具有成为 C 标准一部分的优势。

    至于 void*,我只会将其用于多态性。 IE。如果我还不知道它会指向什么类型的东西,我只会称它为 void 指针。在你的情况下,你有一个字节数组,所以我会使用 uint8_t* 作为类型来标记它。

    【讨论】:

      【解决方案6】:

      对于缓冲区实现,我更喜欢unsigned char *uint8_t *,因为void * 有一个令人讨厌的限制,即您不能对其执行指针数学运算。因此,如果您想在缓冲区的某个偏移量处处理一些数据,或者只是将缓冲区分成块或其他任何东西,那么无论如何您都会被强制转换为其他类型来进行数学运算。

      我更喜欢unsigned char *uint8_t * 而不是普通的char *,因为special rules 涉及别名和char *,这可能会严重影响缓冲区的某些循环。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-27
        • 2017-01-26
        • 1970-01-01
        • 1970-01-01
        • 2019-04-25
        • 1970-01-01
        • 2019-03-19
        相关资源
        最近更新 更多