获取任意数据的函数:使用void *或char *?

时间:2015-06-24 09:54:17

标签: c

我最近在C中为LevelDB编写了一个包装器,并且发现了以下问题。用于在数据库中存储数据的LevelDB函数如下所示:

leveldb_put(leveldb_t* db, const leveldb_writeoptions_t* options, const char* key, size_t keylen, const char* val, size_t vallen, char** errptr);

对于键和值,他们使用char*。这意味着我必须抛出不是char指针的参数。这通常是因为我经常将结构存储在数据库中。

考虑到这一点后,我决定在我的包装函数中使用void*来获取密钥和数据。它看起来像这样:

int db_put(db_t db, void *key, size_t keylen, void *value, size_t valuelen)
{
    char *k = (char*)key;
    char *v = (char*)value;

    /* Call leveldb_put() here with k and v as parameters. */

    return 0;
}

这样我就不必将我传递给我db_put()函数的参数强制转换。我认为这个解决方案更优雅,但我想LevelDB在选择char指针时知道他们在做什么。

是否有理由不使用void*将任意数据传递给函数?

4 个答案:

答案 0 :(得分:4)

  

是否有理由不使用void *将任意数据传递给a   功能

没有。事实上,void * 存在以便于传递任意数据而无需丑陋的投射。这就是ptr-to-void标准化的原因。至少在C中。 C ++是一个不同的野兽。

在LevelDB,他们必须处理带有char *或C89前编译器的历史代码,或者导致重构惯性的任何其他隐藏的原因。他们的代码也适用于ptrs-to-void。

请注意,在您的db_put版本中,应删除广告素材,因为它们是多余的。

答案 1 :(得分:0)

void *可以被认为是一个包含指针的黑盒子。抱着它 在一个空白*你有效地说你不关心或它包含在那一点,所以这将允许你做出任何假设。 Void *是一个“真正的”通用指针,可以直接分配给任何特定的数据类型,而无需使用强制转换。

同时,char *明确指定相应对象的类型。最初没有char *和char *也用于表示通用指针。当使用char *时,需要使用显式强制转换,但是不建议使用char *作为通用指针,因为它可能会产生混淆,就像在那里很难判断char *是否包含字符串或一些通用数据。

此外,在char *中执行算术是合法的,但在void *上则不合法。

使用void *的缺点是它们的主要用途,它们可以隐藏您存储的数据的实际类型,这可以防止编译器和其他东西检测到类型错误。

在您的特定情况下,使用void *而不是char *没有问题,因此您可以使用void *而无需担心。

编辑:更新并重新编写了纠正错误信息的答案

答案 2 :(得分:0)

当前接受的答案只是为了奉承OP;它实际上有点无效。

考虑您的任意struct可能(很可能)在某处填充字节。这些填充字节的值是不确定的,可能是也可能不是无关紧要。

如果您put structkey有填充字节,请考虑会发生什么情况,然后您尝试get value { {1}}除了填充字节外其他方面都是相同的。

如果您将来选择这样做,还要考虑如何处理指针成员。

如果您打算将key用作struct,最好将其序列化,这样您就可以保证检索相应的key而无需担心这些填充位

也许您可以传递一个函数指针,告诉您的包装器如何将value序列化为字符串......

答案 3 :(得分:0)

你应该序列化为像json这样的标准格式,而不是像这样处理原始数据。它看起来非常容易出错,除非你总是假设任意数据只是一个字节缓冲区。在这种情况下,我将使用uint8_t指针(这是一个unsigned char *)并将所有数据结构转换为它,以便例程只是认为它正在处理字节缓冲区。

关于无效的说明*:我几乎从未使用它们。在介绍void指针时要仔细考虑,因为在大多数情况下,您可以采用正确的方法来利用标准类型来避免将来的错误。你应该使用void *的唯一地方是malloc()这样的地方,那里没有更好的方法。