我在Uno克隆上运行以下程序。
uint32_t counter = 0;
void setup() {
Serial.begin(9600);
while(!Serial);
}
void loop() {
char *result = malloc(32);
sprintf(result, "%024u", counter);
Serial.print("{\"n\": ");
Serial.print(result);
Serial.println(" }");
delay(100);
counter++;
}
一切正常,字符串已打印,计数器按预期增加。下面是输出示例:
{"n": 000000000000000000000000 }
{"n": 000000000000000000000001 }
{"n": 000000000000000000000002 }
{"n": 000000000000000000000003 }
....
但是,每次counter > 45
时,程序都会停止通过UART打印,这是此时的输出结果:
{"n": 000000000000000000000044 }
{"n": 000000000000000000000045 }
{"n": }
对于可能导致此问题的原因的任何见解,都将受到赞赏。
答案 0 :(得分:4)
每次调用loop
时,都会分配32个字节的内存。 Arduino Uno没有太多内存,因此很快就会用完。使用完毕后,您需要释放内存。这是一个比喻。
RAM是一条字符串。此字符串的长度是固定的。您可以切断该字符串的一部分,然后将其用于任何您喜欢的对象。这是分配。如果继续从字符串中删减32个字节,则最终用完。使用完毕后,您需要将这条弦带绑回到主弦上。这是释放。
这是一个非常糟糕的类比,但我希望您能想到!要解决紧迫的问题,您只需在函数的末尾附加free(result)
即可在每次分配时取消分配。为了真正正确地解决问题,您应该在堆栈上分配而不是在堆上分配。在堆栈上进行分配时,内存将在作用域末尾自动释放。要在堆栈上分配32个字节,只需执行以下操作:
char result[32];
result
中的内存仅在其包围范围内有效(loop
函数)。上面的代码段基本上与此等效:
char result_0;
char result_1;
char result_2;
// ...
char result_29;
char result_30;
char result_31;
堆栈分配比堆分配(malloc
和free
)要快得多,因为编译器确切知道您正在使用多少内存。但这有其缺点。为了堆栈分配内存,编译器需要知道您正在使用多少内存!您不能这样做:
int size = get_a_number_from_somewhere();
char result[size];
因为编译器不知道要分配多少内存。但是,这是
int size = get_a_number_from_somewhere();
char *result = malloc(size);
// ...
free(result);
完全可以。
通常,当所需的内存量是编译时常量(例如32
)时,应进行堆栈分配,否则应进行堆分配。每次您写malloc
时,请仔细考虑相应的free
应该去哪里。
答案 1 :(得分:1)
允许使用malloc(),但在内存非常有限的Arduino上不是很有用。
Arduino没有操作系统,如果没有足够的内存分配,这可能会造成干扰。 (无论如何,要使Arduino永远运行,该怎么做?)
如果您有Java背景,也请避免使用new
关键字,这将导致相同的问题。
如果需要内存,请尽早获取(最好在编译时)并保留它。
当然,free(result)
响应也是正确的。
答案 2 :(得分:0)
为什么不使用String()
,例如下面的代码:
uint32_t counter = 0;
void setup() {
Serial.begin(9600);
while(!Serial);
}
void loop() {
Serial.print("{\"n\": ");
Serial.print(String(counter));
Serial.println(" }");
delay(100);
counter++;
}