假设函数进程输入并返回一个字符串.在此过程中,可能会发生错误.目前提出的风格是这样的:
int func(input params... char* buffer,unsigned int* buffer_size);
这种风格的好处是原型中包含了所有内容,包括错误代码.并且可以避免内存分配.问题是该功能非常冗长.并且因为buffer_size可以是任何,所以需要更多的代码来实现.
另一种选择是返回char *,并返回NULL以指示错误:
char* func(input params...);
此样式需要调用者删除缓冲区.内存分配是必需的,因此服务器程序可能会面临内存碎片问题.
第二个选项的变体是使用线程局部变量来保存返回的指针char *,这样用户就不需要删除缓冲区.
你喜欢哪种风格?还有原因吗?
解决方法
因此,如果您正在寻找“最低公分母”,您可能还想考虑目标环境中可用的语言结构(编译器可能接受标准C中的任何内容,但如果某些内容不受支持,则链接器会说不.
话虽如此,我总是会选择替代方案1.部分原因是(正如其他人所指出的那样),您不应该直接为用户分配内存(间接方法将进一步解释).即使用户保证使用纯C和纯C,他们仍然可以使用他们自己的自定义内存管理API来跟踪泄漏,诊断日志记录等.通常赞赏支持这样的策略.
在处理API时,错误通信是最重要的事情之一.由于用户可能有不同的方法来处理代码中的错误,因此您应该尽可能地保持整个API中的通信.用户应该能够以一致的方式和最少的代码将错误处理包装到您的API中.我通常总是建议使用清晰的枚举代码或定义/ typedef.我个人更喜欢typedef:ed enums:
typedef enum { RESULT_ONE,RESULT_TWO } RESULT;
因为它提供了类型/分配安全性.
具有get-last-error函数也很不错(但需要集中存储),我个人仅将其用于提供有关已识别错误的额外信息.
替代方案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 ); };
当然,这种结构的风格总是可以进行严肃的辩论.
祝好运!