Declaring and allocating a 2d array in C++

前端 未结 5 1815
天命终不由人
天命终不由人 2021-01-21 18:01

I am a Fortran user and do not know C++ well enough. I need to make some additions into an existing C++ code. I need to create a 2d matrix (say A) of type double whose size (say

相关标签:
5条回答
  • 2021-01-21 18:26

    To allocate dynamically a construction similar to 2D array use the following template.

    #include <iostream>
    
    int main()
    {
       int m, n;
    
       std::cout << "Enter the number of rows: ";
       std::cin >> m;
    
       std::cout << "Enter the number of columns: ";
       std::cin >> n;
    
       double **a = new double * [m];
    
       for ( int i = 0; i < m; i++ ) a[i] = new double[n]();
    
       //...
    
       for ( int i = 0; i < m; i++ ) delete []a[i];
       delete []a;
    }
    

    Also you can use class std::vector instead of the manually allocated pointers.

    #include <iostream>
    #include <vector>
    
    int main()
    {
       int m, n;
    
       std::cout << "Enter the number of rows: ";
       std::cin >> m;
    
       std::cout << "Enter the number of columns: ";
       std::cin >> n;
    
       std::vector<std::vector<double>> v( m, std::vector<double>( n ) );
    
       //...
    
    }
    

    As for this code snippet

    int * x;
    x = new int [10];
    

    then x has type int * and x[0] has type int. So if the size of the pointer is equal to 4 and the size of an object of type int is equal also to 4 then sizeof( x ) / sizeof( x[0] ) will yields 1. Pointers do not keep the information whether they point to only a single object or the first object pf some sequence of objects.

    0 讨论(0)
  • 2021-01-21 18:33
    int ** x;
    x = new int* [10];
    for(int i = 0; i < 10; i++)
        x[i] = new int[5];
    

    Unfortunately you'll have to store the size of matrix somewhere else. C/C++ won't do it for you. sizeof() works only when compiler knows the size, which is not true in dynamic arrays.

    And if you wan to achieve it with something more safe than dynamic arrays:

    #include <vector>
    // ...
    
    std::vector<std::vector<int>> vect(10, std::vector<int>(5));
    vect[3][2] = 1;
    
    0 讨论(0)
  • 2021-01-21 18:40

    The formal C++ way of doing it would be this:

    std::vector<std::vector<int>> a;
    

    This creates container which contains a zero size set of sub-containers. C++11/C++13 provide std::array for fixed-sized containers, but you specified runtime sizing.

    We now have to impart our dimensions on this and, unfortunately. Lets assign the top-level:

    a.resize(10);
    

    (you can also push or insert elements)

    What we now have is a vector of 10 vectors. Unfortunately, they are all independent, so you would need to:

    for (size_t i = 0; i < a.size(); ++i) {
        a[i].resize(10);
    }
    

    We now have a 10x10. We can also use vectors constructor:

     std::vector<std::vector<int>> a(xSize, std::vector<int>(ySize)); // assuming you want a[x][y]
    

    Note that vectors are fully dynamic, so we can resize elements as we need:

     a[1].push_back(10); // push value '10' onto a[1], creating an 11th element in a[1]
     a[2].erase(2); // remove element 2 from a[2], reducing a[2]s size to 9
    

    To get the size of a particular slot:

     a.size(); // returns 10
     a[1].size(); // returns 11 after the above
     a[2].size(); // returns 9 after teh above.
    

    Unfortunately C++ doesn't provide a strong, first-class way to allocate an array that retains size information. But you can always create a simple C-style array on the stack:

    int a[10][10];
    std::cout << "sizeof a is " << sizeof(a) <<'\n';
    

    But using an allocator, that is placing the data onto the heap, requires /you/ to track size.

    int* pointer = new int[10];
    

    At this point, "pointer" is a numeric value, zero to indicate not enough memory was available or the location in memory where the first of your 10 consecutive integer storage spaces are located.

    The use of the pointer decorator syntax tells the compiler that this integer value will be used as a pointer to store addresses and so allow pointer operations via the variable.

    The important thing here is that all we have is an address, and the original C standard didn't specify how the memory allocator would track size information, and so there is no way to retrieve the size information. (OK, technically there is, but it requires using compiler/os/implementation specific information that is subject to frequent change)

    These integers must be treated as a single object when interfacing with the memory allocation system -- you can't, for example:

    delete pointer + 5;
    

    to delete the 5th integer. They are a single allocation unit; this notion allows the system to track blocks rather than individual elements.

    To delete an array, the C++ syntax is

    delete[] pointer;
    

    To allocate a 2-dimensional array, you will need to either:

    Flatten the array and handle sizing/offsets yourself:

    static const size_t x = 10, y = 10;
    int* pointer = new int[x * y];
    pointer[0] = 0; // position 0, the 1st element.
    pointer[x * 1] = 0; // pointer[1][0]
    

    or you could use

    int access_2d_array_element(int* pointer, const size_t xSize, const size_t ySize, size_t x, size_t y)
    {
        assert(x < xSize && y < ySize);
        return pointer[y * xSize + x];
    }
    

    That's kind of a pain, so you would probably be steered towards encapsulation:

    class Array2D
    {
        int*         m_pointer;
        const size_t m_xSize, m_ySize;
    public:
        Array2D(size_t xSize, size_t ySize)
            : m_pointer(new int[xSize * ySize])
            , m_xSize(xSize)
            , m_ySize(ySize)
        {}
    
        int& at(size_t x, size_t y)
        {
            assert(x < m_xSize && y < m_ySize);
            return m_pointer[y * m_xSize + x];
        }
    
        // total number of elements.
        size_t arrsizeof() const
        {
            return m_xSize * m_ySize;
        }
    
        // total size of all data elements.
        size_t sizeof() const
        {
            // this sizeof syntax makes the code more generic.
            return arrsizeof() * sizeof(*m_pointer);
        }
    
        ~Array2D()
        {
            delete[] m_pointer;
        }
    };
    
    Array2D a(10, 10);
    a.at(1, 3) = 13;
    int x = a.at(1, 3);
    

    Or,

    For each Nth dimension (N < dimensions) allocate an array of pointers-to-pointers, only allocating actual ints for the final dimension.

    const size_t xSize = 10, ySize = 10;
    int* pointer = new int*(x); // the first level of indirection.
    for (size_t i = 0; i < x; ++i) {
        pointer[i] = new int(y);
    }
    pointer[0][0] = 0;
    for (size_t i = 0; i < x; ++i) {
        delete[] pointer[i];
    }
    delete[] pointer;
    

    This last is more-or-less doing the same work, it just creates more memory fragmentation than the former.

    -----------EDIT-----------

    To answer the question "why do I not have 10" you're probably compiling in 64-bit mode, which means that "x" is an array of 10 pointers-to-int, and because you're in 64-bit mode, pointers are 64-bits long, while ints are 32 bits.

    0 讨论(0)
  • 2021-01-21 18:43

    The C++ equivalent of your Fortran code is:

    int cols, rows;
    if ( !(std::cin >> cols >> rows) )
         // error handling...
    
    std::vector<double> A(cols * rows);
    

    To access an element of this array you would need to write A[r * rows + c] (or you could do it in a column-major fashion, that's up to you).

    The element access is a bit clunky, so you could write a class that wraps up holding this vector and provides a 2-D accessor method.

    In fact your best bet is to find a free library that already does this, instead of reinventing the wheel. There isn't a standard Matrix class in C++, because somebody would always want a different option (e.g. some would want row-major storage, some column-major, particular operations provided, etc. etc.)

    Someone suggested boost::multi_array; that stores all its data contiguously in row-major order and is probably suitable. If you want standard matrix operations consider something like Eigen, again there are a lot of alternatives out there.

    If you want to roll your own then it could look like:

    struct FortranArray2D   // actually easily extensible to any number of dimensions
    {
        FortranArray2D(size_t n_cols, size_t n_rows)
            : n_cols(n_cols), n_rows(n_rows), content(n_cols * n_rows) { }
    
        double &operator()(size_t col, size_t row) 
            { return content.at(row * n_rows + col); }
    
        void resize(size_t new_cols, size_t new_rows)
        { 
             FortranArray2D temp(new_cols, new_rows);
             // insert some logic to move values from old to new...
             *this = std::move(temp);
        }
    
    private:
        size_t n_rows, n_cols;
        std::vector<double> content;
    };
    

    Note in particular that by avoiding new you avoid the thousand and one headaches that come with manual memory management. Your class is copyable and movable by default. You could add further methods to replicate any functionality that the Fortran array has which you need.

    0 讨论(0)
  • 2021-01-21 18:49

    I would recommend using std::vector and avoid all the headache of manually allocating and deallocating memory.

    Here's an example program:

    #include <iostream>
    #include <vector>
    
    typedef std::vector<double> Row;
    typedef std::vector<Row> Matrix;
    
    void testMatrix(int M, int N)
    {
       // Create a row with all elements set to 0.0
       Row row(N, 0.0);
    
       // Create a matrix with all elements set to 0.0
       Matrix matrix(M, row);
    
       // Test accessing the matrix.
       for ( int i = 0; i < M; ++i )
       {
          for ( int j = 0; j < N; ++j )
          {
             matrix[i][j] = i+j;
             std::cout << matrix[i][j] << " ";
          }
          std::cout << std::endl;
       }
    }
    
    int main()
    {
       testMatrix(10, 20);
    }
    
    0 讨论(0)
提交回复
热议问题