Go Down

Topic: Returning actual length of array (Read 233 times) previous topic - next topic

gregmcc

Apr 18, 2019, 12:12 pm Last Edit: Apr 18, 2019, 12:57 pm by gregmcc
I've got the below code which declares a 10 record multidimensional array. I then add 3 records to the array (In my application I can add up to 10 records, 3 is just in the example)

I then want to count the number of elements in the array which below would be 3, but Arduino is returning 10 (The number I declared)

How do I programmatically change the code to return 3?

Code: [Select]

String myArray[10][8] = {
  "Cat","Dog","Mouse"
};

void setup() {
  Serial.begin(115200);
  Serial.println(sizeof(myArray)/sizeof(myArray[0]));
}

void loop() {
 
}

AWOL

You haven't added anything to your array, you have only initialised three of the elements.

An empty 10x8 array of String wastes 480 bytes of RAM, even before you've put any data in.
"Pete, it's a fool (who) looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

gregmcc

Re-read through some documents on arrays and clearly I don't understand them very well. I don't think I need a MD array.

I now have this:

 
Code: [Select]

String myArray[10];


void setup() {
  Serial.begin(115200);
  myArray[0] = "abc";
  myArray[1] = "def";
  myArray[2] = "ghi";
 
  Serial.println(sizeof(myArray)/sizeof(myArray[0]));

}


Which still returns 10?

AWOL

Re-read through some documents on arrays and clearly I don't understand them very well. I don't think I need a MD array.

I now have this:

 
Code: [Select]

String myArray[10];


void setup() {
  Serial.begin(115200);
  myArray[0] = "abc";
  myArray[1] = "def";
  myArray[2] = "ghi";
 
  Serial.println(sizeof(myArray)/sizeof(myArray[0]));

}


Which still returns 10?
The array still (always) has ten elements.
Now it it only wastes 60 bytes, before initialisation.
"Pete, it's a fool (who) looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

gregmcc

Solved it using a count loop. Maybe not the best way to do it but its working.

Code: [Select]


void setup() {
  Serial.begin(115200);
  myArray[0] = "abc";
  myArray[1] = "def";
  myArray[2] = "ghi";

  int count=0;

  for (int i = 0; i < (sizeof(myArray) / sizeof(myArray[0])); i++) {
    if (myArray[i] != "") {
      count++;
    }
  }

  Serial.println(count);

GoForSmoke

You could keep a byte variable named howManyStrings or you could write a function that returns a count of Strings > "" or Strings with len > 0 (same thing) that you'd need a byte anyway and more code for sure.

***** and sure enough before I could type this out the OP did the function!

While the OP is at this point and has a choice....

You could take the blue pill which is continuing to use C++ String in your AVR code. You will get all comfy and knowing and only occasionally trying something that crashes before the answer is get a bigger chip.

Unos have little RAM. C char array "strings" ( a series of ASCII text values that ends with a char==0 in a char array ) take less space and are easy to get at being arrays of char variables.

char myStr[ 20 ];

strcpy( myStr, "text" );  // copies 1 string into another

You have to keep and check your own array limits with C strings but they don't ever copy themselves just to append a letter then leave a hole in your heap (eating CPU cycles to do all that) and you -can- easily get to every char in your string directly.

With C strings you plan ahead and the practice gets you better at what can take you farther in the tight MCU world.

You choice the OP has is which direction to develop yourself, your knowledge and coding habits?
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

PaulS

Quote
Maybe not the best way to do it
You misspelled "definitely".

Stop using Strings. An array of char *s, with the elements initialized to NULL will let you count the number of non-NULL pointers.

But, you shouldn't have to. The function that puts data in the array should tell you how much data it put in the array.
The art of getting good answers lies in asking good questions.

GoForSmoke

#7
Apr 18, 2019, 01:50 pm Last Edit: Apr 18, 2019, 01:51 pm by GoForSmoke
I forgot to mention the best part about const strings on AVR's (labels, prompts, error/help messages and keyword tables) is that they can be kept in and used directly from flash memory that AVR's have lots of compared to RAM.

String don't flash.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

Montmorency

#8
Apr 18, 2019, 03:39 pm Last Edit: Apr 18, 2019, 07:31 pm by Montmorency
I then want to count the number of elements in the array which below would be 3, but Arduino is returning 10 (The number I declared)

How do I programmatically change the code to return 3?
"The actual length" of your array is 10. C++ has no feature to tell you how many elements you actually "used" in the initializer. You can only develop some kind of "protocol" yourself, that would allow you to manually detect where "unused" portion of the array begins.

For example, in your case the "unused" elements will be initialized to strings of zero length. So you can simply write a cycle that will scan your 2D array and search for a first empty string. That way you will discover that there are only 3 elements used in your initial array.

However, if tomorrow you decide that you also need to store empty strings in the middle of your array, this approach will break down. This approach will work only if you can come up with a reliable and unique "reserved" value to use as an "end of elements" indicator.

An arguably better way to do it would be just store the proper value in a separate variable and maintain this variable.

P.S. Did you really need a 2D array? Note that for an array of `String` objects, that 8 is not in any way related to length of your strings. You actually have a 2D array of 80 `String` objects. Did you really need 80?

Montmorency

#9
Apr 18, 2019, 03:44 pm Last Edit: Apr 18, 2019, 07:31 pm by Montmorency
You haven't added anything to your array, you have only initialised three of the elements.
True.

But to be precise, it is worth mentioning that C++ (as well as C) adheres to "all-of-nothing" "all-or-nothing" approach to initialization. It is not possible to use initialization syntax to initialize only a portion of an aggregate. One cannot initialize only 3 of the 80 elements. The language will always ensure that the rest of the array it is also initialized.

So, in this case the whole array will be initialized, all 80 elements. In this example the "unused" portion of the array is guaranteed to be default-initialized.

PaulS

Quote
But to be precise, it is worth mentioning that C++ (as well as C) adheres to "all-of-nothing" approach
All-of-nothing?
The art of getting good answers lies in asking good questions.

Montmorency

#11
Apr 18, 2019, 04:07 pm Last Edit: Apr 18, 2019, 04:32 pm by Montmorency
All-of-nothing?
Oh, yes!

You know, when one declares a local array

Code: [Select]
int a[100];

the array is left uninitialized (contains garbage values initially). That's the "nothing" part.

But if one supplies an initializer for just one element

Code: [Select]
int a[100] = { 42 };

the language guarantees that the whole array will be initialized. In this example the rest of the array is guaranteed to be zeroed-out. That's the "all" part.


It is one of the fundamental design decisions built into C and C++ idea of initialization.

PaulS

Quote
the array is left uninitialized (contains garbage values initially). That the "nothing" part.

But if one supplies an initializer for just one element
...
the language guarantees that the whole array will be initialized. In this example the rest of the array is guaranteed to be zeroed-out. That the "all" part.
So, it's all OR nothing.
The art of getting good answers lies in asking good questions.

Montmorency

#13
Apr 18, 2019, 04:13 pm Last Edit: Apr 18, 2019, 04:14 pm by Montmorency
So, it's all OR nothing.
Oh, just a typo. Thanks for pointing it out. Will fix it.

gregmcc

Thanks for the info. Have still much to learn. 

Time to get out that C book.


Go Up