21

I want to pass an array to a constructor, but only the first value is passed--the rest looks like garbage.

Here's a simplified version of what I'm working on:

#include <iostream>

class board
{
    public:
        int state[64];
        board(int arr[])
        {
            *state = *arr;
        }
        void print();
};

void board::print()
{
    for (int y=0; y<8; y++)
    {
        for (int x=0; x<8; x++)
            std::cout << state[x + y*8] << " ";
        std::cout << "\n";
    }
}

int main()
{
    int test[64] = {
        0, 1, 2, 3, 4, 5, 6, 7,
        1, 2, 3, 4, 5, 6, 7, 8,
        2, 3, 4, 5, 6, 7, 8, 9,
        3, 4, 5, 6, 7, 8, 9,10,
        4, 5, 6, 7, 8, 9,10,11,
        5, 6, 7, 8, 9,10,11,12,
        6, 7, 8, 9,10,11,12,13,
        7, 8, 9,10,11,12,13,14 };

    board b(test);
    b.print();

    std::cin.get();
    return 0;
}

Can someone explain why this doesn't work and how to properly pass an array? Also, I don't want to copy the array. (And do I really have to indent every line by 4 spaces for code? That's pretty tedious.)

Svad Histhana
  • 469
  • 2
  • 7
  • 13

6 Answers6

10

In this case it might be best to use a reference to the array:

class board
{
    int (&state)[64];

public:
    board(int (&arr)[64]) 
        : state(arr)
    {}

    // initialize use a pointer to an array
    board(int (*p)[64]) 
        : state(*p)
    {}


    void print();
};

A couple of advantages - no copying of the array, and the compiler will enforce that the correct size array is passed in.

The drawbacks are that the array you initialize the board object with needs to live at least as long as the object and any changes made to the array outside of the object are 'reflected' into the object's state. but those drawbacks occur if you use a pointer to the original array as well (basically, only copying the array will eliminate those drawbacks).

One additional drawback is that you can't create the object using a pointer to an array element (which is what array function parameters 'decay' to if the array size isn't provided in the parameter's declaration). For example, if the array is passed through a function parameter that's really a pointer, and you want that function to be able to create a board object referring to that array.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • This works! Thank you. How does this compare with vvnraman's solution? Also, can you explain what `: state(arr)` does? – Svad Histhana Feb 24 '12 at 08:16
  • `state(arr)` initializes the reference to the array that's passed as the constructor parameter. – Michael Burr Feb 24 '12 at 08:21
  • Can someone explain why the array is bound to `test` and is thus destroyed when it goes out of scope? If two variables are pointing to the same array, why does one have precedence over the other? – Svad Histhana Feb 24 '12 at 08:42
  • 1
    @Svad: the reference is just an alias for the object it's initialized with. The reference has no effect on that object's lifetime. If you need the `board` object to live longer than the array used to initialize it, then I'd suggest copying the array (which you indicated several times you didn't want to do). An alternative might be to use `shared_ptr` to manage the array's lifetime; for that you'd need to create the array in dynamic memory somehow. – Michael Burr Feb 24 '12 at 08:55
  • I don't want to copy because I plan to make a very large number of board objects, and copying an array every time would be costly. So it seems I need to allocate memory. Avram mentioned `new` in another response. I'll also look into `shared_ptr`. Thanks for the explanation and advice. – Svad Histhana Feb 24 '12 at 09:06
8

Attempting to pass an array to a function results in passing a pointer to the first element of the array.

You can't assign arrays, and taking a parameter like T[] is the same as T*. So

*state = *arr;

Is dereferencing the pointers to state and arr and assigning the first element of arr to the first element of state.

If what you want to do is copy the values from one array to another, you can use std::copy:

std::copy(arr, arr + 64, state); // this assumes that the array size will
                                 // ALWAYS be 64

Alternatively, you should look at std::array<int>, which behaves exactly like you were assuming arrays behave:

#include <array>
#include <algorithm>
#include <iostream> 

class board
{
    public:
        std::array<int, 64> state;

        board(const std::array<int, 64> arr) // or initialiser list : state(arr)
        {
            state = arr; // we can assign std::arrays
        }
        void print();
};

void board::print()
{
    for (int y=0; y<8; y++)
    {
        for (int x=0; x<8; x++)
            std::cout << state[x + y*8] << " ";
        std::cout << "\n";
    }
}

int main()
{
    // using this array to initialise the std::array 'test' below
    int arr[] = {
        0, 1, 2, 3, 4, 5, 6, 7,
        1, 2, 3, 4, 5, 6, 7, 8,
        2, 3, 4, 5, 6, 7, 8, 9,
        3, 4, 5, 6, 7, 8, 9,10,
        4, 5, 6, 7, 8, 9,10,11,
        5, 6, 7, 8, 9,10,11,12,
        6, 7, 8, 9,10,11,12,13,
        7, 8, 9,10,11,12,13,14 };

    std::array<int, 64> test(std::begin(arr), std::end(arr));

    board b(test);
    b.print();

    std::cin.get();
    return 0;
}
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • Thank you. That explains why I'm only getting the first value. I don't want to copy the array, however. Nor do I want to use vectors. Surely there's a way to properly pass an array? – Svad Histhana Feb 24 '12 at 07:41
  • 3
    @SvadHisthana nope, you cannot pass a real array by value in C++. You can pass an array by reference though (and that only helps to figure out the size of it) but that's it. – Seth Carnegie Feb 24 '12 at 07:42
  • I wasn't intending to pass by value. vvnraman's solution is what I was going for. Sorry if I didn't phrase my question better. Thanks for your help! – Svad Histhana Feb 24 '12 at 07:58
  • @SethCarnegie: You have reversed the `std::copy` arguments. They are: `std::copy(first, last, output)`. – Christian Ammer Feb 24 '12 at 08:00
  • @ChristianAmmer thanks, fixed. Feel free to edit my posts for silly mistakes like that. – Seth Carnegie Feb 24 '12 at 08:03
  • Do note that you can get rid of `arr` in your code. Since `std::array` is an aggregate you can initialize it like you do a regular array. This cuts out a step. – NathanOliver Oct 10 '16 at 18:06
1
#include <iostream>

class board
{
    public:
        int * state;    //changed here, you can also use **state
        board(int *arr)               //changed here
        {
          state = arr;
        }
        void print();
};

void board::print()
{
    for (int y=0; y<8; y++)
    {
        for (int x=0; x<8; x++)
            std::cout << *(state + x + y*8) << " ";   //changed here
        std::cout << "\n";
    }
}

int main()
{
    int test[64] = {
        0, 1, 2, 3, 4, 5, 6, 7,
        1, 2, 3, 4, 5, 6, 7, 8,
        2, 3, 4, 5, 6, 7, 8, 9,
        3, 4, 5, 6, 7, 8, 9,10,
        4, 5, 6, 7, 8, 9,10,11,
        5, 6, 7, 8, 9,10,11,12,
        6, 7, 8, 9,10,11,12,13,
        7, 8, 9,10,11,12,13,14 };

    board b(test);
    b.print();

    std::cin.get();
    return 0;
}

or you can use it as:

class board
{
    public:
        int state[64];
        board(int arr[])
        {
            for(int i=0;i<64;++i)
               state[i] = arr[i];
        }
        void print();
};

EDIT 1: stable solution

class board
    {
        public:
            int * state;    //changed here, you can also use **state
            board(int *arr)               //changed here
            {
              state = new int[64];
              for(int i=0;i<64;++i)
                   state[i] = arr[i];
            }
            void print();
    };
Rohit Vipin Mathews
  • 11,629
  • 15
  • 57
  • 112
  • Thank you. Though when I tried compiling your first solution, I received an error for line 6: "only static const integral data members can be initialized within a class" vvnraman's solution seems to be working. – Svad Histhana Feb 24 '12 at 07:54
  • which is the line? i cant find any assignment of class members outside constructor – Rohit Vipin Mathews Feb 24 '12 at 07:56
  • 1
    Your first example doesn't compile. You can't initialize a member outside a function using new(). A second issue is that the constructor doesn't copy the contents of the array into the allocated block of memory. It just re-assigns the pointer to point to the array passed into the constructor. This would work in your example code, but in a real program, that memory would become invalid as soon as the array test goes out of scope. The memory allocated and assigned to state would just be leaked. – Al Riddoch Feb 24 '12 at 07:56
  • oh sorry i didnt see it was outside constructor. yeah you can assign memory inside the constructor and copy array contents to avoid out of scope issues. – Rohit Vipin Mathews Feb 24 '12 at 08:00
0

*state = *arr; is using dereferencing, which returns the value at the address of the pointer.

This is the same as state[0] = *arr; because *arr is an int.

See this article for info on pointers. See the deference section.

To solve this problem you want to do this:

for (int i = 0; i < 64; i++) state[i] = arr[i]
Avi
  • 1,231
  • 10
  • 23
  • Thank you. However, my program won't compile if I try to change `*state = *arr;` to `state = arr;` I get an error for state saying "expression must be a modifiable lvalue". – Svad Histhana Feb 24 '12 at 07:37
  • @SvadHisthana See the last line, it will do it for you – Avi Feb 24 '12 at 07:44
  • Thank you, but I don't want to copy the array. vvnraman's solution seems to be working for me. – Svad Histhana Feb 24 '12 at 07:56
  • But you might have scope problems in a bigger program. ie if you orginal array goes out of scope – Rohit Vipin Mathews Feb 24 '12 at 08:01
  • Beware. That solution will work in this case. However, test is a local variable and a pointer to it won't be valid after the function returns. Of course when main returns the program is done, but you can run into trouble if test wasn't from main. It's a poor design decision to allow this to happen. – Avi Feb 24 '12 at 08:02
  • @SvadHisthana Just be careful of the drawbacks if you just copy the pointer, as commented by Seth Carnegie on vvnraman's answer. – Some programmer dude Feb 24 '12 at 08:16
  • @Avram: Is there any way to keep the array alive independently of `test` without copying it? I guess I don't understand why the array is destroyed when `test` goes out of scope even if another variable is pointing to the array. Can I just make `test` point to something else, or does that affect `state`? – Svad Histhana Feb 24 '12 at 08:23
  • Short answer, no. The thing with a pointer is that it is little more than an int and its value is a memory address. So just like a variable int i in a function is gone when that function ends, so to test is gone when main ends. And just because some pointer has it's address stored test isn't saved any more than i is. If you want to not have it go, then new/delete are the keywords you need to become familiar with. – Avi Feb 24 '12 at 08:35
  • Thank you for explaining this. I'll look into new and delete. – Svad Histhana Feb 24 '12 at 08:44
0

The name of an array is the address of the first element in it.

Hence the line *state = *arr will set state[0] to arr[0].

Since right now you have defined state as int state[64];, state is const pointer of type int whose address cannot be changed.

You can change it to int *state; and then state = arr will work.

vvnraman
  • 1,322
  • 13
  • 21
  • I tried your suggestion and got the error "expression must be a modifiable lvalue" for state. – Svad Histhana Feb 24 '12 at 07:34
  • @SvadHisthana I updated the answer as there was a bug in the previous one. I didn't notice that `state` is declared as `int []` which makes it a `const pointer` and hence its address cannot be changed. – vvnraman Feb 24 '12 at 07:39
  • 2
    @SvadHisthana remember though that if you do it that way, `state` will point to the same array as `arr` and any changes you make to one will be made to the other. Also if `arr` goes out of scope, `state` will point to a destroyed array – Seth Carnegie Feb 24 '12 at 07:51
  • Good to know. That was the desired behavior. However, is there any way to preserve `state` in case `arr` does go out of scope? – Svad Histhana Feb 24 '12 at 08:06
  • To preserve `state`, you would need to allocate memory for it (`state = new int[64];` and then copy the contents of `arr` to `state` (`std::copy(arr, arr + 64, state);` ). Of course if you are going to do this, you would have to implement the [Rule of Three](http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three), as now state is a resource. – vvnraman Feb 24 '12 at 08:16
  • So there's no way to preserve `state` without copying the array since the original array declaration is bound to `test`? I just started learning C++ yesterday, so forgive my naivete. ;) – Svad Histhana Feb 24 '12 at 08:28
0

*arr gives the value that is stored at arr[0] . In c++ , the name of the array is a pointer to the first element in the array.

So when you do *state = *arr , you store the value at arr[0] in the variable state.

Now , if you want to pass the array without having to copy each element explicitly , I suggest that you make another array of the same size in the method which you are calling and then pass the name of the array from the caller , in essence :

methodWhereArrayisPassed(int *arrayName)
{
    int arrCopy[64];
    arrCopy = arrayName;

// Do more stuff here
}

methodWhichPassesArray()
{
    // do stuff here
    int arr[] = {
       0, 1, 2, 3, 4, 5, 6, 7,
       1, 2, 3, 4, 5, 6, 7, 8,
       2, 3, 4, 5, 6, 7, 8, 9,
       3, 4, 5, 6, 7, 8, 9,10,
       4, 5, 6, 7, 8, 9,10,11,
       5, 6, 7, 8, 9,10,11,12,
       6, 7, 8, 9,10,11,12,13,
       7, 8, 9,10,11,12,13,14 };

methodWhereArrayisPassed(arr);

// do stuff here
}
angryInsomniac
  • 829
  • 2
  • 13
  • 27