【发布时间】:2015-02-26 15:15:15
【问题描述】:
我正在运行一个带有 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;
}
据我所知,这应该可行。似乎没有人用负增量调用它,但我想这是由于 newlib malloc 的设计。唯一有点奇怪的是,第一次调用_sbrk 的增量为零。 (不过这可能只是malloc对堆起始地址的好奇。)
堆栈不应与堆发生冲突,因为两者大约有 60 KiB RAM。链接描述文件可能很疯狂,但至少堆和堆栈地址似乎是正确的。
【问题讨论】:
-
请注意,这些是
doubles,而不是floats。不知道这是否重要,无论如何您都不能将float传递给snprintf()。 -
snprintf()的原型是int snprintf(char *restrict s, size_t n, const char *restrict format, ...);你没有根据原型调用函数。您是否#include <stdio.h>编译时启用了所有警告? -
确定您的真实代码使用的是
snprintf()而不是sprintf()? -
@unwind:很好,但 AFAIK
printf是一个可变参数函数,自动将浮点数提升为双精度数。 (至少gcc不会抱怨带有迂腐设置的格式。)对于我的原始示例(字面常量1.23),参数无论如何都是双精度的,但在修改后的示例中它是单精度浮点数。我不确定newlib nano的内部结构,我猜它宁愿将浮点数保持为浮点数,因为双精度在嵌入式系统中非常费力(但这只是一个猜测)。 -
我对实现细节不太熟悉,但以下引起了一些轻微的怀疑:
%f通常涉及将float转换为double; EABI 要求double使用 8 字节对齐;您的_sbrk()仅对其分发的内容强制执行 4 字节对齐。这有多重要可能取决于printf()和malloc()的胆量,但应该可以直接进行试验。
标签: c arm embedded printf newlib