Я столкнулся с проблемой форматирования в своем коде. Вот проблема:
#include <iostream>
#include <cstdio>
#include <cstdarg>
void PP_p_VSPF_vsprintf(char *buf, size_t max_size, const char *fmt, va_list args )
{
const int stat = vsnprintf(buf, max_size, fmt, args);
if (stat >= static_cast<int>(max_size)) // not enough room in buffer
{
buf[max_size - 1] = '\0';
}
else if (stat < 0)
{
buf[max_size - 1] = '\0';
}
}
void PP_p_SPF_sprintf(char *buf, size_t max_size, const char *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
PP_p_VSPF_vsprintf( buf, max_size, fmt, argptr );
va_end( argptr );
}
int main() {
char ExpCrcData[100] = "";
unsigned int crc32Values[] = {0xe870cde0u, 0x1ec5adaeu, 0xcb43101bu};
for (int i = 0; i < 3; ++i) {
PP_p_SPF_sprintf(ExpCrcData, 100, "%s0x%08x ", ExpCrcData, crc32Values[i]);
std::cout << "Download admin string is now: " << ExpCrcData << std::endl;
}
return 0;
}
Используя компилятор GNU,
В Windows я получаю вывод:
Загрузить административную строку сейчас: 0xe870cde0
Загрузить административную строку сейчас: 0xe870cde0 0x1ec5adae
Загрузить административную строку сейчас: 0xe870cde0 0x1ec5adae 0xcb43101b
В Linux я получаю вывод:
Загрузить административную строку сейчас: 0xe870cde0
Загрузка административной строки сейчас: 0x1ec5adae
Загрузка административной строки сейчас: 0xcb43101b.
Может ли кто-нибудь объяснить причину этой разницы?
Кроме того: else if (stat < 0) { buf[max_size - 1] = '\0';
имеет больше смысла, чем else if (stat < 0) { buf[0] = '\0';
. Если в коде возникает ошибка кодировки, лучше всего вернуть ""
.
https://linux.die.net/man/3/sprintf говорит:
C99 и POSIX.1-2001 указывают, что результаты не определены, если вызов sprintf(), snprintf(), vsprintf() или vsnprintf() приведет к копирование будет происходить между объектами, которые перекрываются (например, если массив целевых строк и один из предоставленных входных аргументов относятся к тот же буфер).
Поскольку вопрос помечен как C и C++, здесь цитата C++:
https://en.cppreference.com/w/cpp/io/c/fprintf говорит:
Если вызов sprintf или snprintf вызывает копирование между объекты, которые перекрываются, поведение не определено (например,
sprintf(buf, "%s text", buf);
).
buf
и буфер, на который указывает аргумент переменной, соответствующий спецификатору %s
, перекрываются, что является неопределенным поведением.
Поэтому
PP_p_SPF_sprintf(ExpCrcData, 100, "%s0x%08x ", ExpCrcData, crc32Values[i]);
должно быть
PP_p_SPF_sprintf(ExpCrcData+strlen(ExpCrcData), 100-strlen(ExpCrcData), "0x%08x ", crc32Values[i]);
Другая возможность — отслеживать, где вы находитесь в буфере и что осталось.
buf
и format
не пересекаются. buf
и буфер, на который указывает аргумент переменной, соответствующий спецификатору %s
, перекрываются.
@IanAbbott да, конечно. Я изменил текст. Спасибо.
@mch Можно ли внести некоторые изменения в саму функцию PP_p_VSPF_vsprintf, поскольку это решило бы проблемы для всех случаев появления PP_p_SPF_sprintf в моем коде. Из-за некоторых ограничений изменение на высоком уровне невозможно (присутствует более 500 вхождений!!).
@PiyushKumarGupta Только в этом случае buf
и первый аргумент переменной равны? Убедитесь, что строка формата начинается с %s
, получите первый аргумент переменной и сравните с buf
. Если да, сделайте buf +=strlen(buf); fmt +=2;
и используйте argptr
, где вы уже извлекли первый элемент.
@mch Я просто хотел найти общее решение этой проблемы. Многие модули кода используют эту функцию в разных аспектах. Их сложно анализировать, поэтому, если вы предложите альтернативную функцию для vsnprintf в Linux, это тоже нормально, или некоторые настройки в PP_p_VSPF_vsprintf или PP_p_SPF_sprintf также будут высоко оценены.
Привет! Пожалуйста, не отмечайте [c] для кода [c++]