Not able to do char* to char**

What is wrong with this... StrTitlea[0] = subString(str, 1, 3). It has to be something with char*=char*. Maybe I have to resize it, but I doubt it because it's with a pointer. Can someone help me out with this?

Here is my code

char* str;
char line[60];
char** StrTitlea;
int cnt = 0;

while (DbFile.available()) {
	int n = DbFile.fgets(line, sizeof(line));
        str = line;
        StrTitlea[0] = subString(str, 1, 3);
        cnt++
}
char* subString(char* s, int index, int n) {
	char* res = new char[n + 1];
	sprintf(res, "%.*s", n, s + index);
	Serial.println(res);
	return res;
}
This is the stack I get.
Exception:Error:
e:\documents\visual studio 2019\projects\arduino\sd_card_read_sort_list_sdfat\libraries\sdfat-master\2.1.0\sdfat-master\src\common/arduinofiles.h(63): error 0x400d1480:SortPlaylist(char*, char*)
0x400014fd: ?? ??:0
0x4000150d: ?? ??:0
e:\documents\vs\projects\arduino\sd_card_read_sort_list_sdfat\libraries\sdfat-master\2.1.0\sdfat-master\src\common/arduinofiles.h(63): error 0x400d167c:setup()
C:\Users\user\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.6\cores\esp32/main.cpp(18): error 0x400d4636:loopTask(void*)
/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c:355 (discriminator 1):::0x40086161:vPortTaskWrapper
0x40078000: ?? ??:0
0x40080400: ?? ??:0
0x400806a8: ?? ??:0

If I leave this StrTitlea[0] = subString(str, 1, 3) out it doesn't get stuck anymore

just use char* StrTitlea; it's not a pointer to a pointer

building dynamic strings will lead to disappointment probably

not sure either what you are trying to achieve, you read a file but will only keep the last substring? is that it?

This is what I want to achief

StrTitlea[0] = 001.mp3
StrTitlea[1] = 002.mp3

001.mp3 is also a char*. Forgot to tell that.

I'm getting crazzy. If I do

StrTitleArray[0] = subString("my test", 1, 3);

outside the while it works...

when I place in the while it get stuck

Yes

so StrTitlea then needs to be a 2D array of chars

char StrTitlea[20][10]; // room for 20 titles of max 9 chars and trailing null

then to copy the substring into the array, you would use strncpy() for example

try something like this:

const byte maxCnt = 20;
const byte maxTitleLength = 9;
char titles[maxCnt][maxTitleLength + 1]; // +1 for the trailing null char
byte cnt = 0;

while (DbFile.available()) {
  int n = DbFile.fgets(line, sizeof(line));
  if (cnt < maxCnt) {
    strncpy(titles[cnt], line + 1, 3); // copy 3 characters starting from second char in line into titles[cnt]
    titles[cnt][3] = '\0'; // terminate the cString
    // strncat(titles[cnt], maxTitleLength, ".mp3"); // if you want to add ".mp3"
    // titles[cnt][maxTitleLength] = '\0'; // just in case terminate the cString
    cnt++;
  } else {
    // handle overflow, too many titles
  }
}


Instead of reading the whole entry just to pick the first 3 chars, try reading when serial.available() > 0 one char at a time and save the first 3 in a pre-defined array, dynamic allocation is not a good idea in a small memory environment.

Consider storing the file names and titles in EEPROM that saves through power cycles.

Ok I'm getting the point now :slight_smile: Can't set a dynamic in to two dynamic array. Because the memory doesn"t know how much memory it has to located. I was thinking now is evryting constant but it's wasen't for the array's.

This is what I did.

char* StrTitleArray[255];
str = subString(line, 1, 5);
StrTitleArray[cnt] = str;

And it works, thanks to the advice of @J-M-L . I think now that I can set as "many" titles in it, but with a string lenght of max 255.

Strang engouh... if I declared like this

char* StrTitle[2];

I'm getting evy char of the title. Not sure how that comes

this could work indeed.
you have dynamic allocation for the content and so you need to worry about freeing up those bytes at some point possibly

I understand you don't know how many you'll have but at some point (if you have too many) your Arduino will be short in SRAM and because you don't test the allocation (hard on an AVR) you will experience possibly some random crashes.

If you think you won't have more than 20 or 100, just allocate statically the memory for the max usage expected, this way you'll be sure that the rest of the code is not impacted when you'll reach the limit.


post full code. the declaration just creates space for 2 pointers. what they point at depends of the rest of the code

Would the memory be free when I leave the procedure? If I declared within the procudere

try this simple code

class A {
  public:
    A()  {Serial.println("\tA constructor");} // constructor
    ~A() {Serial.println("\tA destructor");} // destructor
    void sayHello() {Serial.println("\tHello from A");}
};

A* createInstance() {
  Serial.println("entering createInstance");
  A* a = new A;
  return a;
  Serial.println("leaving createInstance");
}

void setup() {
  Serial.begin(115200); Serial.println();
  Serial.println("entering setup");
  A* myA = createInstance();
  myA->sayHello();
  // delete myA; // try with this commented or not
  Serial.println("leaving setup");
}

void loop() {}

compile and run twice, once as is and once after uncommenting
// delete myA; // try with this commented or not

look at the Serial monitor for what happens (115200 bauds)

it is usually recommended to avoid calling new and delete explicitly.

There are multiple publications on this, check for example (first hit)

You where right. it was because I declared the line like this char line[60];
Soo when I do this

char* StrTitleArray[2];
char* str;
str = subString(line, 1, 5);

str has a then a size of 60

sooo

StrTitle[cnt] = str; //is not size 2 anymore but 60

no... str is still just a pointer
it's pointing to the newly allocated space so it has 6 bytes allocated (from the new char[n + 1];)

post #11 intresting...

if I set it with an equal length will it allocated to to a new space?

char line[60];
char* StrTitleArray[60];

Are you reading filenames off an SD card or some other type of storage?
I would probably go through the directory listing twice, the first time to count how many files there are, then the 2nd time to get the filenames you want. Knowing how many files there are allows you to declare an array within the function with sufficient space to hold all the filenames, and lets you test for some maximum limit on the number of files if you want to limit the memory used.

you seem a bit lost with pointers :slight_smile:

char line[60]; this allocates 60 bytes of memory, first byte is pointed at by the name of the array

char* str; this allocates (on AVR) 2 bytes for a pointer. it's pointing nowhere at this stage. There is NO memory whatsoever allocated for the text. (you can't copy anything into the space pointed at)

char* StrTitleArray[2]; this allocates (on AVR) 4 bytes of memory as a pointer is 2 bytes and you want space for 2 of those. The pointers are not initialized (unless it's a global variable) and there is NO memory whatsoever allocated for the text. (you can't copy anything into the space pointed at)

you could do
str = line;
and now the pointer str would point at the first byte of the memory location the line array is stored

similarly you can do
StrTitleArray[0] = line
that would initialise the first pointer in the array to point at the first byte of the memory location the line array is stored

but unless you have memory set aside or dynamically allocated, you can't copy anything in those pointers (str or StrTitleArray[0] or StrTitleArray[1]) as there is no byte reserved for content.

makes sense ?

Thanks for the tip. I had that in mind before but did not yet dit that because I saw no reason for it. Now I do.

On post #17

Very much. Thanks for clearing my mind.

Now I try to declare a 2D array with one (variable) size and one fix size. The variable size is the size that I previous determ from the DbFile.

DbFile.rewind();
int cnt = 0;
char line[255];
while (DbFile.available()) {
     DbFile.fgets(line, sizeof(line));
     cnt++;
}
memset(line, 0, sizeof line);//clear array
char StrTitle[255];
StrTitle = new char[cnt][255];