-1

I am exploring pointer "mechanics" in C/C++. I try to understand if and how is possible to implement a 2D matrix using two pointers (one for "rows" and one for "columns") instead of a single double pointer. I am aware that a matrix with rows*columns number of values could be stored in memory sequentially, but i am looking to comprehend deeper the mechanics of pointers and eventually to implement a function similar to

int value=getValue(vectorNr,vectorValue) 

that is able to "simulate" the construct

value=Matrix[vectorNr][vectorValue]

vectorPointer                 vectorValue
| AddressV1 |------|valAddr11 valAddr12 valAddr13 |
| AddressV2 |------|valAddr21 valAddr22 valAddr23 |
| AddressV3 |------|valAddr31 valAddr32 valAddr33 |   

I tried to begin writing a code like this but I quickly get stuck on pointer arithmetic and address offsetting. I also might chose a very dirty approach so any comment is welcome.

CODE TO IMPLEMENT A 2D ARRAY WITH POINTERS (BUT NOT USING DOUBLE POINTERS). To avoid confusion between rows and columns I refer to "Vectors as rows" and "Columns as vector values"

    int vectorsNumber = 3; //Number of Vectors
    int valuesNumber = 3; //Number of values stored in one     Vector


    //Addresses of Vectors. Since Vectors holds a reference to set of values, vectorPointer will hold an address for every set.
    void* vectorPointer = malloc(vectorsNumber     *sizeof(void*)); 


    //Populating the vectorPointer with the address generated by allocating memory for every set of values
    for (int i = 0; i < vectorsNumber; i++)
    {
        vectorPointer = (int*)malloc(valuesNumber * sizeof(int)); //Values shall be of int types
        vectorPointer++; //ILLEGAL since cannot perform arithmetic on pointers of type void. What do do??
    }

    //Restore the initial address. In any case...ILLEGAL arithmetic.    What do do??
    for (int i = 0; i < vectorsNumber; i++)
    {
        vectorPointer--; //Restore the initial address. In any case...ILLEGAL arithmetic.
    }

    //Declaring the pointer to hold the address of one value. Memory was already allocated before
    int* valueAddress; 
    for (int j = 0; j < vectorsNumber; j++)
    {
        //Getting the address of the first value of the first Vector
        valueAddress = (int*)vectorPointer; //Is this casting valid in C language?


        //Populating the value with whatever operation
        for (int k = 0; k < valuesNumber; k++)
        {

            *valueAddress = (k + 1)*(j + 1); //populate the Vector with int values
        }

        vectorPointer++; //Switch to next Vector.ILLEGAL arithmetic

    }
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Paolo
  • 173
  • 2
  • 9
  • 3
    There is no such language as "C/C++", there is only the two *very different* languages C and C++. So please pick *one* of them, as the answer could differ depending on language. – Some programmer dude Mar 20 '19 at 08:45
  • Why using `void*` whereas you want `int**` ? – Jarod42 Mar 20 '19 at 08:45
  • 2
    As a tip though: Think about how you would create a non-dynamic (non-pointer) "2d" array. Draw it out on paper and think about its layout. Perhaps you don't need two pointers? – Some programmer dude Mar 20 '19 at 08:46
  • I can't agree with Some programmer dude's first comment enough. Because I'm pretty sure [this Q&A](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) is a proper duplicate for a C question. – StoryTeller - Unslander Monica Mar 20 '19 at 08:50
  • Why dont you switch from `void*` to `int*`? - this should fix your Illegal artithmetic warnings – marcinj Mar 20 '19 at 08:50
  • Walking `vectorPointer` forward and backward is an odd choice. You're making an array so use array indexing. What you have looks very error prone. – Blastfurnace Mar 20 '19 at 08:54
  • @Jarod42 I am just curious to see if an int** can be somehow implemented using pointers to pointers – Paolo Mar 20 '19 at 08:56
  • @Blastfurnace Of course i am aware of that. I am just exploring how is an array indexing can be implemented using plain pointers and low level stuff – Paolo Mar 20 '19 at 08:57
  • @Someprogrammerdude I am aware that since a MxN matrix contains m*n values, i am aware i could create one single *int pointer and allocating m*n int blocks... – Paolo Mar 20 '19 at 09:01
  • If, for some reason, you want two separate arrays, where one maps a "row" to some specific location of the second array, then think about how to get a pointer to a "row" of your single data array. But more importantly, think long and hard about the *reason* you want this. Is it only plain curiosity? And if not, then what is the *actual* problem you need to solve? What part of your analysis and design requires you to have the second "mapping" array? – Some programmer dude Mar 20 '19 at 09:11
  • 1
    @Someprogrammerdude It is plain curiosity. I guess that some decades ago nothing was such obvious. I see computers for what they are, machines that evolves throught defined states. So since a matrix it's already quite an abstraction, and a command like int A[][] it's expression of that abstraction, i wonder which "1 to 1" instructions must be given to pass across states and so implement what we believe a matrix is. Since plain pointers are somehow something very connected to a memory block, i wondered what implementation has to be done to make it equivalent to something like A[][]...just that. – Paolo Mar 20 '19 at 09:16
  • 2
    @Paolo A true 2D array in C is a very thin abstraction over the hardware. `array[i][j]` literally means access memory address `address + i*N + j`. C doesn't care what you want the array dimensions to symbolize, arrays are raw memory maps. A pointer-to-pointer look-up table is a far higher layer of abstraction (and therefore performs much slower). – Lundin Mar 20 '19 at 09:26
  • @Lundin "A pointer-to-pointer look-up table" its a very interesting hint to me. Thanks for point it out with this words. – Paolo Mar 20 '19 at 09:43
  • 1
    @Paolo That's simply what `type** arr = malloc (sizeof *type); ... arr[i] = malloc (sizeof *arr[i]);` is. It's not a 2D array, nor can it be used as one. – Lundin Mar 20 '19 at 09:48
  • @Lundin if i declare type** arr = malloc (sizeof *type), i am not allowed to declare type* arr = malloc (sizeof type) because arr is already assigned. Does it means that * or ** are pure compiler sintaxis? – Paolo Mar 20 '19 at 10:15
  • @Paolo `type arr = malloc(...` is simply not valid C so I don't understand the question. – Lundin Mar 20 '19 at 10:19
  • @Lundin Sorry i meant type* arr = malloc (sizeof type)...or maybe not even this is accepted because C does not do implicit casting?I am slowly understanding pointers but there are so many details :) – Paolo Mar 20 '19 at 10:21

3 Answers3

2

Actually, you only need one pointer. One way of doing it is by allocating enough memory to hold all the values, and then have functions that map the x/y values in the array to the respective memory location. Assume we want those to be the dimensions and our array variable:

int dimX = 10, dimY = 5;
int *array;

You can set a value this way:

void arraySet(int value, int x, int y) {
    array[x + dimX * y] = value;
}

And get a value this way:

int arrayGet(int x, int y) {
    return array[x + dimX * y];
}

Allocate the memory beforehand such as in the main function:

array = malloc(sizeof(int)*dimX*dimY);

Use it like this:

arraySet(123, 9, 3); // sets the value of [9, 3] to 123
printf("Memory at 9, 3 is %d\n", arrayGet(9, 3));
Blaze
  • 16,736
  • 2
  • 25
  • 44
  • There was a new C standard released in the year 1999 making this method called "mangled arrays" pretty much obsolete. Use pointers to VLA instead. Cleaner code and faster executable. – Lundin Mar 20 '19 at 09:17
2

This "two pointers" idea doesn't make any sense and the code you posted cannot be salvaged. What you should do instead is to use a pointer to a 2D array:

int (*ptr)[x][y] = malloc(sizeof *ptr);
...
free(ptr);

That's it. However, a pointer to a 2D array is cumbersome, since we have to de-reference it before accessing the actual array. That is, we'd end up writing (*ptr)[i][j] = ...; which is ugly.

To dodge this, we can instead still allocate a 2D array, but instead of pointing at "the whole array", we point at the first element, which is a 1D array:

int (*ptr)[y] = malloc( sizeof(int[x][y]) );
...
ptr[i][j] = ... ; // more convenient syntax for access
...
free(ptr);

More info: Correctly allocating multi-dimensional arrays

Lundin
  • 195,001
  • 40
  • 254
  • 396
1

You can simulate int a[2][3]; with

  • one dimensional array and index computing:

    int* matrix = (int*) malloc(6 * sizeof(int));
    
    int get_matrix_2_3(int* matrix, int i, int j) { return matrix[3 * i + j]; }
    
  • 2-dimensional array:

    int** matrix = (int**) malloc(2 * sizeof(int*));
    for (int i = 0; i != 2; ++i) {
         matrix[i] = (int*) malloc(3 * sizeof(int));
    }
    
    matrix[1][2] = 42;
    
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    There is however never a reason to write code like this unless you need completely variable dimensions, such as in the case of a table of strings. [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) – Lundin Mar 20 '19 at 11:54