在一次采访中,我被要求(除其他外)实现以下功能:

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_tptrdiff_t )无关。比如说,字符串的大小“合理”。

我真的不知道错误可能是什么。恕我直言,所有指针算法都可以。我什至不认为 vsnprintf 的两次随后调用会产生相同的结果。恕我直言,所有可变参数处理的东西也是正确的。不需要 va_copy (这是使用 va_list 的被调用者的责任)。同样在 x86 上 va_copyva_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/

    10-11 21:16