在一次采访中,我被要求(除其他外)实现以下功能:
int StrPrintF(char **psz, const char *szFmt, ...);
类似于
sprintf
,除了函数必须自己分配它而不是已经分配的存储空间,并在 *psz
变量中返回。此外,*psz
可能指向一个已经分配的字符串(在堆上),它可能在格式化期间被使用。当然,这个字符串必须通过适当的方式释放。返回值应该是新创建的字符串的长度,或者错误时为负。
这是我的实现:
int StrPrintF(char **psz, const char *szFmt, ...)
{
va_list args;
int nLen;
va_start(args, szFmt);
if ((nLen = vsnprintf(NULL, 0, szFmt, args)) >= 0)
{
char *szRes = (char*) malloc(nLen + 1);
if (szRes)
if (vsnprintf(szRes, nLen + 1, szFmt, args) == nLen)
{
free(*psz);
*psz = szRes;
}
else
{
free(szRes);
nLen = -1;
}
else
nLen = -1;
}
va_end(args);
return nLen;
}
问题作者声称此实现中存在错误。不仅是可能在特定深奥系统上失败的标准违规,而且是一个“真正的”错误,它可能在大多数系统上偶然失败。
它也与使用
int
而不是适合内存容量的类型(例如 size_t
或 ptrdiff_t
)无关。比如说,字符串的大小“合理”。我真的不知道错误可能是什么。恕我直言,所有指针算法都可以。我什至不认为
vsnprintf
的两次随后调用会产生相同的结果。恕我直言,所有可变参数处理的东西也是正确的。不需要 va_copy
(这是使用 va_list
的被调用者的责任)。同样在 x86 上 va_copy
和 va_end
是没有意义的。如果有人能发现(潜在的)错误,我将不胜感激。
编辑:
查看答案和评论后 - 我想添加一些注释:
psz
是一个有效的指针(不要与 *psz
混淆), szFmt
是一个有效的格式说明符,并且所有可变参数都被评估并对应于格式字符串。 free
指针调用 NULL
按照标准是可以的。 vsnprintf
指针和 size=0 调用 NULL
是可以的。它应该返回结果字符串长度。 MS 版本虽然不完全符合标准,但在这种特定情况下也是如此。 vsnprintf
不会超过指定的缓冲区大小,包括 0 终止符。意味着 - 它并不总是放置它。 最佳答案
不太对。我在 C11 标准中没有发现对 vsnprintf
的任何此类要求。它确实在脚注中说明了这一点:
当您调用 vsnprintf
时,va_list
可以按值或按引用传递(就我们所知,它是一种不透明的类型)。所以第一个 vsnprintf
实际上可以修改 va_list
并破坏第二个。推荐的方法是使用 va_copy
制作副本。
事实上,根据 this article 的说法,它不会在 x86 上发生,但在 x64 上会发生。
关于c - 关于 vsnprintf(采访),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10069597/