3

I am working with C++ to construct an array, which is then passed into Python (similar to: Embed python / numpy in C++). I am new to C++ and I am confused with some of the details of the code. I'm hoping I can get an understanding of how this code works, because I need to change it. So my question is: what is this method of initializing an array?

const int SIZE{ 10 };
double(*c_arr)[SIZE]{ new double[SIZE][SIZE] };

For the record, I've been able to make this a rectangular array by calling:

const int numberRows = 5000;
const int numberColumns = 500;
double(*c_arr)[numberColumns]{ new double[numberRows][numberColumns] };

I fill the array:

// fill the array from a file
std::string line;
int row = 0;
int column = 0;
while (std::getline(dataFile, line)) {
    std::stringstream lineStream(line);
    std::string cell;
    while (std::getline(lineStream, cell, '\t')) {
        c_arr[row][column] = std::stod(cell);
        column++;
    }
    row++;
    column = 0;
    if (row == numberRows) {
        break;
    }
}

I still don't understand what is meant by double(*c_arr). Whenever I try to initialize this array differently, I get errors. For example: double *c_arr[numberRows][numberColumns]; raises errors when I try to fill the array c_arr[row][column] = std::stod(cell);. If I change the initialization to be: double c_arr[numberRows][numberColumns];, then I get a segmentation fault when run. What I'd like to eventually achieve is a function that returns a pointer to an array; something like:

double *load_data(int rows, int columns) {
    double(*c_arr)[columns]{ new double[rows][columns] };
    //fill array here
    return (c_arr)
}

When I construct such a function, I get an error at the second occurrence of the columns variable: expression must have a constant value -- the value of parameter "columns" cannot be used as a constant. I don't really how to do this, but I'm hoping that if I can understand the array initialization, I'll be able to properly construct the load_data function.

CopyOfA
  • 767
  • 5
  • 19
  • A pointer-to-array (`int (*p)[5]`) is different from an array-of-pointers (`int *p[5]`). I think this is the source of your confusion. – chi Nov 17 '21 at 16:14
  • Ahh, that makes a little more sense, then. So, the parenthese around a pointer implies the pointer is pointing to the object? Something like this...? – CopyOfA Nov 17 '21 at 16:28
  • It's the declaration syntax for variables. In complex cases, it's notoriously tricky, since we write something before the name (`p`), something after, and the order is not trivial to understand. Parentheses are needed in some cases to force the wanted type, as in the example I made above. `int (*p)[5]` means p is a pointer to an array of 5 ints. `int *p[5]` instead parses as `int *(p[5])` which is an array of 5 pointers to int. – chi Nov 17 '21 at 18:07

2 Answers2

2

what is this method of initializing an array?

const int SIZE{ 10 };
double(*c_arr)[SIZE]{ new double[SIZE][SIZE] };

c_arr is a pointer to double[SIZE] i.e. pointer to array of doubles.

new double[SIZE][SIZE] allocates a default initialised dynamic array of double[SIZE]. The result is a pointer to the first element. That pointer is used to initialise c_arr.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Thank you! More questions: When I initialize a rectangular array, I initialize `double(*c_arr)[numberColumns]{ new double[numberRows][numberColumns] }`. Why do I declare `numberColumns` to be the size of the dynamic array? Also, why can I not initialize the array pointer as `double *c_arr[numberRows][numberColumns]`? – CopyOfA Nov 17 '21 at 15:59
  • @CopyOfA That's not what you did. The size of the dynamic array is `numberRows`. You can initialise an array of pointers. – eerorika Nov 17 '21 at 16:13
  • I'm a little confused. I thought the size of the array was `numberColumns`, since that is the first call (`double(*c_arr)[numberColumns]`). – CopyOfA Nov 17 '21 at 16:45
  • @CopyOfA The size of the dynamic array is `numberRows`. The elements of the dynamic array are arrays whose size is `numberColumns`. `c_arr` points to the fist element. – eerorika Nov 17 '21 at 16:59
2
double(*c_arr)[SIZE]{ new double[SIZE][SIZE] };

The above is/can be read as:

c_arr is a pointer to an array of size SIZE. Next, new double[SIZE][SIZE] creates a 2D array and also returns a pointer to its first element(which is also an array of double). Next, the pointer c_arr is initialized with the pointer returned in the last step from new double[SIZE][SIZE];

Now lets take a look at:

double *c_arr[numberRows][numberColumns];

In this case, c_arr is a 2D array of pointers to double. And so you cannot initialize this 2D array of pointers with a single pointer like:

 double d=15; 
 double *c_arr[5][6]=&d ;//incorrect

Now coming to your error:

When I construct such a function, I get an error at the second occurrence of the columns variable: expression must have a constant value -- the value of parameter "columns" cannot be used as a constant

This is because, in C++ the size of an array must be a compile time constant(constant expression). So for example,

int n = 10;
double arr[n]; //incorrect

The correct way would be:

const int n = 10;
double arr[n];//correct

So in your code the variable named columns is not a constant expression which is the reason you get the above mentioned error.

To correctly return a pointer to an array, you can modify your load_data function to look like:

auto load_data(int rows, int columns) -> double (*)[5]{

    double(*c_arr)[5]{ new double[4][5] };
    
    //fill array here
    return c_arr;
}

In the above modification of load_data, instead of using the numbers 4 and 5 you can use other constant expressions.

Jason
  • 36,170
  • 5
  • 26
  • 60
  • Thank you for this. Per the error, how can I pass such an argument into an array-construction function? Should it be `double *load_data(int rows, const int columns) {...}`? Also, I get an error on the return type if I initialize `c_arr` as `double(*c_arr)[numberCols]{ new double[numberRows][numberCols] };`. I thought I was returning a pointer, so this should work...? – CopyOfA Nov 17 '21 at 16:37
  • @CopyOfA For returning a pointer through the `load_data` function you can use the method that i have added at the end of my answer. Check it out. Now the function `load_data` returns a pointer to an array of size 5 of `double`. Note again that i have given the example for particular numbers like 4 and 5. If you have some constant expressions then you can use those instead of the numbers 4 and 5. You should instead use dynamic sized containers like `std::vector`. – Jason Nov 17 '21 at 17:07
  • It doesn't appear that you use the function arguments `rows` or `columns`. How can I use function arguments to construct the array pointer? Is this not possible? – CopyOfA Nov 17 '21 at 17:14
  • @CopyOfA Using the arguments `rows` and `columns` is **not possible** since they both are not constant expression. On the other hand if you use `std::vector` then you can avoid these problems altogether. – Jason Nov 17 '21 at 17:15
  • Hmmm... So, the array size has to be hard-coded? – CopyOfA Nov 17 '21 at 17:25
  • 1
    @CopyOfA Basically yes **if** you use arrays. On the other hand if you use `std::vector` then you can use the passed arguments like `rows` and `columns`. – Jason Nov 17 '21 at 17:28
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/239327/discussion-between-copyofa-and-anoop-rana). – CopyOfA Nov 17 '21 at 17:33