Yes, sorry. I was trying to boil the question down to its simplest form and lost some information.
I'm not so much concerned with the compiler error (it's valid), just trying to find out if the foo() function is defined to receive a String and a char parameter, and I want to pass one character to it out of a text sequence such as
char keys[] = “123456789*0#”;
foo(keys[4]);
How would I go about converting keys[4] to something that foo() would accept?
The complete sketch is 598 lines and it's working using the parameter structure at the top of the original question.
All I'm trying to find out is, given that foo() expects a String parameter, what data conversion would need to be done to pass one character to it in the second example. It is a learning question about data conversion, not a programming issue.
and have foo() do something appropriate with the byte.
It is best to avoid using the String class and objects on Arduinos, especially AVR-based ones like the Uno, as Strings cause memory problems and program crashes.
you actually define an array of 12 Strings instances (each entry in the array is a String instance that could grow independently of each other. you could do keys[4] = "Hello World"; for example.)
if you call foo(keys[4]); and foo() is defined as
void foo(String s) {
Serial.println(s); // will print 5
}
that's OK since keys[4] is indeed a String (capital S, an instance).
What the function receives is actually a copy of the String that was in the array, if you modify it, you don't modify the String that's in the array.
if you define
you are defining an array of chars (each entry in the array is a char) and you left the compiler decide the size and offered as an initialisation list a cString which is null terminated. So keys actually has 13 bytes, the last one being the trailing null char you don't see but is part of the cString as per the C/C++ standard when you use the double quotes for a text (a string literal).
if you wanted only 12 characters, then you would need to do
char keys[12] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '0', '#'};
or char keys[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '0', '#'};
(note the single quotes for char, whereas double quotes are for cStrings)
then the initialisation is actually made of 12 individual chars and so that's the size of the array.
In both cases, keys[4] is actually just a char, it's not a String. When you call the foo function with keys[4] , which is a char , the compiler looks for a matching function signature that can accept a char as an argument. It does not find one but it finds that foo is defined to accept a String parameter, so it's trying to find an implicit conversion from char to String — but there are none and so it results in a compilation error.
You would need to call foo(String(keys[4])); and then the parameter is actually a String instance and you initialized this String through the String constructor which accepts a char.
alternatively, you could define a second version of the foo() function which signature would show it accepts a char as a parameter instead of a String as shown by @kenb4
depending on what microcontroller you use.
Using Strings can eat up all RAM and then start to overwrite other variables which leads to real strange code-behaviour that might only occur from time to time.
Which means it is very hard to debug.
Each time you do any kind of String-operation like
passing a parmater,
assigning a String to another String
adding some characters to a String,
another piece of RAM is occupied.
If your loop runs 10000 of times this leads to program-crashes.
So if ever possible don't use String (The one with the capital S). on small RAM-microcontrollers like Arduino Uno or Mega.
Ecxept you really know how to avoid total RAM-occupation cause by Strings
If you are free to choose a c_string (array of char with null-termination
or an array of char (without null-termination
I kinda agree that using cStrings is fine for most of what we do here (non nul terminated char arrays are not strings, just arrays)
Yet, there are some misconceptions in what you state.
No. worse case scenario is that the heap reaches the stack, which is trapped and you get a stack overflow error and crash. Otherwise what happens is that you run out of memory and the String operations just do not complete and you are not told so, so your code can't really take that into account.
you can pass parameter by reference so no copy
assigning a String to another String is indeed duplicating the memory — as for any other type (an int, a struct, ...). So not sure what's your point.
adding some characters to a String can be done somewhat more gently to the heap if you use += with the String class but of course you need more space to accommodate the extra character, as you would with a cString
if you use more memory than you have yes.
if your Strings are local variables, they are freed-up upon exiting the loop, so memory is claimed and unless you've done weird stuff with local and global variables and poked holes into the heap, it's not as bad as you make it sound since the memory is available again for the next round.
Title: Understanding char and String data types - data conversion
The title asks for dataconversion.
Depending on how and where the conversion is done this might lead to problems.
It will help to understand how to safely use Strings.
"estimations" do not work technical proof is what matters and understanding what is at play.
A String object does not shrink. So if you have a global instance that you want to duplicate through unoptimised code like aString = aString + "0123456789"; then you are asking for a temporary area that is as big as the aString variable and then expanding it with the operation's parameter before changing the back end of aString and freeing the original buffer which is returned to the heap. That's when things can "break" (operation will silently fail) if there is not a large enough free block in the heap to accommodate the operation.
Of coures, code fails too if your Strings grow beyond the memory limit, then sure you run out of heap space. That's like any other variable, you can't use more memory than you have.
But if what you do is grow (within memory limits) and discard the strings (local variables which get freed up) then the various tiny bits of heap areas that used to be disconnected become reunited as one big area that you can tap into again during the next loop.
Doing "weird stuffs" consists in allocating and not freeing a byte here and there to prevent the disconnected areas in the heap to become united again.
here is a visual representation of what happens in the heap as you grow a String
the allocation function keeps tracks of blocks and will reuse larger blocks as they come free/reunited, so it's not just eating up all the way to the end of memory and then get stuck.
but if you start poking holes (the green cells) with memory that does not get freed-up then you limit the ability of your String to expand as you prevent the reunion of the areas
On an AVR it's hard to lead to a crash as the String class just stops doing what's being ask (like concatenating strings etc) if there is not enough memory. The code won't crash because of the String, it will just misbehave as the expected operation won't have been done.
Thank you for the answer. I could have sworn I tried String(keys[4]) when I first saw the compiler error and it also failed compilation. But this String() conversion does work as it should.
The sketch runs on a MKR1000 and I had already changed to use char instead or String daa type when the issue first came up. But I wanted to learn how to deal with the function call data type conversion should the need arise again. And now I know - use String().