A Newbie's Troubled Plight with Functions and Arrays

As an arduino and general C/C++ newbie, dealing with arrays area major pain in the side. Actually using them is mostly fine. It's kind of annoying that you have to manually shift everything down if you "delete" a value, but that's beside the point. The real problem (at least for me) is declaring them in functions. Let me give you a few examples of the problems I keep running into:

1. Arrays as parameters

Let's say I have a simple adding function:

int add(int a, int b) {
    return a + b;
}

I find myself wanting to add up a bunch of numbers at the same time, so I use arrays to quickly mass-produce addition without having to call the function a trillion times. In order to do this, my reasoning is simple: You make arrays that you don't know the length of by doing int[] someArray; , so why not do the same here?

With this in mind, I update the add function like so:

int add(int[] numbers) {
    int additionThusFar;
    for (byte i = 0; i > sizeof(numbers) / sizeof(numbers[0]); i++) {
      additionThusFar += numbers[i];
    }
   return additionThusFar;
}

I pass numbers 1-9 in and Serial print what this function returns, and...

/usr/local/bin/arduino-cli compile --fqbn arduino:avr:uno --build-cache-path /tmp --output-dir /tmp/63609153/build --build-path /tmp/arduino-build-EA7DE48C5522DFA4DDE56F496C67791F /tmp/63609153/sketch_apr28a

/tmp/63609153/sketch_apr28a/sketch_apr28a.ino:5:15: error: expected ',' or '...' before 'numbers'

int add(int[] numbers) {

^~~~~~~

/tmp/63609153/sketch_apr28a/sketch_apr28a.ino:5:15: error: expected ',' or '...' before 'numbers'

int add(int[] numbers) {

^~~~~~~

/tmp/63609153/sketch_apr28a/sketch_apr28a.ino: In function 'int add(int*)':

/tmp/63609153/sketch_apr28a/sketch_apr28a.ino:7:33: error: 'numbers' was not declared in this scope

for (byte i = 0; i > sizeof(numbers) / sizeof(numbers[0]); i++) {

^~~~~~~

/tmp/63609153/sketch_apr28a/sketch_apr28a.ino: In function 'void loop()':

/tmp/63609153/sketch_apr28a/sketch_apr28a.ino:19:41: error: cannot convert '<brace-enclosed initializer list>' to 'int*' for argument '1' to 'int add(int*)'

Serial.println(add({1,2,3,4,5,6,7,8,9}));

^

Error during build: exit status 1

It expected a comma or 3 dots before I made the parameter? Huh??? Of course, I go to try both of those things and it doesn't work. Thanks, compiler...

2. Returning arrays

Ok, let's say that I have this simple adding example again:

int add(int a, int b) {
    return a + b;
}

Now, I have a different problem: I find myself needing to add up 10 different sets of numbers. While I could do it manually by doing...

int answer1 = add(0, 1);
int answer2 = add(2,3);
int answer3 = add(4, 5);
...

I don't wanna do that. That's a lotta writing. So instead. I want to just give 2 sets of numbers as parameters and return an array. However, I remember how I still can't figure out how use arrays as parameters thanks to my room temperature IQ, so I use my last 2 braincells to come up with an innovatve, yet extremely wasteful solution: Strings. Now, the only thing standing in my way to unrivaled adding efficiency is the function's return variable declaration. What do I call it? I don't know how many sets of numbers I'm going to add, and I don't want to limit it to a certain amount of choices, so I guess I'll call it int[] add()? And so, I write the following code:

int[] add(String set1, String set2) { // Set 1 and set 2 have to be the same size, or this won't work lol
    int[set1.length()] additionResults;
    for (byte i = 0; i > set1.length(); i++) {
      additionResults[i] = atoi(set1[i]) + atoi(set2[i]);
    }
   return additionResults;
}

Like last time, I Serial print my answers, and...

/usr/local/bin/arduino-cli compile --fqbn arduino:avr:uno --build-cache-path /tmp --output-dir /tmp/3181240453/build --build-path /tmp/arduino-build-EA7DE48C5522DFA4DDE56F496C67791F /tmp/3181240453/sketch_apr28a

/tmp/3181240453/sketch_apr28a/sketch_apr28a.ino:5:4: error: decomposition declaration cannot be declared with type 'int'

int[] add(String set1, String set2) { // Set 1 and set 2 have to be the same size or this wont work lol

^~

/tmp/3181240453/sketch_apr28a/sketch_apr28a.ino:5:4: note: type must be cv-qualified 'auto' or reference to cv-qualified 'auto'

/tmp/3181240453/sketch_apr28a/sketch_apr28a.ino:5:4: error: empty decomposition declaration

/tmp/3181240453/sketch_apr28a/sketch_apr28a.ino:5:7: error: expected initializer before 'add'

int[] add(String set1, String set2) { // Set 1 and set 2 have to be the same size or this wont work lol

^~~

/tmp/3181240453/sketch_apr28a/sketch_apr28a.ino: In function 'void loop()':

/tmp/3181240453/sketch_apr28a/sketch_apr28a.ino:19:18: error: 'add' was not declared in this scope

Serial.println(add(String("135"), String("246")));

^~~

/tmp/3181240453/sketch_apr28a/sketch_apr28a.ino:19:18: note: suggested alternative: 'rand'

Serial.println(add(String("135"), String("246")));

^~~

rand

Error during build: exit status 1

Decomposition whatsit? Those words are too big for me to understand. But, after looking deeper into the traceback, I see something about it being mad about the declaration being empty (which I'm assuming means the empty brackets when I first made the function). Well, that sucks-- guess we have to limit how many sets of numbers you can ad at once. And so, I make some edits to my code, and the final revision looks like this:

int[3] add(String set1, String set2) { // Set 1 and set 2 have to be the same size, or this won't work lol
    int[set1.length()] additionResults;
    for (byte i = 0; i > set1.length(); i++) {
      if (i + 1 > 4) {
        Serial.println("Too many numbers to add! Only returning the first 3...");
        return {additionResults[0], additionResults[1], additionResults[2]};
      }
      additionResults[i] = atoi(set1[i]) + atoi(set2[i]);
    }
   return additionResults;
}

And now, I try to run it, and finally it...

/usr/local/bin/arduino-cli compile --fqbn arduino:avr:uno --build-cache-path /tmp --output-dir /tmp/2978091594/build --build-path /tmp/arduino-build-EA7DE48C5522DFA4DDE56F496C67791F /tmp/2978091594/sketch_apr28a

/tmp/2978091594/sketch_apr28a/sketch_apr28a.ino:5:5: error: expected identifier before numeric constant

int[3] add(String set1, String set2) { // Set 1 and set 2 have to be the same size or this wont work lol

^

/tmp/2978091594/sketch_apr28a/sketch_apr28a.ino:5:5: error: expected ']' before numeric constant

/tmp/2978091594/sketch_apr28a/sketch_apr28a.ino:5:4: error: decomposition declaration cannot be declared with type 'int'

int[3] add(String set1, String set2) { // Set 1 and set 2 have to be the same size or this wont work lol

^

/tmp/2978091594/sketch_apr28a/sketch_apr28a.ino:5:4: note: type must be cv-qualified 'auto' or reference to cv-qualified 'auto'

/tmp/2978091594/sketch_apr28a/sketch_apr28a.ino:5:4: error: empty decomposition declaration

/tmp/2978091594/sketch_apr28a/sketch_apr28a.ino:5:8: error: expected initializer before 'add'

int[3] add(String set1, String set2) { // Set 1 and set 2 have to be the same size or this wont work lol

^~~

/tmp/2978091594/sketch_apr28a/sketch_apr28a.ino: In function 'void loop()':

/tmp/2978091594/sketch_apr28a/sketch_apr28a.ino:23:18: error: 'add' was not declared in this scope

Serial.println(add(String("135"), String("246")));

^~~

/tmp/2978091594/sketch_apr28a/sketch_apr28a.ino:23:18: note: suggested alternative: 'rand'

Serial.println(add(String("135"), String("246")));

^~~

rand

Error during build: exit status 1

... errors. Who was I kidding here? If this worked then I wouldn't include this as a segment :p. More on the error itself, it sounds like it's trying to sound as sophisticated as it can. "numeric constant"? That sure is a fancy way to say an unchanging integer...

Looking into the error itself again, things get even more confusing. I can't declare integer arrays with an integer? How else am I supposed to declare them?! Expected an ending bracket before the 3? Then what's supposed to be inside the brackets? Friendship and Magic??? I can't leave it blank either because it complains about that as well! Ugh, it just gives me a headache thinking about it...

But there's also something else: a note. I glossed over it on the last one, but now that it popped up again, it's the perfect time to talk about it. To quote:

/tmp/2978091594/sketch_apr28a/sketch_apr28a.ino:5:4: note: type must be cv-qualified 'auto' or reference to cv-qualified 'auto'

Ok, first question: What the heck does it mean to be 'cv qualified'??? Do I need to vet each data type I use in my code for an auto engineering degree before it has the privilege of being used in my simple addition function? This stuff is just way too over my head...

I've been told by many that I overthink things or forget the most basic parts of whatever I'm stuck on, and I'm sure that's what's happening here. I've tried to quickly google search answers to my problems, but either Bing Search sucks or I'm not searching for the right thing because the answers that populate the search results don't actually reflect my problem-- just something close to it. But this just convinces me even more that I'm doing something dumb because the only way for there to be no good answers to a question online is if your question is very unique (which it isn't), or it's something so obviously wrong that there's no reason for anybody to waste their time asking about it in order to fix it. So, where did I screw up? It would make my day if someone could just tell me where I went wrong. I've been looking at this code and the compiler's cryptic warnings for an hour now going crazy trying to figure this out... Of course, I have more problems, like problems with using multidimensional arrays as parameters, but right now I just want to solve this problem 1 step at a time. Thanks for reading this half-help-half-rant thread :).

have a read of pass-array-to-functions-in-C++

If that’s too complicated for you and If you are using 32 bits Arduino then use other std container classes like Vector, queues and the likes and that will make your life simpler.

Arrays are actually quite simple - with a few things to know

  • they don’t change size
  • they are built by contiguous bytes in memory
  • the name of the array is actually a pointer to the first byte in memory
  • the array itself does not know its size (it’s not stored - the compiler knows it in the context where the array name is in scope - sizeof will give you the right number of bytes)
  • when you pass the array name to a function you actually pass the pointer to that first byte and this in the context of the receiving function, the size is unknown. (That is called array to pointer decay). As you pass a pointer, in the function of you ask for sizeof that parameter you get 2 or 4 - that is the size needed to represent a pointer in your Arduino (2 bytes on 8 bits boards, 4 bytes on 32 bits boards usually)

➜ when you look at most functions dealing with arrays you’ll see they take a pointer and a size

learning the programming language doesn't mean you know how to use it efficiently

the following is a common approach to handing arrays in functions.

Since returning an array from a function requires that it exists after the function returns, it's often better to pass the array and its size to the function and the function returns the # of array elements manipulated. readBytesUntil() reads characters from the serial interface until the terminator is reach and returns the # of chars read

void
loop (void)
{
    if (Serial.available ())  {
        char buf [90];
        int n = Serial.readBytesUntil ('\n', buf, sizeof(buf)-1);
        buf [n] = '\0';
        Serial.println (buf);
    }
}

void
setup (void)
{
    Serial.begin (9600);
}

a common approach is to have a pair of indicies identifying the next locations to write to and read from rather than shifing value

What you do is automatic, but looks like this:

   int someArray[] = {1, 2, 3, 4,};

and there are only certain circumstances where that is valid.

You've invented some other syntax. What you seek is reasonable but at this point you have to stop being creative, and start with what you are trying to do, but learn about the basics without prejudice.

One unfortunate fact is that when an array is passed into a function, the function then knows the type of the array elements, and has a pointer to the first one (index 0), but has no idea how many elements there may be.

This can be solved in a few ways. The easiest is to add an additional parameter to the function, so the length of an array can be passed in as well as the array itself.

Alternatively, a special value or "flag" can be used to mark the end of the array. If you have one to spare. Like 32767 the largest 16 bit signed integer might not be important, so,could be used as the value at, say, index 42. When that value is seen, the assumption can be that all the real numbers have been processed.

a7

My bad for forgetting about the thread I made for a few hours whilst trying to figure out a different problem concerning getting variables from a library-made class structure-- glad I remembered to check back this time

... and there's my obvious oversight-- I put the size of the array in the wrong place. Man, that's embarrassing

This is good to know, especially the last two. It's also good to know what the most common approach to getting arrays are because after reading through all the different methods used in the GFG article, I wasn't sure which was the "industry norm". Thanks!

Huh, that's an interesting way of doing it. So instead of shifting everything down, you just keep track of the ones you killed off? Doesn't seem great if you ever need to iterate through the list at first glance (because you have to account for everything being everywhere in the array), but it wouldn't be that much harder to do than doing the "Move everything down" strat I was doing before. Plus, I'm not sure how viable my strat would be when the array gets massive...

Yeah... even though you say it's reasonable, most of the time it's not. Along with my bad habit of overthinking things, I also have a scope problem. The first example might have a valid use, but the second one? An armless person can count how many times I'll ever need to repeat something enough times to warrant making that on their fingers. Not only does it take an exponentially longer time to code if your function is more complex than the one I provided in my example, but it's also harder to read from an outsider's view looking in than just listing them out as a sane person would do. If you want to see the perameter line of the real function example 2 is based on, it's 1 of 3 of these giant messes:

int getIRBoolInput(String prompts[], String trueOption[] = defaultTrueOption, String falseOption[] = defaultFalseOption, String reconfirmOrder = "y", int timesToReconfirm[] = 0, String customLedString = "bg") {

The Strings act like an array by having each character in it corresponding to a loop through the function. If you 2 was somewhat legible to read, then try reading 5 (excluding the last parameter because it's not iterating the same way as the others). There's defintely a better, more streamlined/legible way of doing things, but as an arduino newbie, I don't know how you would do it in C/C++. And so, when you try to optimize when you don't have a god grasp on the language you're writing, then everything will end up worse than it was when you first started

When would that be? I just assumed I totally dropped the ball and started hallucinating here

Hmm... I could see this being useful if you only plan on adding values or only deleting the array in the order it's stored on an array, but if you start deleting this out of order, things could get messy. I bet if you combined this with the way gcjr mentioned, that would be the best way to navigate an array

I'll probably end up marking horace's answer as the solution because they were the first to answer, but I ended up learning a lot more about arrays than I would have passively leanred on my own. Thanks everybody

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.