do things like .tochararray add a null terminator

and can I always assume it will be there and what if the char buffer provided is shorter than the String to be copied?

and what happens if the String is shorter? does the \0 get put at the end of the last non null character or at the end of the allocated array? If it does the latter what is being padded in the slack space?

Because if I sort these character arrays I need to know how long they are given they may have a max size of 50, but only use 20.

Yes, I've searched for this question and no direct answers.

On a related note, what happens if \0 is a legitimate part of a message, say an integer you are trying to store in the array?

what if the char buffer provided is shorter than the String to be copied?

Then you can get a buffer overrun and really mess things up by writing to or reading from memory that you do not "own".

and what happens if the String is shorter?

All good. The space after the null ('\0') is still allocated but not relevant.

does the \0 get put at the end of the last non null character or at the end of the allocated array

Run the following code.
The sizeof() function tells how many bytes that the string was initialized with. The buffer array is declared with 32 bytes.
The strlen() function returns the number of characters in the string up to but not including the null.

String str = "some characters";

void setup()
{
   Serial.begin(115200);
   char buffer[32];
   str.toCharArray(buffer, 31);
   Serial.print(buffer);
   Serial.print(",  sizeof buffer = ");
   Serial.print(sizeof(buffer));
   Serial.print(",  length of buffer = ");
   Serial.println(strlen(buffer));
}

void loop()
{
}

I think you want String::c_str()

Note: If the string is empty, this may return NULL.

You CAN put a null character into a String, but only with String::setCharAt(unsigned int loc, char c). After that, if you use String::c_str() you will get a string that is terminated where you inserted the null.

One big source of confusion is using the word string for String and char array at the same time.

groundFungus:
Then you can get a buffer overrun and really mess things up by writing to or reading from memory that you do not "own".
All good. The space after the null ('\0') is still allocated but not relevant.
Run the following code.
The sizeof() function tells how many bytes that the string was initialized with. The buffer array is declared with 32 bytes.
The strlen() function returns the number of characters in the string up to and not including the null.

String str = "some characters";

void setup()
{
  Serial.begin(115200);
  char buffer[32];
  str.toCharArray(buffer, 31);
  Serial.print(buffer);
  Serial.print(",  sizeof buffer = ");
  Serial.print(sizeof(buffer));
  Serial.print(",  length of buffer = ");
  Serial.println(strlen(buffer));
}

void loop()
{
}

Thanks i will try that when i get back.

sevenoutpinball:
do things like .tochararray add a null terminator and can I always assume it will be there and what if the char buffer provided is shorter than the String to be copied?

I don't know about things 'like String::toCharArray()' but String::toCharArray() always forces a null terminator. If your array is too small for the whole String, the end of the String is cut off and the last character in your array is set to 0.

sevenoutpinball:
and what happens if the String is shorter? does the \0 get put at the end of the last non null character or at the end of the allocated array? If it does the latter what is being padded in the slack space?

The String keeps a null at the end of its internal buffer and that null is copied into your array if there is room for it.

Note: If the string is empty, this may return NULL.

No empty Arduino Strings return a null terminated char array of at least size 1, with char[0] = '\0'

groundFungus:
Then you can get a buffer overrun and really mess things up by writing to or reading from memory that you do not "own".
All good. The space after the null ('\0') is still allocated but not relevant.
R

So the space after doing the toCharArray is lost to antiquity. Or at least the next startup.

Another thing I notice in people's sketches is:

String xyz;
String abc="";

Are these the same?

char buffer[32];
str.toCharArray(buffer, 31);
So the space after doing the toCharArray is lost to antiquity. Or at least the next startup.

The space not used is not lost to antiquitiy: when buffer[] goes out of scope, it is returned to the stack. All 32 bytes, whatever the length of the data string that it contains and whatever the position of the terminating \0.
You can also re-use it by again executing strBis.toCharArray(buffer,31) but with another string of another length.
Best Regards,
Johi.

sevenoutpinball:
Another thing I notice in people's sketches is:

String xyz;
String abc="";

Are these the same?

I know that String xyz; and String abc(""); are equivalent because the default value for the constructor is "".

String(const char *cstr = "");

I suspect String abc=""; is the same as String abc= (String)""; which should produce the same effect. All will end up being zero-length Strings. It appears that String will allocate a buffer of size 1 and set the first byte to 0 if the previous length was 0.
Interesting note: It doesn't look like a String ever gets smaller during its lifetime. It only grows the buffer bigger if the buffer is ever not large enough. If the buffer is large enough it keeps the same size.

johnwasser:
Interesting note: It doesn't look like a String ever gets smaller during its lifetime. It only grows the buffer bigger if the buffer is ever not large enough. If the buffer is large enough it keeps the same size.

But it goes away as soon as the function that created it ends as johi mentioned right?

Yes, for a detailed tutorial on String life cycles and resizing, see my tutorial on Taming Arduino Strings

drmpf:
Yes, for a detailed tutorial on String life cycles and resizing, see my tutorial on Taming Arduino Strings

great stuff!! I put a routine that logs the event to thingspeak and restarts in of my programs these days. Can't hurt I figure.

if (ESP.getHeapFragmentation() > 25) {
RestartDevice(); a routine that logs the event to thingspeak and restarts in my programs these days.
}

I still don't know the procedure to place hex 00 in a byte that is not to be interpreted as a null termination.

sevenoutpinball:
I still don't know the procedure to place hex 00 in a byte that is not to be interpreted as a null termination.

A String is intended to hold a string and a string can't have a null character in the middle. You can put one in but you can't expect that any characters after that point to be considered part of the String. Sometimes it will work and sometimes it won't. I expect that putting a null in the middle of a String is undefined behavior and you can't expect the String to act any specific way after that.
If you want a dynamically allocated byte array, create an object for that.

but wait, I thought any character array had to have hex 00 at the end. For example in this code I am saving Strings in a char array so that I can put them into an html table and see what is going on from the http screen. How else when you do OTA updates.

You make it sound that only String needs hex 00 at the end. The documentation on this site says you have to have it and the compiler will add it implicitly.

void SerOut() {
  Serial.print(SerTxt); Serial.println(SerNbr);
  FastBlink = millis() + 17000;
  ReadTheClock("SerOut()");
  String GD = " ";
  GD += MD_RTChDST;
  GD += ":" + padWithZeroBelowTen(MD_RTC.m) + ":" + padWithZeroBelowTen(MD_RTC.s);
  GD += " " + SerTxt;
  if (AddNumbers) {
    AddNumbers = false;
    GD += " = ";
    char nbr[11] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
    itoa(SerNbr, nbr, 10);
    unsigned long nines = 999999999;
    int j = 0;
    for (int i = 0; i < 11; i++) {
      if (SerNbr >= nines) {
        GD += nbr[j];
        j++;
      }
      nines = nines / 10;
    }
    SerNbr = 0;
  }
  GD.toCharArray(serialChar, serialColumns);
  AddSerialToArray();
  SerTxt = "";
}
void AddSerialToArray() {
  //move all prior messages down one line.
  for (unsigned int i = serialRows - 1; i > 0; i--) {
    for (unsigned int j = 0; j < serialColumns; j++) {
      serialCharArray[i][j] = serialCharArray[i - 1][j];
    }
    serialCharArray[i][serialColumns - 1] = '\0';
  }
  //Add the new record
  unsigned int i;
  for (i = 0; i < serialColumns; i++) {
    if (isPrintable(serialChar[i])) {
      serialCharArray[0][i] = serialChar[i];
    } else {
      //Serial.print("unprintable "); Serial.println(i);
      serialCharArray[0][i] = '\0';
      break;
    }
  }
  if (serialCharArray[0][i] != '\0') {
    Serial.println("@@@no nullterm");
    serialCharArray[0][i] = '\0';
  }
}

sevenoutpinball:
but wait, I thought any character array had to have hex 00 at the end.

Only if you are going to use it as a string. If it contains binary data then you have to specify the number of characters. For example:

char string[20] = "TEST";

Serial.print(string);  // Sends 4 characters and stops at the 0.
Serial.print(string, sizeof string); // Send 20 characters, including 'T','E', 'S', 'T', 0 and the 15 characters after that
Serial.print(string, strlen(string)); // Sends 4 characters because that is the number of characters before the first 0

string[2] = 0;

Serial.print(string);  // Sends 2 characters and stops at the 0.
Serial.print(string, sizeof string); // Send 20 characters, including 'T', 'E', 0, 'T', 0 and the 15 characters after that
Serial.print(string, strlen(string)); // Sends 2 characters because that is the number of characters before the first 0

Ok, I'm going to have to look at that closely. But now I'm wondering what an array of unsigned ints would do, were they all 00? Does the compiler still add one extra byte?

Or is this a case of only when using String functions as with Serial.print does it matter. But the documentation says the compiler always adds a byte and the compiler doesn't know if you are using String functions. Interesting stuff. I think you should be writing the documentation for this stuff!!!

So sizeof never varies, but strlen does. I bet that has sunk more programs than can be counted by all the computers in all the credit cards!

Thank you very much for that.