将零参数包传递给 printf

Passing zero argument pack to printf

我创建了一个 class,它有一个可变模板方法。此方法调用 printf 函数。将零参数传递给该方法时,我收到 gcc 的编译警告:

warning: format not a string literal and no format arguments [-Wformat-security]

一个简化的 class 例子是:

class printer{
    std::map<int,std::string> str;
  public:
    printer(){
      str[0] = "null\n";
      str[1] = "%4d\n";
      str[2] = "%4d %4d\n";
      str[3] = "%4d %4d\n%4d\n";
    }
    template<typename ...Args>
    void print(Args... args){
      printf(str[sizeof...(args)].c_str(),args...);
    }
};

使用时

printer p;
p.print(23);
p.print(345,23);

一切顺利编译,但使用时

printer p;
p.print();

我收到编译警告

main.cpp: In instantiation of ‘void printer::print(Args ...) [with Args = {}]’:
main.cpp:23:11:   required from here
main.cpp:17:50: warning: format not a string literal and no format arguments [-Wformat-security]
       printf(str[sizeof...(args)].c_str(),args...);

当然如果我打电话

printf("null\n");

没有出现警告。

有人可以解释为什么会这样吗?

我可以在不禁用 -Wformat-security 标志的情况下删除警告吗?

这是一个预期的警告,如果我们查看 -Wformat-security 的文档,它说:

-Wformat-security If -Wformat is specified, also warn about uses of format functions that represent possible security problems. At present, this warns about calls to printf and scanf functions where the format string is not a string literal and there are no format arguments, as in printf (foo);. This may be a security hole if the format string came from untrusted input and contains `%n'. (This is currently a subset of what -Wformat-nonliteral warns about, but in future warnings may be added to -Wformat-security that are not included in -Wformat-nonliteral.) -Wformat=2

当您不传递任何参数时就是这种情况,因为 c_str() 的结果不是 字符串文字 .

本案例:

printf("null\n");

不会发出警告,因为 "null\n" 是一个不可能从用户输入的字符串文字。

我们可以从这个 .

看出为什么这是一个潜在的安全问题

如果您不想要全部 -Wformat-secrity:

,看来您必须打开特定的开关

-Wformat is included in -Wall. For more control over some aspects of format checking, the options -Wformat-y2k, -Wno-format-extra-args, -Wno-format-zero-length, -Wformat-nonliteral, -Wformat-security, and -Wformat=2 are available, but are not included in -Wall.

虽然这是一个糟糕的选择,如果 -Wformat-secrity 稍后添加更多选项,那么您需要不断更新。

AndyG 提到的另一种选择是重载:

void print(){
  std::printf("null\n");
}