Character / String concatenation

Hi all,

I am fairly new to Arduino and I am currently trying to do the simplest of things (in Java), ie: String concatenation. At first when everything was a String, life was good but since everything requires a pointer to a character, and since these values do not change in my case, I thought I would just declare them as char* but I must be missing something.

//clientId is ok here
const char* clientId = "8ad7a149-f2cd-446c-b013-7483ca424e63";

//but trying to concatenate clientId with other Strings like this, is not working.
const char* connectTopic = ("device/" + String(clientId) + "/connect").c_str();

I am clueless as to how you can concatenate everything as a char* instead of playing around with Strings and than using c_str()... and even what I pasted isn't working

Thanks
X

Use strncat() or strcat().

I have been told that Arduino String objects tend to result in heap fragmentation if you have a large sketch, use a lot of strings and your sketch runs over a period of days or weeks or more.

If these don't apply to you then just use the Arduino String class.

If you use strcat etc then this might help.

char *strcat(char *strDestinationString, char *strStringToAppend)

If is your responsibility to ensure that strDestinationString is big enough to receive the additional characters from strStringToAppend.

If not you will bet a memory overrun and your sketch will crash the the arduino will probably reset.

You also might consider the attached string class I have created.

You use it like so:

CBuff<20> buff;
CString str(buff);

CBuff is a template class for which you specify a character loength.
It contains a char array that is NOT created via new char[20].
Instead the char array is created as char m_strBuff[20].

The CBuff constructor also uses memset to intialise all elements of the array to 0 so that by default it is an empty string length 0.

CString contains the convenient Arduino String class functions but I have added a heap of other convenient functions modeled on those class CString in Microsoft's Foundation Classes (MFC).

You can put my CString class in an Arduino library if you wish when you get up to this level, but I have not bothered. I just copy CString.cpp/h into each Arduin project that I want to use the class in.

CString.cpp (21.2 KB)

CString.h (8.24 KB)

Hey, thanks for your time to reply. I have solved it as follows. My only remaining question is how efficient is this?

// I will use the prefix more than once, hence why its on its own line
const size_t prefix_size = sizeof("device/");

char* serveTopic    = new char[prefix_size + strlen(clientId) + sizeof("/serve")];
char* connectTopic  = new char[prefix_size + strlen(clientId) + sizeof("/connect")];


//On setup I call this method
void generateMqttTopics(){
  sprintf(connectTopic, "device/%s/connect", clientId);
  sprintf(serveTopic,   "device/%s/serve",   clientId);
}

Truth to be told...Efficiency is pretty poor :slight_smile: (because bringing in the whole code for sprintf is really heavy for the small task you have - just use strcpy() and strcat())

You could also easily be missing the 1 byte at the end of your arrays for à correctly formatted c-String (the ‘\0’) if you were not (by chance?) doing a sizeof("/serve") instead of a strlen() as you do for the clientID. Indeed luckily for you, sizeof() will take into account the space for the ‘\0’ and thus lead to the correct result for you. (actually on second thought given you use sizeof() as well for prefix_size, you end up with one extra char memory allocation than really needed)

Also often there is no need to concatenate the stuff, just print/send it out in chunks:

Serial.print(“Hello “);
Serial.println(“World“);

Will lead to the same data being transmitted as Serial.println(“Hello World“);and you did not need the extra memory to have “Hello World” on top of the existing ones (and if those are constant you can safely keep them in flash memory with PROGMEM)

boylesg:
I have been told that Arduino String objects tend to result in heap fragmentation if you have a large sketch, use a lot of strings and your sketch runs over a period of days or weeks or more.
If these don't apply to you then just use the Arduino String class..

You did not get it... what leads to heap fragmentation is allocating and freeing in various orders String (which happens a lot when you concatenate). it’s not uniquely related to how large or small your code is, or running over long period...sure if you start from less memory available and you run for a long time it does not help, but this it’s really related to how often do you allocate/free and in which order... remember your arduino can do thousands of things per second...

To reinforce what others have said, your sprintf() version uses 4014 bytes of flash and 242 of SRAM. This version, which gets rid of the bloated call to sprintf():

const size_t prefix_size = sizeof("device/");
char clientId[] = "myID/";
//char* serveTopic    = new char[prefix_size + strlen(clientId) + sizeof("/serve")];
//char* connectTopic  = new char[prefix_size + strlen(clientId) + sizeof("/connect")];

char serveTopic[prefix_size + sizeof(clientId) + sizeof("/serve")];
char connectTopic[prefix_size + sizeof(clientId) + sizeof("/connect")];


void setup() {

  Serial.begin(9600);
  generateMqttTopics();
  Serial.println(serveTopic);
  Serial.println(connectTopic);
}

void generateMqttTopics(){
//  sprintf(connectTopic, "device/%s/connect", clientId);
//  sprintf(serveTopic,   "device/%s/serve",   clientId);
  strcpy(serveTopic, "device/");
  strcat(serveTopic, clientId);
  strcat(serveTopic, "/serve");
  
  strcpy(connectTopic, "device/");
  strcat(connectTopic, clientId);
  strcat(connectTopic, "/connect");
}

void loop() {
}

uses 1958 bytes of flash and 262 bytes of SRAM, plus you don't have to worry about memory fragmentation.

econjack:
To reinforce what others have said, your sprintf() version uses 4014 bytes of flash and 242 of SRAM. This version, which gets rid of the bloated call to sprintf():

const size_t prefix_size = sizeof("device/");

char clientId[] = "myID/";
//char* serveTopic    = new char[prefix_size + strlen(clientId) + sizeof("/serve")];
//char* connectTopic  = new char[prefix_size + strlen(clientId) + sizeof("/connect")];

char serveTopic[prefix_size + sizeof(clientId) + sizeof("/serve")];
char connectTopic[prefix_size + sizeof(clientId) + sizeof("/connect")];

void setup() {

Serial.begin(9600);
  generateMqttTopics();
  Serial.println(serveTopic);
  Serial.println(connectTopic);
}

void generateMqttTopics(){
//  sprintf(connectTopic, "device/%s/connect", clientId);
//  sprintf(serveTopic,  "device/%s/serve",  clientId);
  strcpy(serveTopic, "device/");
  strcat(serveTopic, clientId);
  strcat(serveTopic, "/serve");
 
  strcpy(connectTopic, "device/");
  strcat(connectTopic, clientId);
  strcat(connectTopic, "/connect");
}

void loop() {
}




uses 1958 bytes of flash and 262 bytes of SRAM, plus you don't have to worry about memory fragmentation.

As mentioned above, one too many byte is allocated in the arrays And best would be to have the cstring defined as const to not duplicate them in the code ( for example “device/” is allocated many times). (Ideally you would go to progmem)