String to char array conversion

Hi, In a serial communication project my Arduino was reading wifi ssid and pass from sd card for ESP8266 and my NON WORKING code was like this:

String ssid = "my ssid sent over serial";
String pass = "same way";

  char cssid[ssid.length()];
  char cpass[pass.length()];
  ssid.toCharArray(cssid, ssid.length());
  pass.toCharArray(cpass, pass.length());
WiFi.begin(cssid, cpass);

I made guess about null-termination and by trial error found out that this code works:

  char cssid[ssid.length()+1];
  char cpass[pass.length()+1];
  ssid.toCharArray(cssid, ssid.length()+1);
  pass.toCharArray(cpass, pass.length()+1);
WiFi.begin(cssid, cpass);

Then I referred back to Arduino manual online to hopefully understand why the second one works and the secret behind that +1 but failed to make it clear.

Could anyone please explain what is happening there and why that +1 was needed please?

A well formed string in C needs to be null terminated so when you do char s[] = "HELLO" the array does not have 5 characters (the length of the HELLO String) but 6 because there is a hidden '\0' trailing character to denote the end of the string.

So when you build a char buffer to store your content, you need to ensure you have that extra space

You should get used to using char arrays and not the String class which uses lots of memory and can create also hard to debug memory issues at run time in some circumstances

A program needs some way to know where a string stops. After all - a string is just some byes in memory, there's nothing special about the bytes immediately after your string.

There are three ways to do this.

1 - fixed width. Whenever you work with a string, it's always a string of however many characters and the code that used the string is simply expected to know that. For instance, PIN numbers might be strings of four characters, no more and no less. Everything that works with PIN numbers is expected to know that.

Obviously, this won't work for anything that needs strings of varying length.

2 - a character count. Whenever you use strings that might vary in length, then a count must be passed as part of the string. This is how it used to be done in - for instance - Pascal. You can either hold a count as a separate parameter, or you might adopt a convention that the first byte of the string is actualy a count (limiting you to 255 characers).

C does supply quite a bit of support for these two methods by whay of the str*n*X functions - strncpy, strnlen, etc.

3 - a terminator character. This is the C method - strings have a trailing '\0', an ASCII NUL. Disadvantages of this are * strings can never have '\0' as a character in them * the only way to know how long a string is to scan it * you always have to make room for that trailing '\0'

Advantages are * more compact - you only ever need one extra byte * unlimited length * architecture independence * the C idiom for string copy: while(*p++ = *q++); compiles down to two machine-language instructions on the PDP-11, which is the machine that the guys who invented C were using.

C provides support for this third method in that * the libc functions that work with strings expect this, and * in the C language, string literals are turned into nul-terminated arrays of char by the compiler.

You might have read the incorrect reference page; https://www.arduino.cc/en/Reference/String has a section about the NULL termination.

Going on J-M-L's example, this how you can see the numbers that he gave.

char s[] = "HELLO";

void setup()
{
  Serial.begin(9600);
  Serial.print("Length of s = "); Serial.println(strlen(s));
  Serial.print("Size of s = "); Serial.println(sizeof(s));
}

void loop()
{
}

This will print 5 (the number of characters as you count them) and 6 (the actual size in memory and includes the terminating nul character).

You could have made your life a little easier by using the c_str method instead of the toCharArray ;)

  WiFi.begin(ssid.c_str(), pass.c_str());

Lastly you should not use String (capital S) as it can cause unexpected behaviour (running out of memory); you can define a buffer of sufficient size and apply te principles in the Serial Input Basics - updated thread; the principles do not only apply to the serial port but also to a file on SD card or data received from a network card.

You could have made your life a little easier by using the c_str method instead of the toCharArray

The c_str() method returns a pointer to the string embedded in the String instance.

The toCharArray() makes a copy of the string embedded in the String instance.

Depending on what you want to do with the data, the pointer may, or may not, be appropriate.

Parsing the string, using the copy, will leave the original unmolested. Parsing using the pointer will not.

Of course, there was no reason for OP to have wrapped the string in a String in the first place.

By the looks of it, it would do the trick ;)

Special thanks to everyone! I posted a question, expecting a line of response to be happy, and I got a chapter of a programming book and I learned how to program over char/String in general!

One new question arrived: what is this character '\0' there? I understand it as 2 characters of '\' and '0', but that is a single byte there right? i.e. the compiler turns this '\0' to a byte, as long as I understand, But what that byte is please?

what is this character '\0' there? I understand it as 2 characters of '\' and '0',

It is NOT two characters. See the single quotes? They denote a SINGLE character. See the backslash? That is the escape character that says, to the compiler, that the one following it has special meaning? The special meaning is NULL (or STOP).

Thanks PaulS, it's clear now.