0

How should I return an array from a function? My code is

float ClassArray::arr_sub(float a[100][100], float b[100][100]) {
    int i,j;
    for(i = 1; i < 10; i++) {
        for(j = 1; j < 10; j++){
            f[i][j]=b[i][j]-a[i][j];
        }
    }
    return f;
}

and the f returned from this function should be assigned to another array g declared in some other class.

float g[100][100];
g= cm.arr_sub(T,W);

but while building the classes, it says incompatible type assignment of float to float[100][100].

Tim
  • 35,413
  • 11
  • 95
  • 121
user1322915
  • 1,211
  • 2
  • 13
  • 10
  • 1
    I notice that the arrays are 100x100 but you have i and j only going up to 10. This means you're only working on elements in the first ten rows and first ten columns. It's not really relevant to your question, but if your actual program does that and it's not just a typo then it's something you should fix in your program. – bames53 Apr 22 '12 at 02:26

4 Answers4

11

My answer here to another question on arrays explains why you don't want to use arrays.

As I say in that answer you can't assign an array like you're trying:

float g[100];
g = foo(); // illegal, assigning to arrays is not allowed

Another of the weird restrictions on arrays is that you're not allowed to return them from functions:

float foo()[100]; // illegal, returning an array from a function is not allowed

Also note that when you declare a function like float arr_sub(float a[100][100]) you might think you're passing an array by value, but in fact that invokes another of the weird exceptions made for arrays. In C and C++, whenever you declare a formal parameter of a function to be an array, the type is adjusted from 'array' to 'pointer to the array's element type'.


Since arrays don't behave like they ought, you should instead use std::array or std::vector:

std::array<float,100> foo(); // works

std::array<float,100> g;
g = foo(); // works

To do multi-dimentional arrays you can use:

std::array<std::array<float,100>,100> g;

Though that's a bit cumbersome so you can typedef it:

typedef std::array<std::array<float,100>,100> Matrix;

Matrix ClassArray::arr_sub(Matrix a, Matrix b) {
    ...
}

Matrix g;
g = cm.arr_sub(T,W);

And if you have a compiler that supports C++11 you can even do a template type alias:

template<typename T,int Rows,int Columns>
using Matrix2d = std::array<std::array<T,Columns>,Rows>;

Matrix2d<float,100,100> g;

Note on performance

There is one reason you might not want to return an std::array by value. If the array is large then there may be a signficant performance cost in copying the data from the return value into the variable you assign it to. If that ever proves to be a problem for you, then the solution with std::array is the same as it would be for other large types; use an 'out' parameter instead of returning by value.

void arr_sub(Matrix a, Matrix b, Matrix &result);

Matrix g;
arr_sub(T,W,g);

This doesn't apply to std::vector because std::vector can take advantage of move semantics to avoid having to copy all its elements.

Community
  • 1
  • 1
bames53
  • 86,085
  • 15
  • 179
  • 244
2

If you insist on using "plain C" 2D arrays, the best thing is to pass a pointer to the result along with the two input parameters, rather than passing the arrays by value the way you did.

However, the best thing to do in C++ is to use vector<vector<float> > instead, and pass it by reference.

void ClassArray::arr_sub(
    const vector<vector<float> > &a
,   const vector<vector<float> > &b
,   vector<vector<float> > &res)
{
    for(int i=0 ; i != a.size() ; i++)
        for(int j=0 ; j != b.size() ; j++)
            res[i][j] = b[i][j] - a[i][j];
}

void main() {
    vector<vector<float> > a(100, vector<float>(100, 12.345));
    vector<vector<float> > b(100, vector<float>(100, 98.765));
    vector<vector<float> > res(100, vector<float>(100, 0));
    arr_sub(a, b, res);
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • -1: Statically-sized arrays and dynamically-sized vectors are different tools for different jobs, and `vector>` is not necessarily the best way to obtain two dimensions. – Potatoswatter Apr 22 '12 at 02:17
  • 1
    @Potatoswatter They are "different tools", allright, but one of these tools can be used in place of the other under nearly all circumstances without noticeable loss of performance, and with noticeable gain in readability. I always go for a vector first, and switch to a fixed multi-dimension array only after a round of heavy profiling. I've seen a few cases when I needed to switch to a fixed 2D array for optimization, but the one described in the OP does not look like one of these cases. Stack overflow is about promoting best practices, and choosing vectors over plain arrays is one of them. – Sergey Kalinichenko Apr 22 '12 at 02:31
  • Using `std::array` instead of a C array in a matrix library is a good idea. Using `std::vector` for fixed-size matrices is a bad idea. Are you claiming that `std::vector` produces more readable code than `std::array`? – Potatoswatter Apr 23 '12 at 07:05
  • @Potatoswatter How could I claim anything about `std::array` without mentioning it even once? It should be plain obvious that I was comparing the flexibility OP's `float g[100][100]` to `std::vector >`, that's all. – Sergey Kalinichenko Apr 23 '12 at 10:57
  • Well, like I said, two different tools for two different jobs. On the other hand `std::array` is a drop-in replacement which provides all the benefits of `std::vector` in this context without the associated costs. You said "Stack overflow is about promoting best practices, and choosing vectors over plain arrays is one of them" and `std::array` *is a plain array*. – Potatoswatter Apr 23 '12 at 13:25
  • @Potatoswatter If you make no difference between a plain array and an elaborately constructed, 500-line long, template cover for it, further argument makes no sense: you are finding an excuse to defend your position, which is, quite frankly, indefensible. – Sergey Kalinichenko Apr 23 '12 at 13:52
  • The implementation is irrelevant. (Although, if some implementation is 500 lines, that's all comments and one-line functions. It's still dead simple.) `std::array` is what you use when you need a plain array. It is a plain array. Yes, we can forget about C arrays entirely. No, `vector` has never been and never will be a replacement for plain arrays, especially for small matrices as in the OP illustration. – Potatoswatter Apr 23 '12 at 14:09
  • @Potatoswatter 10,000 element matrix is no *small* by any measure. Considering that `std::array` did not make it into the standard prior to C++11, your statement about "has never been" is more than questionable. – Sergey Kalinichenko Apr 23 '12 at 14:12
  • 10000 will still fit on the stack, although you want to avoid copies. As bames pointed out they may actually be 100 elements. `std::array` was previously Boost array and `std::tr1::array`, and in any case you get the essential benefits from `template< typename T, size_t N > struct my_array { T elements[ N ]; };` — the rest is just sugar. On the other hand going to the heap N+1 times for every NxN matrix is also more than questionable. – Potatoswatter Apr 23 '12 at 14:18
  • @Potatoswatter How's bames53's answer have any relevance to this discussion? If it is better in your opinion, go ahead and vote for it. It does not invalidate the fact that my answer is 100% working, 100% relevant, and 100% correct. – Sergey Kalinichenko Apr 23 '12 at 14:30
  • Not his answer, his comment below the question. – Potatoswatter Apr 23 '12 at 14:31
0

The best way to do this is to wrap everything into a class. From the look of things, its a Matrix.

There are probably a hundred Matrix classes out there already, so it is really pointless to write another one.

But, if this is a learning exercise it might be worthwhile.

To answer your asked question, make a third argument to your function: float result[100][100]. Inside your function, write the results into the result array.

This works because in C and C++, arrays are always passed by reference and never by value. This is because C++ passes only the pointer to the beginning of the array.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
  • Better to explicitly pass an array reference `float (&result)[100][100]` than to use a bare array type that will decay to the confusing `float (*result)[100]`. – Potatoswatter Apr 22 '12 at 02:19
0

if you really wish to return an array and some how manage to use it in the main(), the most efficient way would be to declare the returning array as dynamic. that way you will avoid losing the pointer to this new array as it will be allocated in heap and not in stack.

Amit Vig
  • 175
  • 2
  • 12