Hi there,
I'm working on an Arduino project for an Arduino Uno. The projects consists of several classes with different functions.
I'm nearly going crazy creating a simple string out of an char[] within a method (function). Whatever I try, nothing works. I tried the same thing in a simple .ino where everything is ok.
So here is what I have:
Extract from Commandinterpreter.h
Here I declare a struct "CommandLineQueueRecord" and a queue "CommandLineInputQueue" which is used as a record to a queue (Library cppQueue)
Furthermore, in CommandInterpreter.cpp, there's a function "getCommandLine()" that basically should retrieve one commandline from the queue.
I can retrieve a record from the queue and I can also prove, that the content is correct, but I simply fail to put the char[] from the retrieved record to a String. Here's the code of the function:
// ---------------------------------------------
// Tries to retrieve a new command
// ---------------------------------------------
String CommandInterpreter::getCommandLine() {
CommandLineQueueRecord commandRec;
char ch;
String s1;
String s2;
if (CommandLineInputQueue->getCount() > 0) {
CommandLineInputQueue->pop(&commandRec);
Serial.print("cmdLine=");
Serial.println(commandRec.commandLine);
s1 = String(commandRec.commandLine);
Serial.print("s1=");
Serial.println(s1);
// Copy the commandline into a string. All the variants found on the internet do not work! Grrrrrrrrr :(
// Like: String s = String(commandRec.commandLine);
// So I will try it manually ==> Also no success
byte idx = 0;
s2 = "";
while (commandRec.commandLine[idx] != 0 && idx <= MaxCommandLineLenght) {
ch = commandRec.commandLine[idx];
Serial.print(". ");
Serial.println(ch);
s2 = s2 + ch;
Serial.print("s2=");
Serial.println(s2);
idx++;
}
}
else {
s1 = "";
}
return s1;
}
When I run it, we can see, that the content of the record from the code is correct, here "abcd", but then, all attempts to copy it into a string fail, whatever I try:
Output of the console:
cmdLine=abcd
s1=
. a
s2=
. b
s2=
. c
s2=
. d
s2=
Does anybody has a clue, what's going on? Grrr, I am going crazy
Avoid using String objects with AVR processors like the Uno. They are never necessary and furthermore, lead to memory problems and program crashes.
Stick with C-strings (zero terminated character arrays) and the associated helper functions like strcpy, strcat, etc. (better, use the n versions, strncpy, strncat).
For any other dev board yes, but the arduino does not handle Strings properly. You also need to reserve space for Strings, so s2 = ""; is only big enough for a single character.
I didn't know all that but indeed had difficulties in the past with unstable programs. Now I understand, why memory management isn't that easy with only a few bytes of memory. As a experienced C# software developer I just thought, that nowadays String handling shouldn't be such a problem anymore and that compilers etc. would handle this correctly.
What I will do know is looking for my old C++ Books in the shelf and refactor my Arduino programs from String to the old fashioned C-Strings.
Again, thanks a lot for all the links and help
Michael
C style strings residing on the stack but with C++ expressions. They can not be expanded over predefined size though. But they don't mess with the heap at all.
This "just" is to learn a big heap about the traps of out of boundary writing
which means do a lot of boundary checking to really make sure that you do not crash your code
The more convenient way to use "c_strings" without all the make sure you only store withing boundaries is to use SafeStrings
// SafeStrings are based on array of chars
// SafeStrings offer almost the same comfort as Strings
// but avoid some dis-advantages of variable-type String
// the name SafeString is PROGRAM They are safe to use
// with the alternatives "String" must not always but CAN
// eat up memory over time which will make the code crash
// with zero-terminated array of chars (c_string)
// you have to take care of boundary checking yourself
// otherwise your code will start to behave weird and this kind of bug is very hard to find
// you can read more about this here https://www.forward.com.au/pfod/ArduinoProgramming/ArduinoStrings/index.html#safestring
// and here https://hackingmajenkoblog.wordpress.com/2016/02/04/the-evils-of-arduino-strings/
// very basic demo-code to show how to declare a SafeString-variable
// and how to assign text to them
// as a personal convention I use the suffix "_SS" to indicate
// THIS IS A SAFESTRING-VARIABLE
// but you can name it whatever you like
#include "SafeString.h"
createSafeString(myTitle_SS, 64); // reserve 64 bytes for the SafeString-variable
createSafeString(myString_SS, 32); // reserve 32 bytes for the SafeString-variable
int myInteger = -1234;
float myFloat = -987.009;
void setup() {
Serial.begin(115200);
Serial.println( F("Setup-Start") );
Serial.println();
myString_SS = F("fixed text directly assigned");
Serial.print( F(" #") ); // leading double-cross "#" to show where the string starts
Serial.print(myString_SS);
Serial.println(F("#") ); // trailing double-cross "#" to show where the string REALLY ends
myTitle_SS = F("content of an integer:");
myString_SS = myInteger;
Serial.println(myTitle_SS);
Serial.print( F(" #" )); // leading double-cross "#" to show where the string starts
Serial.print(myString_SS);
Serial.println( F("#") ); // trailing double-cross "#" to show where the string REALLY ends
myTitle_SS = F("content of a float:");
myString_SS = myFloat;
Serial.println(myTitle_SS);
Serial.print( F(" #" )); // leading double-cross "#" to show where the string starts
Serial.print(myString_SS);
Serial.println( F("#") ); // trailing double-cross "#" to show where the string REALLY ends
myTitle_SS = F("you can append more text with the +=-operator ");
myString_SS = F("text ");
myString_SS += myInteger;
myString_SS += F(" ,");
myString_SS += myFloat;
Serial.println(myTitle_SS);
Serial.print( F(" #" )); // leading double-cross "#" to show where the string starts
Serial.print(myString_SS);
Serial.println( F("#") ); // trailing double-cross "#" to show where the string REALLY ends
}
void loop() {
}
Of course. This is just my opinion, I do like C style strings a lot, since you can always tell what is going on with them. But it is very easy to overrun the buffer if you don't pay enough attention and these kinds of bugs are rather difficult to find. Besides, they are inconvenient to use with complicated string expressions.
There are also some tricks that you can use with std::strings or Arduino Strings to reduce heap fragmentation. Like avoiding long living strings with a lot of changes, reserving buffer memory in advance, ....
I also prefer using concat member function, which actually check the success rather than += operator or even + itself (like s = s + "a"). So your code knows when it can not perform the calculation.
Well indeed they won't overflow but if they were to overflow in the regular c-string based code, they would as well there ➜ probably meaning that your code won't do what you want.
do even if you use the so called SafeStrings, you do need to test for "a lot of boundary checking" anyway, just using a different API.
What do you think is better in terms of coding :
try to perform an operation and test to see if it failed
check if the operation is possible and if so do it
also if you plan to do anything with C/C++ programming for microcontrollers, understanding how to manage arrays and bounds in general (not just for c-strings) is really something you need to know about. So I'd recommend to start there the journey.
if you want a throw away code for a school assignment and it has to run for 3 minutes during your show and tell, then the String class is likely all you need — no need to explore the complexities of the so called SafeStrings API...
I don't know because I do not understand what you want to say with that.
The maximum that can happen with a SafeString is that assigning a string with too many characters gets truncated
Which of course can cause a malfunction of the code
But it will be much easier to debug by printing the variables content
than chasing down the field of rabbit-holes because writing to an array of char outside of its boundaries which can cause ANY kind of strange behaviour you can not predict.