我正在开发一个支持多种编程环境的库,比如VB6和FoxPro。我必须坚持使用C语言,因为它是最小的公分母。现在我有一个关于风格的问题。
假设函数处理输入并返回一个字符串。在此过程中,可能会发生错误。当前建议的样式是:
int func(input params... char* buffer, unsigned int* buffer_size);这种风格的好处是,原型中包含了所有内容,包括错误代码。并且可以避免内存分配。问题是这个函数相当冗长。因为buffer_size可以是任意的,所以它需要更多的代码来实现。
另一种选择是返回char*,并返回NULL表示错误:
char* func(input params...);这种风格要求调用者删除缓冲区。内存分配是必需的,因此服务器程序可能会面临内存碎片问题。
第二个选项的变体是使用线程局部变量来保存返回的指针char*,这样用户就不需要删除缓冲区。
你喜欢哪种风格?原因呢?
发布于 2009-03-09 15:32:46
当涉及到这个问题时,我有点“被损坏了”。我曾经为嵌入式电信设计和维护相当大的API。一个你不能认为任何事情都是理所当然的环境。甚至不是像全局变量或TLS这样的东西。有时,甚至堆缓冲区也会出现,实际上是寻址ROM内存。
因此,如果您正在寻找“最小公分母”,您可能还需要考虑在您的目标环境中有哪些语言构造可用(编译器很可能接受标准C中的任何内容,但如果某些内容不受支持,则链接器将拒绝)。
话虽如此,我还是会选择alternative 1。部分原因是(正如其他人所指出的),您永远不应该直接为用户分配内存(下面将解释一种间接方法)。即使用户被保证使用纯C语言,他们仍然可以使用他们自己的自定义内存管理API来跟踪泄漏,诊断日志记录等。
在处理时,错误通信是最重要的事情之一。由于用户可能有不同的方法来处理其代码中的错误,因此您应该在整个API中对这种通信保持尽可能一致。用户应该能够以一致的方式和最少的代码将错误处理封装到API中。我通常推荐使用清晰的枚举代码或定义/typedefs。我个人更喜欢typedef:ed枚举:
typedef enum {
RESULT_ONE,
RESULT_TWO
} RESULT;..because它提供了类型/赋值安全性。
有一个get-last-error功能也很好(但是需要中央存储),我个人只使用它来提供关于已经识别的错误的额外信息。
alternative 1的冗长可以通过像这样的简单复合来限制:
struct Buffer
{
unsigned long size;
char* data;
};那么你的api看起来可能会更好:
ERROR_CODE func( params... , Buffer* outBuffer );这一策略也为更复杂的机制打开了大门。例如,你必须能够为用户分配内存(例如,如果你需要调整缓冲区的大小),那么你可以提供一种间接的方法:
struct Buffer
{
unsigned long size;
char* data;
void* (*allocator_callback)( unsigned long size );
void (*free_callback)( void* p );
};当然,这种构造的风格总是值得认真讨论的。
祝好运!
发布于 2009-03-09 15:10:25
我更喜欢第一个定义,其中缓冲区和它的大小是传入的。也有例外,但通常您不希望在调用函数后进行清理。然而,如果我分配内存并将其传递给一个函数,那么我知道我必须自己清理。
处理不同大小的缓冲区应该不是什么大问题。
发布于 2009-03-09 15:07:43
如果我必须在显示的两种风格中选择一种,我每次都会选择第一种。第二种风格给你的库的用户提供了一些其他的东西来考虑,内存分配,并且肯定会有人忘记释放内存。
https://stackoverflow.com/questions/626567
复制相似问题