在这种情况下,您不需要判断文档就可以对返回任意字符串/字符串数组API做出合理的假设.C字串是ugly,所以你实际上是在谈论三种可能性:
API接受一个字符串,并在该字符串中返回一个指针.显然,它没有分配新的字符串,所以不需要free
,但您仍然记录了它在提供的字符串中返回一个指针(您不希望strtok
的用户认为它给了他们新分配的子字符串)
API返回一个新字符串,可以是:
- 在静态/缓存存储中分配(必须记录,因为它涉及显式释放函数以返回缓存,和/或它本质上是不可重入的且不是线程安全的),或者
- 新分配的(必须释放,尽管不总是
free
个,所以您也需要记录)
除了这些一般情况外,没有可依赖的标准;如果它明显没有分配,则将其记录下来(缺少free
就是隐含的),但在所有其他情况下,您必须记录每个函数需要进行的清理(如果有).
当我们讨论字符串数组时,它变得更加复杂.字符串拆分函数可以通过以下三种方式之一合理地实现(对于合理的定义而言):
- 一个类似
strtok
的函数,它就地修改原始字符串(用NUL
s替换分隔符),并返回一个新分配的指向现有字符串的指针数组,该数组必须是free
d,但子字符串不需要是free
d(事实上,原始字符串必须保持活动状态,直到使用完数组为止)
- 一个天真的解决方案,
malloc
个指针数组,以及每个子字符串,具有离散的分配.如果他们通常会提供一个自定义释放函数来避免每个调用者重新实现它,free
将是一件非常痛苦的事情.与任何其他解决方案相比,它的性能更低,内存开销更大,但它确实意味着您可以单独修改realloc
和修改子字符串,这是有优势的.
- 结合了#1和#2的特征的优化版本,其中单个批量分配包含所有子字符串指针,后跟子字符串的原始数据.像#1一样,只需要单个
free
来清理数据,并且作为交换,存储等于输入字符串大小的额外数据(但没有多个分配开销和内存碎片),像#2一样,输入字符串的生命周期 可以保持不变,输入字符串的生命周期 不需要被延长以匹配子字符串指针的生命周期 (作为交换,不能执行子字符串的realloc
).
所有这些对于输入字符串的生存期和可变性都有不同的规则,返回值的哪些组件可以修改,如何修改,以及完成后如何清理.没有单一的标准可供 Select ,因此,您必须显式地记录它(对于第二种情况,如果您将清理简化为"调用此释放函数"并提供可重入性保证,则在技术上不需要记录确切的设计,但如果您隐藏这些实现细节,则API使用者无法从该设计的一些独特好处中受益,例如允许realloc
和变化子字符串).