2

Is there a way to tell the compiler that I've allocated a memory of size N * M and I wanna treat this pointer as N * M array? In other words, is there a way to write something like this?:

int arr[N][M] = (int[N][M])malloc(N * M * sizeof(int));
arr[x][y] = 123;

I know that the compiler doesn't know the dimensions of the array, all it knows is that that's a pointer. so my question is: can I somehow tell the compiler that this pointer returned by malloc is an array pointer and it's dimensions are N * M? I can use an array to pointers, pointer to arrays or pointer to pointers, but in all cases I'll have to lookup 2 addresses. I want to have a contiguous memory on the heap and treat it as a multidimensional array. just like how I would write:

int arr[N][M];

Is there any way to achieve that?

StackExchange123
  • 1,871
  • 9
  • 24
  • Are N and M constexpr or #defines ? Did you try (int[][M])malloc... – Jeffrey Mar 04 '20 at 22:22
  • 3
    The way to do this is with `std::vector`. If you need more then one dimension, then you put the vector into a class that fakes that the vector has multiple dimensions. Or, you could just get a library that does this already. – NathanOliver Mar 04 '20 at 22:26
  • In C I can write `int (*a)[10]=malloc(100);`. – Marc Glisse Mar 04 '20 at 22:31
  • This smells like [XY problem](http://xyproblem.info/). Can you first explain why you are need this? I'm pretty sure there is another way to tackle you `X` problem. – Marek R Mar 04 '20 at 22:43
  • `int arr[N][M]` is not a pointer, it is an array. – Eljay Mar 04 '20 at 22:45
  • @jeffreysupportsMonica I mean by N and M any constant numbers. How should the lhs look like? I tried auto arr = (int[][3])malloc(2 * 3 * sizeof(int)); and it didn't work. – StackExchange123 Mar 04 '20 at 23:01
  • @NathanOliver Can you provide an example please? I suppose what you're saying is that there is no way to treat a pointer as a multidimensional array. But there is a workaround. – StackExchange123 Mar 04 '20 at 23:01
  • @Marc Glisse Unfortunately it doesn't work in C++. – StackExchange123 Mar 04 '20 at 23:01
  • @Marek R I'm sorry if my problem wasn't obvious. But I think I've addressed my problem. I don't wanna access more than one pointer. I want the compiler to use only one pointer to access my elements since some times I don't need to have an array to pointers and my problem can be solved using multidimensional array (I think it's even better since it'll be more efficient). Usually array size is quite big that I can't put it in the stack. But because I don't know how to allocate multidimensional arrays on the heap, I tend to declare an array to pointers. – StackExchange123 Mar 04 '20 at 23:02
  • @Eljay Yes I know. I want to treat a pointer as an array. In other words I wanna write ptr[x][y] and let the compiler figure out that I mean *(ptr + x * N + M). – StackExchange123 Mar 04 '20 at 23:03
  • 1
    It's technically undefined behaviour to access malloc'd space in C++ ; you should use `new` or higher level containers – M.M Mar 04 '20 at 23:13
  • M.M: can you provide a citation? – Jeffrey Mar 04 '20 at 23:41
  • 1
    @JeffreysupportsMonica https://stackoverflow.com/questions/40873520/, https://stackoverflow.com/questions/36024325/ – M.M Mar 05 '20 at 05:08
  • @StackExchange123 I've added an answer that has a very abbreviated but working concept. – NathanOliver Mar 05 '20 at 13:22

4 Answers4

3

In a C++ program you should use the operator new.

As for malloc then in C++ M shall be a constant expression if you want to allocate a two-dimensional array.

You can write for example

int ( *arr )[M] = ( int ( * )[M] )malloc( N * M * sizeof(int) );

or

int ( *arr )[M] = ( int ( * )[M] )malloc( sizeof( int[N][M] ) );

If to use the operator new then the allocation can look like

int ( *arr )[M] = new int[N][M];

If M is not a compile-time constant then you can use the standard container std::vector as it is shown in the demonstrative program below

#include <iostream>
#include <vector>

int main() 
{
    size_t n = 10, m = 10;

    std::vector<std::vector<int>> v( n, { m } );

    return 0;
} 
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

What you want is a "matrix" class like

template <typename T>
class matrix
{
    size_t len;
    size_t width;
    std::vector<T> data;
public:
    matrix(size_t len, size_t width) : len(len), width(width), data(len*width) {}
    T& operator()(size_t row, size_t col) { return data[width * row + col]; }
    const T& operator()(size_t row, size_t col) const { return data[width * row + col]; }
    size_t size() const { return len * width; }
};

int main(int argc, char const *argv[])
{
    matrix<int> m(5, 7);
    m(3, 3) = 42;
    std::cout << m(3, 3);
}

This keeps all of the data in a single contiguous buffer, and doesn't have any undefined behavior unlike all the other examples that use malloc. It's also RAII, and you don't have to write any copy or move constructors since all of the members "do the right thing" with the compiler provided defaults. You can make this class more complicated, provide a proxy object so you can overload operator[] and be able to do m[][], but at it's base this is what you want.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
0

If you what to avoid use of stack and you need large single block of data to keep two dimensional array of constant size (know at compile time) the this is the best cleanest way to do it:

std::vector<std::array<int, M>> arr(N);
arr[x][y] = 3;

Now if you need M is a value known at run-time, it would be best to use boost::multi_array

I do not see a reason to use malloc.

Marek R
  • 32,568
  • 6
  • 55
  • 140
-1

You can do exactly what you want with a helper function. This let's you specify the array size at runtime, and it uses malloc as requested (although typically you should be using new):

#include <iostream>
#include <string>
#include <memory>

template <class T>
T** Get2DMalloc(size_t m, size_t n) {
    T** ret = (T**)malloc(sizeof(T*) * m);
    for (size_t i = 0; i < m; ++i) {
        ret[i] = (T*)malloc(sizeof(T) * n);
    }

    return ret;
}

template <class T>
void Free2D(T** arr, size_t m, size_t n) {
    for (int i = 0; i < m; ++i) {
        free(arr[i]);
    }

    free(arr);
}

int main() {
    int m = 3;
    int n = 3;

    int** a = Get2DMalloc<int>(3, 3);


    for (int x = 0; x < m; ++x) {
        for (int y = 0; y < n; ++y) {
            a[x][y] = x * m + y;
        }
    }

    for (int i = 0; i < m * n; ++i) {
        std::cout << a[i / m][i % n] << std::endl;
    }

    Free2D<int>(a, m, n);

    system("pause");
    return 0;
}
Alex
  • 877
  • 5
  • 10
  • Using `malloc` like this is undefined behavior. – NathanOliver Mar 05 '20 at 13:23
  • @NathanOliver Not it isn't, that's absurd. All memory is allocated and freed correctly. Also it directly answers the poster's question. You should know better than to comment like that-- you could harm a new programmer's understanding. – Alex Mar 05 '20 at 19:38
  • It's not obsured at all. In C++, `malloc` **does not create an object**. If you don't have an object, then any use of that variable besides creating an object in that memory is UB. This is how C++ works. See [this](https://stackoverflow.com/questions/36024325/at-what-point-does-memory-allocated-by-malloc-get-a-type) and [this](https://stackoverflow.com/questions/40873520/reinterpret-cast-creating-a-trivially-default-constructible-object) for standard citations. – NathanOliver Mar 05 '20 at 19:41