Jump to content

Returning multidimensional arrays from function

- - - - -

This topic has been archived. This means that you cannot reply to this topic.
11 replies to this topic

#1
ThemePark

ThemePark

    Programmer

  • Members
  • PipPipPipPip
  • 124 posts
I've been trying to learn some C++ for a project of mine, but it is giving me quite a headache.

I have a function where I want to initialize an int array, do some calculations on it and then return it, but I'm having a hard time figuring out exactly how to do it.

So far I have this (just a test function):
class Test

{

  int* func()

  {

     int x[1];


     x[0] = 2;


     return x;

  }

}

But I get the warning: "Address of local variable 'x' returned". I've been reading up on that and I have an idea why but I can't see how to return the array then. I've been looking through tutorials and books and searched on Google, but I can't find anything about returning arrays from functions.

Another problem I'm having, is with assigning an object to an array. Say for example that I have a class called Tile, and I want an array of Tiles, run through the array and create each new tile with the constructor Tile(int x). That would give me something like this:

#include "Tile.hpp"


class Test

{

  void func()

  {

    Tile tst[1];


    tst[0] = Tile(5);

  }

}

But then I get the error: "expected unqualified-id at end of input".

I am rather confused as how to create an object in C++. I tried out the previous approach, and looking in a book, it said to use tst[0] (5). Btw, I'm using 2 lines in this case, because in reality I have my "Tile tst[1]" in my header file, and want to create the objects in my cpp file.

Also, studying C++ and trying to fix previous errors, I have found out some things which I would like someone to tell me if I hav e understood them correctly. I am not certain I found the right way to do things.

It is especially pointers and arrays that confuse me. I've read that they are not the same, but I have also found out that I can apparently not use empty arrays as a return for function, or as a parameter, instead I need to use pointers. So not like this:

class Test

{

  int[][] func(int[][] x)

  {

 

  }

}


but like this instead:


class Test

{

  int** func(int** x)

  {

 

  }

}


Also, from what I've understood so far, whenever I make a variable with some object type, like Tile t; it automatically creates an object of the Tile class using the default constructor. Therefore, if I have made any constructors, I also need to add the default constructor for it to work. Is that also correct?

Thanks to whomever will help me, in advance.

#2
WingedPanther

WingedPanther

    A spammer's worst nightmare

  • Moderators
  • 16,831 posts

ThemePark said:

I've been trying to learn some C++ for a project of mine, but it is giving me quite a headache.

I have a function where I want to initialize an int array, do some calculations on it and then return it, but I'm having a hard time figuring out exactly how to do it.

So far I have this (just a test function):
class Test

{

  int* func()

  {

     int x[1];


     x[0] = 2;


     return x;

  }

}

But I get the warning: "Address of local variable 'x' returned". I've been reading up on that and I have an idea why but I can't see how to return the array then. I've been looking through tutorials and books and searched on Google, but I can't find anything about returning arrays from functions.
Here's the problem, because x is a local variable, the space it occupies will be reclaimed and used for something else. Also, an array name is a pointer, so you are returning the address of a memory location that could be used for anything.

You need to declare the variable in the calling function and pass it as a parameter.

Quote


Another problem I'm having, is with assigning an object to an array. Say for example that I have a class called Tile, and I want an array of Tiles, run through the array and create each new tile with the constructor Tile(int x). That would give me something like this:

#include "Tile.hpp"


class Test

{

  void func()

  {

    Tile tst[1];


    tst[0] = Tile(5);

  }

}

But then I get the error: "expected unqualified-id at end of input".

I am rather confused as how to create an object in C++. I tried out the previous approach, and looking in a book, it said to use tst[0] (5). Btw, I'm using 2 lines in this case, because in reality I have my "Tile tst[1]" in my header file, and want to create the objects in my cpp file.
When you declare tst, each object in the array is created using the default constructor with no parameters. I would just create an initialization function and call it using tst[0].init(5);

Quote


Also, studying C++ and trying to fix previous errors, I have found out some things which I would like someone to tell me if I have understood them correctly. I am not certain I found the right way to do things.

It is especially pointers and arrays that confuse me. I've read that they are not the same, but I have also found out that I can apparently not use empty arrays as a return for function, or as a parameter, instead I need to use pointers. So not like this:

class Test

{

  int[][] func(int[][] x)

  {

 

  }

}


but like this instead:


class Test

{

  int** func(int** x)

  {

 

  }

}


An array such as int t[5] is NOT a pointer, however the name of the array, t, IS a pointer to the first element of the array. The difference is that if you try to increment t, you will get a compiler error, but if you declare a regular pointer and initialize it as t, you can increment the regular pointer.

Don't feel bad about being confused. LOTS of people find arrays and pointers confusing.

Quote


Also, from what I've understood so far, whenever I make a variable with some object type, like Tile t; it automatically creates an object of the Tile class using the default constructor. Therefore, if I have made any constructors, I also need to add the default constructor for it to work. Is that also correct?

Thanks to whomever will help me, in advance.

That is correct.
Programming is a branch of mathematics.
My CodeCall Blog | My Personal Blog

#3
CPD

CPD

    Learning Programmer

  • Members
  • PipPipPip
  • 57 posts

Quote

But I get the warning: "Address of local variable 'x' returned".
When you make an array, or any local variable in a function, that object is destroyed when the function returns. So if you return a pointer to that object, after the function returns, the pointer points to invalid memory. That's what the warning tells you.

If you want a function to return an array, the array has to exist outside of the function. In your class Test it might be like this:
class Test
{
  int x[1];
public:
  int* func()
  {
     x[0] = 2;

     return x;
  }
};
x is a part of a Test object, so basically as long as you can call the method on that object, the array will exist and can be returned. Other options are making the array static within func, or making the array global. A common way around this is to take the array as a parameter and not worry about returning it:
class Test
{
public:
  void func(int x[])
  {
     x[0] = 2;
  }
};
Arrays are passed by pointer, so you can change the elements and it'll still work as expected:
#include <iostream>

using namespace std;

class Test
{
public:
  void func(int x[])
  {
     x[0] = 2;
  }
};

int main()
{
  Test t;
  int a[1] = {5};

  cout << a[0] << '\n';
  t.func(a);
  cout << a[0] << '\n';
}

Quote

But then I get the error: "expected unqualified-id at end of input".
Assuming the Tile class has both a default constructor and a one argument constructor, the only error in that code is not terminating the class definition with a semicolon:
#include "Tile.hpp"

class Test
{
  void func()
  {
    Tile tst[1];

    tst[0] = Tile(5);
  }
};

Quote

looking in a book, it said to use tst[0] (5)
That'll give you a syntax error. You had it the right way in your example. You can also use the array initialization list:
class Test
{
  void func()
  {
    Tile tst[1] = {
      Tile(5)
    };
  }
};

Quote

I have also found out that I can apparently not use empty arrays as a return for function, or as a parameter, instead I need to use pointers.
You can't return an array in C++ period. The closest you can get is returning a pointer to the array:
#include <iostream>

using namespace std;

int array[5] = {0, 1, 2, 3, 4};

int (*function())[5]
{
  return &array;
}

int main()
{
  int (*a)[5] = function();
  int *b = *a;

  for (int i = 0; i < 5; i++)
    cout << b[i] << '\n';
}
This isn't a common idiom because it's easier to pass the array as a parameter instead of adding a level of indirection so that you can return an array.

You can also return an array by reference, and that drastically cleans up the syntax for using the return value, but the syntax for declaring the function is still obscure:
#include <iostream>

using namespace std;

int array[5] = {0, 1, 2, 3, 4};

int (&function())[5]
{
  return array;
}

int main()
{
  int *a = function();

  for (int i = 0; i < 5; i++)
    cout << a[i] << '\n';
}
When passing an array to a function, the first dimension turns into a pointer. An array of int turns into a pointer to int, an array of arrays of int--a 2D array--turns into a pointer to an array of int, an array of arrays of arrays of int--a 3D array--turns into a pointer to a 2D array. Any more than 3 dimensions and you might have a design problem. ;)

Here's those three in code:
const int MATS = 3;
const int ROWS = 3;
const int COLS = 3;

void function1(int *array1D);
void function2(int (*array2D)[COLS]);
void function3(int (*array3D)[ROWS][COLS]);

int main()
{
  int a[ROWS];
  int b[ROWS][COLS];
  int c[MATS][ROWS][COLS];

  function1(a);
  function2(b);
  function3(c);
}
But that's kind of hard to type, so C++ allows you to use a full array notation instead of manually converting the first dimension into a pointer. This is the exact same thing, just a different syntax:
const int MATS = 3;
const int ROWS = 3;
const int COLS = 3;

void function1(int array1D[]);
void function2(int array2D[][COLS]);
void function3(int array3D[][ROWS][COLS]);

int main()
{
  int a[ROWS];
  int b[ROWS][COLS];
  int c[MATS][ROWS][COLS];

  function1(a);
  function2(b);
  function3(c);
}
The size of the first dimension isn't needed because it's not used. Your compiler will just turn the second syntax into the first syntax and ignore any size you give for the first dimension.

Quote

Therefore, if I have made any constructors, I also need to add the default constructor for it to work. Is that also correct?
Yes, in particular when you create an array, all of the elements of that array are default constructed, so your class has to have a zero argument constructor.

#4
birinight

birinight

    Learning Programmer

  • Members
  • PipPipPip
  • 36 posts
When you declare the function test every variable initialized inside are temporary. They are destroid once you leave the function. So you cannot return a variable that no longer exists. To solve this problem you have two aproaches.

You can declare the array before you call the function and pass a function as an argument.

example:
class Test
{
	
	const int* func(const int *x)
	{
		x[0] = 2
		return x;
	}	
};

int main()
{
	Test t;
	int y[1];
	t.func(y);

	return 0;	
}

Or you can declare x inside the class
class Test
{
	int x[1]
	
	const int* func(void)
	{
		x[0] = 2
		return x;
	}	
};

int main()
{
	Test t;
	cout <<	t.func(); // and
	cout << t.x;
	return 0;	
}

The second question is that you are initialzing data with the wrong expression.
class Tile
{
private:
	Tile();
	int _x;
public:
	Tile(int x)
	{
		_x = x;
	}
		
};


class Test
{
  void func()
  {
	Tile tst[1] = { Tile(5) }; // The right way
  }
};

int main()
{
	
}


PS: Tip to the webmasters. You should have a warning system for threads that have been modified between posting as we can loose context or repeat ourselves.
Have a look at kpgen at sourceforge
Neither Emacs or Vi are my primary editors...
..and I'm not ashamed!!!

#5
ThemePark

ThemePark

    Programmer

  • Members
  • PipPipPipPip
  • 124 posts
This is why I signed up for this place, good help from nice, friendly, understanding people. :D I think I can get it all to work with all this advice, but I have a question about something CPD said.

He mentions making my array static or global to overcome one of my problems. How will it being static help me? As far as I know, all static does is to make the array class dependant so that all objects of a class will share the same array. And as for global, isn't that exactly what I'm doing by putting the array outside any functions but inside the class, like this:

class Test
{
  int x[1];

  int* func()
  {
     x[0] = 2;

     return x;
  }
}

Oh, and birinight, if your P.S. was because you all posted similar answers to me, I have to say I rather like that. Not only does it tell me for sure how to do things or how not to, and what's possible and impossible, but it will also give me a lot of ways of solving a problem since different people usually come up with different solutions, and so I have the luxury of choosing the solution I find to fit my code best.

#6
CPD

CPD

    Learning Programmer

  • Members
  • PipPipPip
  • 57 posts

Quote

How will it being static help me? As far as I know, all static does is to make the array class dependant so that all objects of a class will share the same array.
static is an overloaded keyword, it means different things in different places. The static I was talking about is a local static variable:

#include <iostream>


using namespace std;


int *function()

{

  static int a[] = {1, 2, 3, 4, 5, -1};

  return a;

}


int main()

{

  int *p = function();


  while (*p != -1)

    cout << *p++ << '\n';

}

When you make a local variable static, it says that the object will persist between calls of the function. This is a way to get around the problem of the array being destroyed when the function returns. If the array is required to persist between calls, you can safely return a pointer to it.

Quote

And as for global, isn't that exactly what I'm doing by putting the array outside any functions but inside the class
It's the same idea, but having a static or non-static member of the class instead of a global variable is a better design. Global variables are immediately useful because they're visible everywhere, but maintaining them and troubleshooting problems that involve them are more difficult for the same reason.

#7
WingedPanther

WingedPanther

    A spammer's worst nightmare

  • Moderators
  • 16,831 posts
A static variable is a variable that doesn't have its memory reclaimed/overwritten when it goes out of scope. As a result, for example, you can have a static variable in a function that increments each time you enter so you can keep track of how many times the function was called.
Programming is a branch of mathematics.
My CodeCall Blog | My Personal Blog

#8
lucuis

lucuis

    Newbie

  • Members
  • PipPip
  • 21 posts
I think, that the best solution is to use dynamic allocation:

class Test
{
  int* func()
  {
     int x = new int[1];

     x[0] = 2;

     return x;
  }
}

But don't forget to free the memory outside the function.

#9
CPD

CPD

    Learning Programmer

  • Members
  • PipPipPip
  • 57 posts

Quote

But don't forget to free the memory outside the function.
That's why it's not necessarily the best solution. If you write a function that allocates memory and returns a pointer to it, you can't expect the caller to free that memory. This solution is convenient for you, but it's a high probability memory leak waiting to happen.

The accepted "best" solution in general is requiring the caller to provide an array and then you work with it:
class Test
{
  int* func(int *x)
  {
     x[0] = 2;

     return x;
  }
}
This is better because if the caller has to allocate memory, there's a better chance that the caller will also remember to free it.

#10
lucuis

lucuis

    Newbie

  • Members
  • PipPip
  • 21 posts
I do disagree. It's cool, if the caller knows how much memory the function needs, but in some cases It can break good OOP design, if we are talking about C++.

#11
CPD

CPD

    Learning Programmer

  • Members
  • PipPipPip
  • 57 posts

Quote

but in some cases It can break good OOP design
If you want to talk about good OOP design, then the memory should be wrapped in an object so the destructor will free the memory without relying on an addlebrained caller.

#12
lucuis

lucuis

    Newbie

  • Members
  • PipPip
  • 21 posts

CPD said:

If you want to talk about good OOP design, then the memory should be wrapped in an object so the destructor will free the memory without relying on an addlebrained caller.

It's the truth ;-)