API设计 - 分配输出?

6
C API函数是否应该分配其输出,或者让用户指定输出缓冲区?例如:
BOOL GetString(
    PWSTR *String
    );
...
PWSTR string;
GetString(&string);
Free(string);

对比

BOOL GetString(
    PWSTR Buffer,
    ULONG BufferSize,
    PULONG RequiredBufferSize
    );
...
// A lot more code than in the first case

更具体地说,我想知道为什么Win32 API主要使用第二种情况(例如GetWindowTextLookupAccountSid)。如果API函数知道输出的大小,为什么用户要试图猜测输出大小呢?我找不到任何关于为什么使用第二种情况的信息。
另外:LookupAccountSid示例特别糟糕。在内部,它使用LSA API为调用者分配输出。然后,在LookupAccountSid中,当它可以直接从LSA返回输出时,它让用户分配缓冲区(并猜测正确的缓冲区大小)!为什么?

这要看情况。两种习语都有使用,但各有优缺点。 - Mitch Wheat
除了能够使用基于堆栈的缓冲区之外,第二种情况有什么优势呢? - wj32
它允许调用者使用他们选择的分配器。如果您分配内存并由调用者自行释放,那么调用者需要使用相应的解除分配器 - 这可能不是他们的首选。填充调用者提供的缓冲区使他们可以为其目的选择适当的分配器,而不是依赖于API选择的分配器。 - Anon.
2个回答

6

Win32 API没有预先分配缓冲区,因为它希望给调用代码提供如何提供缓冲区的选择。这允许他们提供堆栈和各种基于堆的缓冲区。有几个地方已经提前知道了缓冲区的最大大小,而开发人员希望使用基于堆栈的缓冲区来简化操作。

文件系统是最好的例子,因为路径不会超过MAX_PATH。所以,开发者只需声明一个基于堆栈的缓冲区,而无需进行分配和释放操作。

C API分配内存的优点在于简化了调用模式。Win32模式的缺点是大多数情况下需要调用API两次。第一次是为了确定缓冲区的大小,第二次是使用适当大小的缓冲区。使用API分配的缓冲区则只需要一次调用。

然而,缺点是你从调用者那里夺走了分配的选择。此外,您必须传达您的选择,以便他们正确释放API(例如Windows可以从几个不同的位置分配)。


3
我注意到将这两个调用和分配封装在自己的函数中非常简单,因此简化调用模式只是一个很小的问题。如果你只打算提供一个版本的API,那么它必须是更灵活的那个。 - Steve Jessop

1

第二种方法有一些优点,如下:

  • 它允许调用者管理内存分配的生命周期
  • 它允许调用者为符合同一模式的不同调用重复使用已分配的内存
  • 它允许调用者决定提供哪个缓冲区,例如堆栈或堆。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接