How to return an array or something like that

Hello out there.
I have the problem that a function should pass an array - impossible - , better: a reference to it (?). I don't get any further.
this is the current code:

void setup () {
  Serial.begin (115200);
  delay(750);
  const byte macAddr[]  = {0x28, 0xFF, 0x64, 0x1E, 0x5E, 0x6C};
  doIt(macAddr, 6);
}  

void doIt(const byte nameOfArray[], const int sizeOfArray) {
  Serial.println("do it :");
  byte returnArray[6];
  for(int i=0; i < sizeOfArray; i++) {
    returnArray[i] = nameOfArray[i] + 13;
    Serial.print(nameOfArray[i], HEX);
    Serial.print("(old), ");
    Serial.print(returnArray[i], HEX);
    Serial.println(" (new)");
  }

  ////
  Serial.println("\nTEST starting...");
  auto& test = returnArray;
  for(int j=0; j < sizeOfArray; j++) {
    Serial.println(test[j], HEX);
  }
  Serial.println("...ending");
  ////

}

void loop() {
  // do nothing
}

now I would like to return the variable 'test' using 'return' so that I can use the modified array in the main code later. I'm stuck there and can't get any further.
Since I'm a beginner, I'm not at all familiar with pointers and references, and I don't want to go into this in depth at the moment. I'm looking for a solution as quickly as possible.

i'm using an esp32 and the arduino-ide 1.8.19.

with the request for constructive and further help. (please no cryptical pseudocode - i'm a beginner, thanks)
thanks in advance

I'll explain since you asked, but I don't think this is your best approach (see end of post for details)

so arrays (in c++) are, by default passed by reference. That includes returns. Just like function arguments, arrays decay to a pointer to the first element.

byte* doIt(const byte nameOfArray[], const int sizeOfArray) {
  Serial.println("do it :");
  byte returnArray[6];
  for(int i=0; i < sizeOfArray; i++) 
  {
    returnArray[i] = nameOfArray[i] + 13;
    Serial.print(nameOfArray[i], HEX);
    Serial.print("(old), ");
    Serial.print(returnArray[i], HEX);
    Serial.println(" (new)");
  }
  Serial.println("\nTEST starting...");
  auto& test = returnArray;
  for(int j=0; j < sizeOfArray; j++)     Serial.println(test[j], HEX);
  Serial.println("...ending");


   return nameOfArray;
}

note that the function return type is a (byte*) that a byte pointer, not a byte (common misconception). Also note the return is just the variable name, no [ ]

now you will get a ptr to the first element, you'll probably want to store that in a var:

btye* myArrayPtr = doIt(macAddr, 6);

to use * to defeference the array positions:

*myArrayPtr   //pos 0
*(myArrayPtr+1) //pos 1
*(myArrayPtr+2)// pos 2

honestly, you'd be better off just making your array global. Your do it function doesn't need to change (excepts for the const needs to go) and whatever you've done to the array will just be there when the function has completed. much simpler.

byte myArray[];   ///global declaration  --you could just make macAddr non const.

`void doIt(byte nameOfArray[], const int sizeOfArray) {
//same stuff }

the name of an array is a pointer to the first element therefore arrays are passed by reference (changes made to the array in a function change the array in the calling function, e.g.

void setup () {
  Serial.begin (115200);
  delay(750);
  byte macAddr[]  = {0x28, 0xFF, 0x64, 0x1E, 0x5E, 0x6C};
  Serial.print("(old), ");
  for(int j=0; j < 6; j++)  
    Serial.println(macAddr[j], HEX);
   doIt(macAddr, 6);
   Serial.println(" (new)");
   for(int j=0; j < 6; j++)  
       Serial.println(macAddr[j], HEX);
}  

void doIt(byte nameOfArray[], const int sizeOfArray) {
  Serial.println("do it :");
  for(int i=0; i < sizeOfArray; i++) {
    nameOfArray[i] = nameOfArray[i] + 13;
  }
}

void loop() {}

when run gives

(old), 28
FF
64
1E
5E
6C
do it :
 (new)
35
C
71
2B
6B
79

Edit: if inside the function you wish to create a new array and return a pointer to it

void setup () {
  Serial.begin (115200);
  delay(750);
  const byte macAddr[]  = {0x28, 0xFF, 0x64, 0x1E, 0x5E, 0x6C};
  byte *retarray;
  Serial.print("(old), ");
  for(int j=0; j < 6; j++)  
    Serial.println(macAddr[j], HEX);
   retarray=doIt(macAddr, 6);
   Serial.println(" (new)");
   for(int j=0; j < 6; j++)  
       Serial.println(retarray[j], HEX);
}  

byte* doIt(const byte nameOfArray[], const int sizeOfArray) {
  Serial.println("do it :");
  static byte returnArray[6];
  for(int i=0; i < sizeOfArray; i++) 
    returnArray[i] = nameOfArray[i] + 13;
  return returnArray;
}

void loop() {}

returnArray[] must be static so it is maintained on return from the function

@mtraven :
thanks for your time !
please no pointers ! (or is there no way using references instead)
what way would you prefer to go ?
A Mac-Address should ever be a const, shouldn't it ?
I know, I can be nasty, sometimes.
Target:
array -> function (to do something with it) -> returns modified array to use for other functions ...

@horace :
thanks for your time, too.
Is there a way using references instead of pointers ?

but first, i want to test the code. I will report later.
many thanks !

the syntax starts to get a bit comples, e.g.

byte (&doIt(const byte nameOfArray[], const int sizeOfArray))[6] {
  Serial.println("do it :");
  static byte returnArray[6];
  for(int i=0; i < sizeOfArray; i++) 
    returnArray[i] = nameOfArray[i] + 13;
  return returnArray;
}

void setup () {
  Serial.begin (115200);
  delay(750);
  const byte macAddr[]  = {0x28, 0xFF, 0x64, 0x1E, 0x5E, 0x6C};
  Serial.print("(old), ");
  for(int j=0; j < 6; j++)  
    Serial.println(macAddr[j], HEX);
   byte (&retarray)[6]=doIt(macAddr, 6);
   Serial.println(" (new)");
   for(int j=0; j < 6; j++)  
       Serial.println(retarray[j], HEX);
}  

void loop() {}

a run still gives

(old), 28
FF
64
1E
5E
6C
do it :
 (new)
35
C
71
2B
6B
79

If your array is in a struct then you can return a full struct by value or reference or pointer

no, there is no way to return an array without using pointers. That's always going to be a reference to the first element and that a pointer. word of advice, take a night & figure out basic reference & pointer stuff, its worth your time.

But as I said, just declare the variable global. and get rid of the const in the function & variable. argument. No pointers, c++ handles the by reference part internally.

its not, it is an array on ints-a mac addresss. and this person seemsadmittedly apposed to learning how pointers work.

really? you're saying this would run? (i dont even think it would compile)

typedef struct fraction
{
    uint32_t numer, denom;
    uint16_t wholeN;
    uint16_t remain;
    int8_t sign;
}fraction;

fraction myFraction;

void setup(){  myReturnFunction();}

void loop(){ //more stuff}

fraction myReturnFunction()
{
   //other stuff
return myFraction;
}

I think it has to be:

void setup(){  myReturnFunction();}

void loop(){ //more stuff}

fraction* myReturnFunction()
{
   //other stuff
return myFraction;
}

in which case, you have a pointer to your structure. So there is no way to return an entire struct by value or reference. If I'm wrong, by all means, educate me.

C-style arrays have many quirks like this. If you use a C++ std::array, you can just treat it as a normal variable and return it from functions:

#include <array>

std::array<byte, 6> doIt(const byte nameOfArray[], const int sizeOfArray) {
  std::array<byte, 6> returnArray;
  assert(sizeOfArray <= returnArray.size());
  for(int i=0; i < sizeOfArray; i++) {
    returnArray[i] = nameOfArray[i] + 13;
  }

  return returnArray;
}

Unless the array is bundled in a Struct

@mtraven:

  1. Possibility (const byte macAddr = {...}) :
void setup () {
  Serial.begin (115200);
  delay(750);
  const byte macAddr[]  = {0x28, 0xFF, 0x64, 0x1E, 0x5E, 0x6C};
  doIt(macAddr, 6);
} // ...setup  


byte* doIt(const byte nameOfArray[], const int sizeOfArray) {
  Serial.println("do it :");
  byte returnArray[6];
  for(int i=0; i < sizeOfArray; i++) 

  {
    returnArray[i] = nameOfArray[i] + 13;
    Serial.print(nameOfArray[i], HEX);
    Serial.print("(old), ");
    Serial.print(returnArray[i], HEX);
    Serial.println(" (new)");
  }

  Serial.println("\nTEST starting...");
  auto& test = returnArray;
  for(int j=0; j < sizeOfArray; j++)     Serial.println(test[j], HEX);
  Serial.println("...ending");


  return nameOfArray;

} // ...byte*


void loop() {

}

ERROR:
invalid conversion from 'const byte* {aka const unsigned char*}' to 'byte* {aka unsigned char*}' [-fpermissive]

  1. Possibility (byte macAddr = {...})
void setup () {
  Serial.begin (115200);
  delay(1000);

  byte macAddr[]  = {0x28, 0xFF, 0x64, 0x1E, 0x5E, 0x6C};

  doIt(macAddr, 6);
} // ...setup  

void doIt(byte nameOfArray[], const int sizeOfArray) {

  Serial.println("do it :");
  byte returnArray[6];

  for(int i=0; i < sizeOfArray; i++) 
  {
    returnArray[i] = nameOfArray[i] + 13;
    Serial.print(nameOfArray[i], HEX);
    Serial.print("(old), ");
    Serial.print(returnArray[i], HEX);
    Serial.println(" (new)");
  } // ...for
  
  Serial.println("\nTEST starting...");
  auto& test = returnArray;
  for(int j=0; j < sizeOfArray; j++)     Serial.println(test[j], HEX);
  Serial.println("...ending");


  return nameOfArray;

} // ...void


void loop() {

}

ERROR:
void doIt ...
=> return-statement with a value, in function returning 'void' [-fpermissive]

byte doIt ...
=> invalid conversion from 'byte* {aka unsigned char*}' to 'byte {aka unsigned char}' [-fpermissive]

@horace :

void setup () {
  Serial.begin (115200);
  delay(750);
  const byte macAddr[]  = {0x28, 0xFF, 0x64, 0x1E, 0x5E, 0x6C};
  byte *retarray;
  Serial.print("(old), ");
  for(int j=0; j < 6; j++)  
    Serial.println(macAddr[j], HEX);
   retarray=doIt(macAddr, 6);
   Serial.println(" (new)");
   for(int j=0; j < 6; j++)  
       Serial.println(retarray[j], HEX);
}  

byte* doIt(const byte nameOfArray[], const int sizeOfArray) {
  Serial.println("do it :");
  static byte returnArray[6];
  for(int i=0; i < sizeOfArray; i++) 
    returnArray[i] = nameOfArray[i] + 13;
  return returnArray;
}

void loop() {}

=> looks like it works :slight_smile: ...

see #1

Hi,
thanks to you, too. I will throw an eye on it ...

It’s an answer to #1 and similar idea to std::array

Consider:

#include <algorithm>
#include <array>

// Type for mac addresses
using mac_t = std::array<byte, 6>;

// Add 13 to each element of the mac address
mac_t doIt(const mac_t &old_mac) {
    mac_t new_mac;
    std::transform(old_mac.begin(), old_mac.end(), new_mac.begin(),
                   [] (byte x) { return x + 13; });
    return new_mac;
}

// Print a mac address
void print(const mac_t &mac) {
    Serial.print("{ ");
    for (byte element : mac) {
        Serial.print(element, HEX);
        Serial.print(", ");
    }
    Serial.print("}\r\n");
}

void setup() {
    Serial.begin(115200);
    mac_t a {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
    print(a);
    mac_t b = doIt(a);
    print(b);
}

void loop() {}
{ 11, 22, 33, 44, 55, 66, }
{ 1E, 2F, 40, 51, 62, 73, }

my bad--if you want to keep the const argument in the function, the function needs to return a const:

const byte* doIt(const byte nameOfArray[], const int sizeOfArray)

yah, why'd ya keep the return statement in a void function? variable is global, so whatever you did to it in the function is already done.

I didn't run it yet, but first:
where i have to place it?
before, in or after setup(); ?
hopefully not in loop() ;.)

Where do you place what? The first code snippet I posted is just an #include statement and a function definition, like your doIt() function, simply with a different return type. This goes at the global scope, not inside the setup or loop function.

The second code block I posted is a complete sketch.

thanks, again.
My code is an example, JUST an example. The array could be a mac-address, a device-address, an int-array ... i don't want to use it only for this code. I just want to learn in the MOMENT, how i can deliver an array to an function, do something with this array (or it's copy, pointer, what else), and than return it for using with or in function, a way to load it in a variable [byte/int blablub = function(xyz)] ... that's not much but complex enough for me.
At my level of programming i just need that in the moment, maybe about half a year. Than, maybe, i will be able to 'work' with classes, structures, ...
earlier i just did use if-else statements ... step by step ... and i need working code, tailored to my example, so i can learn best ...

by the way: you are from germany ?

EDIT:
ah, didn't see your post. people here are sooo fast. no time for me for testing. :frowning: ...
doesn't mean: stopp answering !
you're all awesome, quick and fast support, well too and qualified !

Then the "easiest way" if you don't want complex syntax or more elaborate objects is through pointer and size. The type of the data needs to be known though (if you don't want to use templates)

// fill up the int array with random numbers in interval [0, 100]
void randomContent(int * arrayPointer, size_t arraySize) {
  for (size_t i = 0; i < arraySize; i++) arrayPointer[i] = random(0,101);
}

or

// fill up the int array with random numbers in interval [0, 100]
void randomContent(int arrayPointer[], size_t arraySize) {
  for (size_t i = 0; i < arraySize; i++) arrayPointer[i] = random(0,101);
}

if you do not require the original contents of the array the first program of post #3 does what you are looking for, e.g. returns an updated array which can then use in the following code