Josiah Holmes
Josiah Holmes

Reputation: 69

How to access the elements in a 2D array through a pointer

I understand how to access elements in a 2D array by pointer, but I'm having a bit of trouble accessing the second "element" in an array row and using it to make comparisons. For example, if I had the array:

int numbers[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

I would need to access the elements 2, 5, and 8 for another function. But when I declare a pointer and point it to the next element in an array, like so:

int (*p)[3] = &(numbers[1]);

or

int (*p)[3] = numbers + 1;

It points to the next element, per se, being the next row in the array, since it's a 2D array. That I understand, but I was wondering if there was anything that could be done so that the pointer points to the next "element" in the current row, instead of pointing to the very next row?

Upvotes: 3

Views: 7218

Answers (5)

yMor
yMor

Reputation: 5

To access the values of the second column by pointer you can use a pointer to the first element of the array and a formula *(row*numberofelementsinarow+(pointer+column) to access whatever you want inside the array changing only the row and column you want in that formula:

const int rows=3;
const int columns=3;
int numbers[rows][columns] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
int * p_numbers;
p_numbers=&numbers[0][0];
for (int i=0; i<=rows-1; i++) {std::cout << *(i*columns+(p_numbers+1)) <<"\n";};

The output is: 2 5 8

Upvotes: 1

vcp
vcp

Reputation: 962

numbers is 2D array of integers, which means it is 1D array of 3 1D sub-arrays. You should access the sub-arrays using p and then access second element in that 1D array. Snippet below should explain the idea:

int * p = 0;
p = numbers[0]; // Now array p = {1, 2, 3}
std::cout << p[1] << std::endl; // '2'

p = numbers[1]; // Now array p = {4, 5, 6}
std::cout << p[1] << std::endl; // '5'

p = numbers[2]; // Now array p = {7, 8, 9}
std::cout << p[1] << std::endl; // '8'

Upvotes: -1

Christian Hackl
Christian Hackl

Reputation: 27528

First, let's understand the underlying problem with arrays in C++.

Your array int numbers[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; can be pictured like this in memory, assuming that sizeof(int) is 4 (which may or may not be true on your system, but C++ does not prescribe the memory size of an int and 4 is easier to draw than 8):

enter image description here

As I often like to explain, a multi-dimensional array in C++ is just a special case of a one-dimensional array, just that its element type itself is an array type.

In your case, numbers is an array of size 3 with element type int[3]. The whole thing is contiguous in memory, which means that all elements are placed next to each other without any holes in between. The concept of "rows" and "columns" is really just an illusion provided by how the [] notation accesses the individual array elements by performing pointer arithmetics under the hood.

Getting a pointer to a "row" is easy, because the elements of the "row" all rest next to each other. For example, a pointer to the second "row" is just one that points to the "4" element, because the "4", "5" and "6" elements which constitute the int[3] "row" are directly next to each other:

enter image description here

Now what you want is a pointer to a "column". As the picture shows, that's completely impossible on such a low language level. For example, the second "column" is made up by the "2", "5" and "8" elements. But there is, obviously, no place you can have a pointer point to where those three elements are placed next to each other in memory.

The concept of a "column" can thus be achieved only very indirectly on such a low language level, via operations and not via data types, by traversing the "rows" first. Example:

// print 2nd column:
int const column_index = 1;
for (int row_index = 0; row_index < 3; ++row_index)
{
    std::cout << numbers[row_index][column_index];
}

Nevertheless, this is C++, a language well capable of abstracting away such low-level mechanics. One solution to your problem would be creating a custom data type which performs the indirection internally and keeps the "rows and columns" illusion. You don't even need a two-dimensional array for this; a one-dimensional one is sufficient.

Here is a rather simple example (without error handling, hard-coded values and no const operations):

#include <iostream>

class Column
{
public:
    int& element(int row_index)
    {
        return array[row_index * width + column_index];
    }

private:
    friend class Matrix;

    Column(int width, int(&array)[9], int column_index) :
        width(width),
        array(array),
        column_index(column_index)
    {
    }

    int width;  
    int(&array)[9];
    int column_index;
};

class Matrix
{
public:
    Matrix() : width(3), array {1, 2, 3, 4, 5, 6, 7, 8, 9 }
    {}

    Column column(int column_index)
    {
        return Column(width, array, column_index);
    }

private:
    int width;
    int array[9];

};

int main()
{
    Matrix m;

    for (int column_index = 0; column_index < 3; ++column_index)
    {
        for (int row_index = 0; row_index < 3; ++row_index)
        {
            std::cout << m.column(column_index).element(row_index) << " ";
        }
        std::cout << "\n";
    }
}

Column in this example is called a proxy class. The friend declaration and private constructor ensure that only Matrix can create new objects of the class.

Combined with C++ operator overloading, you can even achieve the original [][] notation. You are practically just giving different names to the column and element member functions:

#include <iostream>

class Column
{
public:
    int& operator[](int row_index)
    {
        return array[row_index * width + column_index];
    }

private:
    friend class Matrix;

    Column(int width, int(&array)[9], int column_index) :
        width(width),
        array(array),
        column_index(column_index)
    {
    }

    int width;  
    int(&array)[9];
    int column_index;
};

class Matrix
{
public:
    Matrix() : width(3), array {1, 2, 3, 4, 5, 6, 7, 8, 9 }
    {}

    Column operator[](int column_index)
    {
        return Column(width, array, column_index);
    }

private:
    int width;
    int array[9];

};

int main()
{
    Matrix m;

    for (int column_index = 0; column_index < 3; ++column_index)
    {
        for (int row_index = 0; row_index < 3; ++row_index)
        {
            std::cout << m[column_index][row_index] << " ";
        }
        std::cout << "\n";
    }
}

Your original goal is now achieved, because you can perfectly create an object to represent an entire column:

Matrix m;

Column second_column = m[1];

for (int row_index = 0; row_index < 3; ++row_index)
{
    std::cout << second_column[row_index] << " ";
}

But we are not there yet. Column is currently a bit of a dangerous class, because it cannot be safely returned from a function. The internal reference to the matrix' array can easily become a dangling one:

Column get_column() // dangerous, see below!
{
    Matrix m;

    Column second_column = m[1];

    // m is destroyed, but reference to its array
    // member is kept in the returned Column, which
    // results in undefined behaviour:
    return second_column;
}

This is similar to how iterator objects are usually not meant to be kept around for a longer time.

You can disable copying for Column and give it a private move constructor, though:

private:
    Column(Column const&) = delete;
    Column& operator=(Column const&) = delete;
    Column(Column&& src) = default;

This strongly discourages the bad behaviour from above, because one would actually have to make get_column return Column&& and std::move the return value to make it compile.

As far as our output loop from above is concerned, it would look like this:

Matrix m;

auto&& second_column = m[1];

for (int row_index = 0; row_index < 3; ++row_index)
{
    std::cout << second_column[row_index] << " ";
}

Whether all of this is worth the trouble, I don't know. Perhaps a big fat comment like // treat Column like an iterator, making sure that it never outlives the Matrix it refers to would work equally well in practice.

Upvotes: 6

058 094 041
058 094 041

Reputation: 496

As I understand the question you're having issues accessing all of the members of your multi-dimensional array. Here's an example of how you would access all of them, you can adapt it for your usage.

#include <iostream>

int main() {
    int numbers[3][3] = {1, 30, 3,
                         4, 40, 6,
                         7, 50, 9};

    for(int x=0;x<3;x++) {

        int* row = numbers[x]; // Decay array to int pointer, pointing to numbers[x][0]

        for(int y=0;y<3;y++) {

            std::cout<<row[y]<<" ";

        }
        std::cout<<"\n";
    }
}

Output:

1 30 3
4 40 6
7 50 9

Upvotes: 0

Andrii Bakal
Andrii Bakal

Reputation: 48

try this code:

int numbers[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
cout << *(*(numbers+2) + 1); 

Upvotes: 1

Related Questions