Передача пакета с нулевым аргументом в printf

Я создал класс с вариационным шаблоном. Этот метод вызывает функцию printf. При передаче нулевых аргументов методу, я получаю предупреждение компиляции gcc, говорящее:

warning: формат не строковый литерал и аргументы формата [-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...);
    }
};

При использовании

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?

Ответ 1

Это ожидаемое предупреждение, если мы посмотрим на документ для - Wformat-security, он говорит:

-Wformat-безопасности     Если задан -Wformat, также предупреждайте об использовании функций формата, которые представляют возможные проблемы безопасности. В настоящее время предупреждает о вызовах функций printf и scanf, где строка формата не является строковым литералом, и нет аргументов формата, как в printf (foo);. Это может быть отверстие безопасности, если строка формата поступает из ненадежного ввода и содержит `% n '. (В настоящее время это подмножество того, что предупреждает -Wformat-nonliteral, но в будущем предупреждения могут быть добавлены в -Wformat-security, которые не включены в -Wformat-nonliteral.) -Wformat = 2

когда вы не передаете никаких аргументов, так как результат c_str() не является строковым литералом.

Этот случай:

printf("null\n");

не предупреждает, потому что "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");
}