Использование snprintf в кроссплатформенном приложении - PullRequest
9 голосов
/ 20 октября 2010

Я пишу программу на C, которая должна быть скомпилирована со всеми основными компиляторами.В настоящее время я занимаюсь разработкой GCC на машине с Linux и скомпилирую на MSVC перед фиксацией кода.Чтобы сделать кросс-компиляцию легкой, я компилирую с флагами -ansi и -pedantic.Это работало хорошо, пока я не начал использовать snprintf, который не доступен в стандарте C89.GCC может скомпилировать это без ключа -ansi, но MSVC всегда будет зависать, поскольку не поддерживает C99.

Итак, я сделал что-то вроде

#ifdef WIN32 
#define snprintf sprintf_s
#endif

Это хорошо работает, потому что snprintf и sprintf_s имеют одинаковые подписи.Мне интересно, это правильный подход?

Ответы [ 5 ]

16 голосов
/ 20 октября 2010

Я обнаружил , это при использовании _snprintf() в качестве альтернативы, а также ошибки, возникающие при срабатывании защиты от переполнения буфера. Из того, что я мог быстро увидеть, подобные предостережения относятся к sprintf_s.

Вы видите проблему? В версии для Linux вывод всегда завершается нулем. В MSVC это не так.

Еще более тонкой является разница между параметром size в Linux и параметром count в MSVC. Первый - это размер выходного буфера, включая завершающий ноль, а второй - максимальное количество символов для хранения, которое исключает завершающий ноль.

О, и не забудьте отправить письмо в Microsoft с требованием, чтобы они поддержали текущие языковые стандарты. (Я знаю, что они уже объявили, что не планируют поддерживать C99, но, в любом случае, им это надо. Они этого заслуживают.)

Суть в том, что если вы хотите играть в нее по-настоящему безопасно, вам придется предоставить свой собственный snprintf() (обертка вокруг _snprintf() или sprintf_s(), фиксирующий их нестандартное поведение) для MSVC.

13 голосов
/ 21 февраля 2014

Ваше предложение может сработать, если вы будете осторожны.Проблема в том, что обе функции ведут себя немного по-разному, если это не проблема для вас, вы можете пойти, иначе подумайте о функции-оболочке:

Различия между MSVC _snprintf и официальным C99 (gcc,clang) snprintf:

Возвращаемое значение:

  • MSVC: вернуть -1, если размера буфера недостаточно для записи всего (не включая завершающий ноль!)
  • GCC: вернуть количество символов, которое было бы записано, если бы буфер был достаточно большим

Записанные байты:

  • MSVC: записать как можно больше, не писать NULL в концеесли свободного места не осталось
  • GCC: писать как можно больше, всегда писать завершающий NULL (исключение: buffer_size = 0)

Интересная %n тонкость: Если вы используете %nв вашем коде MSVC оставит его унифицированным!если он прекратит синтаксический анализ, поскольку размер буфера слишком мал, GCC всегда будет записывать количество байтов, которое было бы записано, если бы буфер был достаточно большим.

Поэтому я предлагаю написать свою собственную функцию-обертку mysnprintf с использованием vsnprintf / _vsnprintf, который выдает одинаковые возвращаемые значения и записывает одинаковые байты на обеих платформах (будьте осторожны: %n труднее исправить).

1 голос
/ 14 июня 2016

Вы можете открыть специальный файл NUL для MSVC и записать в него.Он всегда скажет вам, сколько байтов необходимо, и не будет записывать что-либо.Например:

int main (int argc, char* argv[]) { 
  FILE* outfile = fopen("nul", "wb");
  int written;

  if(outfile == NULL) {
    fputs ("could not open 'nul'", stderr);
  }
  else {
    written = fprintf(outfile, "redirect to /dev/null");
    fclose(outfile);
    fprintf(stdout, "didn't write %d characters", written);
  }

  return 0;
}

Затем вы должны знать, сколько байтов выделено для успешного использования sprintf.

0 голосов
/ 21 мая 2013

самый полный ответ (вы можете улучшить его, если хотите), поместите его в стикер

0 голосов
/ 20 октября 2010

Нет. Ваш подход обречен на неудачу.

sqrt и cos имеют один и тот же прототип.Как вы думаете, вы можете поменять их в программе и получить такое же поведение до / после изменения?


Вы, вероятно, должны написать свой snprintf или загрузить реализацию из Интернета ( Google - ваш друг ) и используйте его как в Linux, так и в Windows.

...