Josue M
Josue M

Reputation: 23

How to you copy elements from a 2D array into a 1D array using pointers C++

I have a function

double* get_row(double *the_array, int row_num, int col_size)    

where the_array is a 2D array, row_num is the row number on the 2D array where i want to copy the values from, and col_size is the column size of the 2D array.

Function Implementation

double* get_row(double *the_array, int row_num, int col_size) {

    double* row = new double[col_size];

    for (int i = 0; i < col_size; i++) {

        *row = *(*(the_array + row_num) + i);
        row++;
    }

    delete[] row;

    return row;
}

im trying to copy values from row row_num of the 2D array the_array into the 1D array row but the compiler gives me an error on the line *row = *(*(the_array + row_num) + i); which i used to copy elements from one array to the other, what is the correct way to complete such task.

BTW the error im getting from visual studio is : operand of '*' must be a pointer.

thank you for your help.

Upvotes: 1

Views: 880

Answers (4)

Geezer
Geezer

Reputation: 5730

This is not modern-day C++, and obviously you should not be deleting your return value.

im trying to copy values from row row_num of the 2D array the_array into the 1D array

First off, the_array in this context, being defined as a raw poiner and used for pointer arithmetic, is a 1D representation of a 2D array -- better be accurate in our description.

the error im getting [...] is : operand of '*' must be a pointer

Ok, so this line:

*row = *(*(the_array + row_num) + i);

is kind of a mess. I think I see where you were going with it, but look, you're dereferencing (using * to the left of an expression) twice for no reason, which BTW causes the error. Remember we said the_array is a raw pointer, being treated as a 1D array. Hence, you can use this type of pointer arithmetic, let's say inside the parenthesis, and then dereference it just once. Your expression above takes the resultant address of (the_array + row_num), dereferences it to get the double within this cell of the array, and then adds i to the double value itself and then tries to dereference this sum of a double and int i -- which is a temporary variable of type double, not much of a pointer. This here is probably the line you were aiming for:

*row = *(the_array + row_num * col_size + i);

Because the 2D data is spread contagiously in memory, row by row, consecutively, you need to multiply the row index like this by the row size (which is also the column count, and I take it that col_size is actually that, otherwise you have no way of traversing between rows) to "skip between the rows" and finally add the current cell index i to get to the specific cell inside the row. Then, like we said, dereferene the whole thing.

Beyond the compilation issue and address calculation, you should at least keep address of row before incrementing it in the loop so you could be returning it and not the pointer past-the-end of the row. In keeping as much as possible with your original function, you can do it using a second pointer like this:

double* get_row(double *the_array, int row_num, int col_size) {

    double* row = new double[col_size];
    double* rowPtr = row;

    for (int i = 0; i < col_size; i++) {
        *rowPtr = *(the_array + row_num * col_size + i);
        rowPtr++;
    }

    return row;
}

I'm trying to keep this as closest as possible to your version, so you could see the difference from what you did. Many things are better done differently, for example why not use the [] operator in the first place like this row[i] = *(the_array + row_num * col_size + i); and then get rid of the second pointer altogether? No real need for doing ++ there as a second line in the loop if you're iterating over i as it is. Another example is did you have to allocate or could you just return a pointer to the existing array but in the right place?

Too important to not mention here is that in almost any scenario you really shouldn't be returning a raw pointer like this, as it could too easily lead to unclear intent in the code, as to who owns the allocation (and is responsible to delete it eventually) and raises the issue of how to prevent memory-leak in case of an exception? The preferred solution is to be using smart pointers, such as std::unique_ptr, an instance of which wraps the raw pointer and makes it so you're going to have to be very explicit if you'll want to just throw that piece of memory around in an unsafe way.

Upvotes: 0

Andy Tan
Andy Tan

Reputation: 58

If get_row doesn't require you to allocate new memory back to the caller, then the solution would simply be

double* get_row(double *the_array, int row_num, int col_size) 
{
    return the_array + (row_num * col_size);
}

If the caller is expected to allocate new memory to the returned row, then

double* get_row(double *the_array, int row_num, int col_size) 
{
    double* row = new double[col_size];
    for(int i = 0; i < col_size; i++)
        row[i] = *(the_array + (row_num * col_size) + i);
    return row;
}

would work.

In the first solution, the idea is to use row_num * col_size to get to the correct row and simply return that address location as the result. In the second solution, we first declare a new row. And then use i to index each individual element in that row and copy each element over from the_array at that row indexed by the_array + (row_num * col_size).

Upvotes: 1

J. Willus
J. Willus

Reputation: 577

double* get_row(double *the_array, int row_num, int col_size) {
    double* row = new double[col_size];

    for (auto i = 0; i < col_size; ++i) {
        row[i] = the_array[row_num * col_size + i];
    }

    // If the return isn't assigned, the program will leak!
    return row;
}

void row_test() {
    double two_d[15];

    const auto row = get_row(two_d, 2, 5);

    // ...
    // Be sure to delete row afterwards
    delete[] row;
}

Upvotes: 0

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275906

I'll assume "col_size" is actually "number of columns" in order to solve this problem. And that the data is arranged in row-major order.

Then the start of row N is located at the_array + row_num*col_size.

I'd start with this helper function:

inline double* view_row_n( double* array, int row, int num_cols ) {
  return array+row*num_cols;
}

then I'd write a copy-buffer:

 std::unique_ptr<double[]> copy_buffer( double const* in, int count ) {
   double* retval = new double[count];
   std::memcpy( retval, in, sizeof(double)*count );
   return std::unique_ptr<double[]>( retval );
 }

now I'd use them together:

double* get_row( double* the_array, int row_num, int col_size) {
  auto retval = copy_buffer( view_row_n( the_array, row_num, col_size), col_size );
  return retval.release();
}

and be done with it.

I'd also change:

double* get_row( double const* the_array, int row_num, int col_size) {

and even:

std::unique_ptr<double[]> get_row( double const* the_array, int row_num, int col_size) {

but that is beyond the scope of your problem.

Upvotes: 1

Related Questions