Returning an int array from a function

There are several ways to achieve what you want:

The way that does not work

Did you try to compile the code you gave in the question? Then you probably noticed it does not compile. I tried and got: “error: expected primary-expression before ‘]’ token”, meaning the compiler expected return array[some_index];.

The way that almost works

Remove the brackets and just return array:

int *function()
{
    int array[3];
    array[0] = 19;
    array[1] = 7;
    array[2] = 69;
    return array;
}

This may work, or not, depending on the surrounding code, on how the optimizer processes your program, and on how lucky you are when you test it. It is called “undefined behaviour” and is something you should always avoid.

Here is what happens: in C (and C++) you cannot return an array. The identifier array “decays” to a pointer pointing at its first element. Then return array is essentially equivalent to return &array[0]. The problem is that, since the array is allocated in the function's stack frame, it ceases to exist when the function returns, thus the caller gets a pointer to an area of memory that is not allocated anymore. Likely memory corruption ahead. The compiler warned me when I tried to do that: “warning: address of local variable ‘array’ returned”. It's a very serious warning that should never be ignored.

The simplest fix: make the array static

As suggested by Chris Stratton in a comment, you can make the array static, so it will be allocated for the whole life of the program:

int *function()
{
    static int array[3];
    array[0] = 19;
    array[1] = 7;
    array[2] = 69;
    return array;
}

The only catch is that the function is now not reentrant, meaning that every time you call it it will clobber the same array it returned on the previous calls. Depending on your use case, this may not be an issue, but it's something you need to be aware of.

Have the caller manage the allocation

A safer (reentrant) way is to have the caller provide the memory needed for the array. This is a very common method in C, and is suggested both by Harper Shelby's answer and Chris Stratton's comment:

int *function(int array[3])
{
    array[0] = 19;
    array[1] = 7;
    array[2] = 69;
    return array;
}

The caller can allocate in static memory, or in it's own stack frame, or on the heap... but you do not care, the allocation is the caller's problem now.

A couple of things to note here:

  • The prototype is equivalent to int *function(int *array): the function receives only a pointer. Writing array[3] instead of *array has the sole effect of documenting that the function expects the pointer to point to somewhere with room for 3 values. You could document that in a comment instead.
  • The function could return void, since the caller presumably knows the address it is giving you. Returning that same address is just for convenience, as it can help you chain calls like another_function(function(array)).

Return the array in a struct

One may wonder: why can't we return an array in the first place. I do not know for sure why the authors of the language made this choice, but one possible explanation is that it is expensive to return a big array by value, as it involves copying the whole of it. Now, if your array is really only three ints, then that argument does not apply, and you may reasonably want to really return the whole array by value. This can be done by embedding it inside a struct:

// Struct definition.
struct Array3 {
    int array[3];
};

Array3 function()
{
    Array3 a;
    a.array[0] = 19;
    a.array[1] = 7;
    a.array[2] = 69;
    return a;
}

I have a function that needs to return 3 int values.

In the interests of completeness I'll suggest another method. Let's assume that you really need 3 values and not 300.

void foo (int & a, int & b, int & c)
{
  a = 19;  //these are just random numbers
  b = 7;
  c = 69;
}

void setup() {
  Serial.begin (115200);
  int x, y, z;
  foo (x, y, z);
  Serial.println (x);
  Serial.println (y);
  Serial.println (z);
}

void loop() {
}

This conceivably will use less memory because the compiler may be able to optimize all 3 values into registers. In fact in that specific example, it has optimized away the entire function call:

000000be <setup>:
  public:
    inline HardwareSerial(
      volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,
      volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
      volatile uint8_t *ucsrc, volatile uint8_t *udr);
    void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
  be:   26 e0           ldi r18, 0x06   ; 6
  c0:   40 e0           ldi r20, 0x00   ; 0
  c2:   52 ec           ldi r21, 0xC2   ; 194
  c4:   61 e0           ldi r22, 0x01   ; 1
  c6:   70 e0           ldi r23, 0x00   ; 0
  c8:   82 e2           ldi r24, 0x22   ; 34
  ca:   91 e0           ldi r25, 0x01   ; 1
  cc:   0e 94 64 01     call    0x2c8   ; 0x2c8 <_ZN14HardwareSerial5beginEmh>
  d0:   4a e0           ldi r20, 0x0A   ; 10
  d2:   50 e0           ldi r21, 0x00   ; 0
  d4:   63 e1           ldi r22, 0x13   ; 19  <---------- x
  d6:   70 e0           ldi r23, 0x00   ; 0
  d8:   82 e2           ldi r24, 0x22   ; 34
  da:   91 e0           ldi r25, 0x01   ; 1
  dc:   0e 94 4d 03     call    0x69a   ; 0x69a <_ZN5Print7printlnEii>
  e0:   4a e0           ldi r20, 0x0A   ; 10
  e2:   50 e0           ldi r21, 0x00   ; 0
  e4:   67 e0           ldi r22, 0x07   ; 7   <---------- y
  e6:   70 e0           ldi r23, 0x00   ; 0
  e8:   82 e2           ldi r24, 0x22   ; 34
  ea:   91 e0           ldi r25, 0x01   ; 1
  ec:   0e 94 4d 03     call    0x69a   ; 0x69a <_ZN5Print7printlnEii>
  f0:   4a e0           ldi r20, 0x0A   ; 10
  f2:   50 e0           ldi r21, 0x00   ; 0
  f4:   65 e4           ldi r22, 0x45   ; 69  <---------- z
  f6:   70 e0           ldi r23, 0x00   ; 0
  f8:   82 e2           ldi r24, 0x22   ; 34
  fa:   91 e0           ldi r25, 0x01   ; 1
  fc:   0c 94 4d 03     jmp 0x69a   ; 0x69a <_ZN5Print7printlnEii>

00000100 <loop>:
 100:   08 95           ret

Tags:

C++

Array