Я использую встроенную металлическую систему с ARM Cortex-M3 (STM32F205). Когда я пытаюсь использовать snprintf()
с числами с плавающей запятой, например:
float f;
f = 1.23;
snprintf(s, 20, "%5.2f", f);
Я забиваю мусор в s
. Формат, похоже, соблюдается, т.е. мусор представляет собой правильно сформированную строку с цифрами, десятичной точкой и двумя конечными цифрами. Однако, если я повторю snprintf
, строка может измениться между двумя вызовами.
Математика с плавающей запятой, похоже, работает иначе, а snprintf
работает с целыми числами, например:
snprintf(s, 20, "%10d", 1234567);
Я использую реализацию newlib-nano
с переключателем компоновщика -u _printf_float
. Компилятор arm-none-eabi-gcc
.
У меня есть сильное подозрение на проблемы с распределением памяти, поскольку целые числа печатаются без каких-либо сбоев, а числа с плавающей запятой действуют так, как будто они были повреждены в процессе. Функции семейства printf
вызывают malloc
с плавающей запятой, а не с целыми числами.
Единственный фрагмент кода, не принадлежащий newlib
, который я использую в этом контексте, - это мой _sbrk()
, который требуется malloc
.
caddr_t _sbrk(int incr)
{
extern char _Heap_Begin; // Defined by the linker.
extern char _Heap_Limit; // Defined by the linker.
static char* current_heap_end;
char* current_block_address;
// first allocation
if (current_heap_end == 0)
current_heap_end = &_Heap_Begin;
current_block_address = current_heap_end;
// increment and align to 4-octet border
incr = (incr + 3) & (~3);
current_heap_end += incr;
// Overflow?
if (current_heap_end > &_Heap_Limit)
{
errno = ENOMEM;
current_heap_end = current_block_address;
return (caddr_t) - 1;
}
return (caddr_t)current_block_address;
}
Насколько мне удалось отследить, это должно сработать. Кажется, никто никогда не называет это с отрицательным приращением, но я предполагаю, что это связано с дизайном новой библиотеки malloc
. Единственное, что немного странно, это то, что первый вызов _sbrk
имеет нулевое приращение. (Но это может быть просто любопытством malloc
по поводу начального адреса кучи.)
Стек не должен сталкиваться с кучей, так как на них приходится около 60 КБ ОЗУ. Сценарий компоновщика может показаться безумным, но, по крайней мере, адреса кучи и стека кажутся правильными.
double
s, а неfloat
s. Понятия не имею, имеет ли это значение, вы все равно не можете передатьfloat
snprintf()
. - person unwind   schedule 26.02.2015snprintf()
являетсяint snprintf(char *restrict s, size_t n, const char *restrict format, ...);
Вы вызываете функцию не в соответствии с к прототипу. Вы#include <stdio.h>
скомпилировали со всеми включенными предупреждениями? - person pmg   schedule 26.02.2015snprintf()
, а неsprintf()
? - person chux - Reinstate Monica   schedule 26.02.2015printf
- это функция с переменным числом аргументов, автоматически повышающая число с плавающей запятой до удвоения. (По крайней мере,gcc
не жалуется на форматы с его педантичными настройками.) В моем исходном примере (буквальная константа1.23
) аргумент в любом случае является двойным, но в исправленном примере это число с плавающей точкой одинарной точности. Я не уверен насчет внутренностейnewlib nano
, я бы предположил, что он скорее сохраняет float как float, потому что двойники довольно трудоемки во встроенных системах (но это только предположение). - person DrV   schedule 26.02.2015printf
во встроенной системе, у меня достаточно производительности, чтобы считать символы, чтобы избежать каких-либо глупых происшествий. ИМХО,sprintf
не должно существовать; это просто опасно. - person DrV   schedule 26.02.2015snprintf(s, "%.2f", 1.23)
, и я подозревал, что вместо того, чтобы не получать / игнорировать предупреждения компилятора, произошла ошибка вырезания / вставки кода. - person chux - Reinstate Monica   schedule 26.02.2015-pedantic
. Я был вынужден отказаться от-Werror
, поскольку я использую STM32 HAL, который, к сожалению, вызывает ужасное количество предупреждений о заполнении и преобразовании. - person DrV   schedule 26.02.2015%f
обычно включает преобразованиеfloat
вdouble
; EABI требует 8-байтового выравнивания дляdouble
; ваш_sbrk()
обеспечивает 4-байтовое выравнивание только того, что выдает. Насколько это важно, вероятно, зависит от чутьяprintf()
иmalloc()
, но экспериментировать с этим должно быть несложно. - person Notlikethat   schedule 27.02.2015-u _printf_float
? Я пытаюсь скомпилировать часть размером 16 КБ (STM32F030F4P6), но двоичный файл кажется слишком большим (около 20 КБ). - person Christoph   schedule 18.06.2015