将零参数包传递给printf

zti*_*tik 10 c++ gcc variadic

我创建了一个具有可变参数模板方法的类.这个方法调用printf函数.将零参数传递给方法时,我得到gcc的编译警告说:

警告:格式不是字符串文字而没有格式参数[-Wformat-security]

一个简化的类示例是:

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...);
    }
};
Run Code Online (Sandbox Code Playgroud)

使用时

printer p;
p.print(23);
p.print(345,23);
Run Code Online (Sandbox Code Playgroud)

一切顺利,但使用时

printer p;
p.print();
Run Code Online (Sandbox Code Playgroud)

我收到编译警告

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...);
Run Code Online (Sandbox Code Playgroud)

当然,如果我只是打电话

printf("null\n");
Run Code Online (Sandbox Code Playgroud)

没有警告出现.

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

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

Sha*_*our 12

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

-Wformat-security如果指定了-Wformat,还会警告使用表示可能存在安全问题的格式函数.目前,这会警告调用printf和scanf函数,其中格式字符串不是字符串文字,并且没有格式参数,如printf(foo); .如果格式字符串来自不受信任的输入并包含"%n",则这可能是一个安全漏洞.(这是-Wformat-nonliteral警告的子集,但在将来的警告中可能会添加到-Wformat-nonliteral中未包含的-Wformat-security.) - Wformat = 2

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

这个案例:

printf("null\n");
Run Code Online (Sandbox Code Playgroud)

没有警告,因为"null\n"字符串文字不可能从用户输入.

我们可以看到为什么这是一个潜在的安全问题,这个%n格式说明符程序在不同的编译器上给出不同的输出.为什么?.

如果你不想要所有的话,看起来你必须打开特定的开关-Wformat-secrity:

-Wformat包含在-Wall中.为了更好地控制格式检查的某些方面,选项-Wformat-y2k,-Wno-format-extra-args,-Wno-format-zero-length,-Wformat-nonliteral,-Wformat-security和-Wformat = 2是可用的,但不包括在-Wall中.

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

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

void print(){
  std::printf("null\n");
}
Run Code Online (Sandbox Code Playgroud)