【问题标题】:How to pass variable number of arguments to printf/sprintf如何将可变数量的参数传递给 printf/sprintf
【发布时间】:2010-11-06 14:01:44
【问题描述】:

我有一个类,它包含一个“错误”函数,它将格式化一些文本。我想接受可变数量的参数,然后使用 printf 对其进行格式化。

示例:

class MyClass
{
public:
    void Error(const char* format, ...);
};

Error 方法应该接受参数,调用 printf/sprintf 对其进行格式化,然后对其进行处理。我不想自己编写所有格式,因此尝试弄清楚如何使用现有格式是有意义的。

【问题讨论】:

    标签: c++ c printf variadic-functions


    【解决方案1】:
    void Error(const char* format, ...)
    {
        va_list argptr;
        va_start(argptr, format);
        vfprintf(stderr, format, argptr);
        va_end(argptr);
    }
    

    如果您想在显示字符串之前对其进行操作,并且确实需要先将其存储在缓冲区中,请使用 vsnprintf 而不是 vsprintfvsnprintf 将防止意外的缓冲区溢出错误。

    【讨论】:

      【解决方案2】:

      看看 vsnprintf,因为这会做你想要的http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/

      你必须先初始化 va_list arg 数组,然后调用它。

      来自该链接的示例: /* vsprintf 示例 */

      #include <stdio.h>
      #include <stdarg.h>
      
      void Error (char * format, ...)
      {
        char buffer[256];
        va_list args;
        va_start (args, format);
        vsnprintf (buffer, 255, format, args);
      
      
        //do something with the error
      
        va_end (args);
      }
      

      【讨论】:

      • vsnprintf 的第二个参数应该是缓冲区长度,包括终止的空字节 ('\0')。所以你可以在函数调用中使用 256 而不是 255。
      • 并且传递幻数是不好的......使用sizeof(buffer)而不是256。
      【解决方案3】:

      我应该阅读更多关于堆栈溢出中现有问题的信息。

      C++ Passing Variable Number of Arguments 是一个类似的问题。 Mike F 有以下解释:

      没有办法调用(例如)printf 不知道有多少论据 你正在传递给它,除非你想要 进入淘气和不可携带 技巧。

      通常使用的解决方案是 总是提供另一种形式 可变参数函数,所以 printf 有 vprintf 需要一个 va_list ......的......版本只是 va_list 版本的包装器。

      这正是我想要的。我执行了这样的测试实现:

      void Error(const char* format, ...)
      {
          char dest[1024 * 16];
          va_list argptr;
          va_start(argptr, format);
          vsprintf(dest, format, argptr);
          va_end(argptr);
          printf(dest);
      }
      

      【讨论】:

      • 最后的'printf(dest);'格式错误 - 它至少也需要一个格式字符串。
      • 它不是因为字符串是格式字符串,即 printf("a string");很好
      • 您可以使用 printf(dest) 直到 dest 恰好包含“%s”或“%d”,然后 BOOM。请使用 printf("%s", dest)。
      • 只是想指出核心转储是最好的情况,在服务器代码中执行此操作,黑客会将您的 CPU 当作早餐。
      • 尝试使用“%.16383s”,这样可以保护数组 dest 不溢出。 (允许使用 '\0' 终止符)
      【解决方案4】:

      您正在寻找variadic functions。 printf() 和 sprintf() 是可变参数函数 - 它们可以接受可变数量的参数。

      这基本上需要以下步骤:

      1. 第一个参数必须说明后面的参数数量。所以在 printf() 中,“format”参数给出了这个指示——如果你有 5 个格式说明符,那么它会再寻找 5 个参数(总共 6 个参数)。第一个参数可以是一个整数(例如“myfunction” (3, a, b, c)”,其中“3”表示“3 个参数”

      2. 然后使用 va_start() 等函数循环并检索每个连续的参数。

      有很多关于如何做到这一点的教程 - 祝你好运!

      【讨论】:

        【解决方案5】:

        下面的简单示例。注意你应该传入一个更大的缓冲区,并测试缓冲区是否足够大

        void Log(LPCWSTR pFormat, ...) 
        {
            va_list pArg;
            va_start(pArg, pFormat);
            char buf[1000];
            int len = _vsntprintf(buf, 1000, pFormat, pArg);
            va_end(pArg);
            //do something with buf
        }
        

        【讨论】:

          【解决方案6】:

          使用带有省略号的函数不是很安全。如果性能对日志函数不重要,请考虑使用运算符重载,如 boost::format。你可以这样写:

          #include <sstream>
          #include <boost/format.hpp>
          #include <iostream>
          using namespace std;
          
          class formatted_log_t {
          public:
              formatted_log_t(const char* msg ) : fmt(msg) {}
              ~formatted_log_t() { cout << fmt << endl; }
          
              template <typename T>
              formatted_log_t& operator %(T value) {
                  fmt % value;
                  return *this;
              }
          
          protected:
              boost::format                fmt;
          };
          
          formatted_log_t log(const char* msg) { return formatted_log_t( msg ); }
          
          // use
          int main ()
          {
              log("hello %s in %d-th time") % "world" % 10000000;
              return 0;
          }
          

          以下示例演示了省略号可能出现的错误:

          int x = SOME_VALUE;
          double y = SOME_MORE_VALUE;
          printf( "some var = %f, other one %f", y, x ); // no errors at compile time, but error at runtime. compiler do not know types you wanted
          log( "some var = %f, other one %f" ) % y % x; // no errors. %f only for compatibility. you could write %1% instead.
          

          【讨论】:

          • 这就是如何让一件容易的事情变得困难。
          • “使用带省略号的函数不是很安全。”如果您唯一安全的替代方案涉及 c++ 和 boost,您应该解释“不是很安全”的意思,并提及如果您使用正确的格式说明符,printf 函数是完全安全的。
          【解决方案7】:

          查看示例http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/,它们将参数的数量传递给方法,但您可以省略它并适当地修改代码(参见示例)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-02-27
            • 2011-11-14
            • 1970-01-01
            • 2014-01-27
            • 2011-10-09
            • 1970-01-01
            相关资源
            最近更新 更多