在一次采访中,我被要求(除其他外)实现以下功能:
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;
}
问题作者声称此实现存在错误。不仅仅是在特定深奥系统上可能失败的标准违规,而是一个“真正的”错误,它可能在大多数系统上偶然失败。
它也与使用 ofint
而不是适合内存能力的类型无关,例如size_t
or ptrdiff_t
。说,字符串的大小是“合理的”。
我真的不知道这个错误可能是什么。恕我直言,所有指针算术都可以。我什至不认为两个后续调用会vsnprintf
产生相同的结果。恕我直言,所有可变参数处理的东西也是正确的。va_copy
不需要(这是使用 的被调用者的责任va_list
)。也在x86上va_copy
并且va_end
没有意义。
如果有人能发现(潜在的)错误,我将不胜感激。
编辑:
查看答案和评论后 - 我想添加一些注释:
- 自然地,我已经使用各种输入构建并运行了代码,包括在调试器中一步一步地观察变量状态。如果不先亲自尝试,我永远不会寻求帮助。我没有看到任何问题,没有堆栈/堆损坏等。我还在调试版本中运行它,启用了调试堆(它不能容忍堆损坏)。
- 我假设函数是使用有效参数调用的,即
psz
是一个有效的指针(不要与 混淆*psz
),szFmt
是一个有效的格式说明符,并且所有可变参数都被评估并对应于格式字符串。 - 根据标准,
free
用指针调用是可以的。NULL
vsnprintf
使用NULL
指针和大小 = 0可以调用。它应该返回结果字符串长度。MS 版本虽然不完全符合标准,但在这种特定情况下也是如此。vsnprintf
不会超过指定的缓冲区大小,包括 0 终止符。意味着 - 它并不总是放置它。- 请把编码风格放在一边(如果你不喜欢它——我很好)。