Matrix with out-of-range index returning correct values

Asked

Viewed 96 times

6

I was trying to create a matrix using while and realized that the program displays all the elements correctly if I just increment the value of j (column) up to 8. The program was not supposed to print random values, since there are only 3 columns?

int main(){
    int m[3][3]={10,2,3,
                4,5,6,
                7,8,9};
    
    int j = 0;
    while(j < 9)
    {
        printf("%d\n", m[0][j]);
        j++;
    }

    return 0;
}
  • In fact there is already an error when declaring a 3x3 matrix assigning the values only at the 0xN position, the statement should be as follows so that it has effectively the two dimensions of the matrix: int m[3][3] = { {10,2,3}, {4,5,6}, {7,8,9} };

  • @bruno101 It’s no problem to initialize the array that way. See here in the second example of the "nested arrays" section. See also this answer which shows that deep down the dimensions are just an "illusion" :-)

5 answers

5

In C and C++ native two-dimensional arrays are stored as continuous zones of memory, as if a normal array were. In this sense, the following figure illustrates this:

inserir a descrição da imagem aqui

Above the array I placed the position we hope to interpret as a two-dimensional array, and at the bottom I placed the position it will occupy in memory against the allocated space. As you see the allocation matches that of a simple 9-position array.

The detail in this type of C arrays has to do with what the compiler does on each access. It switches the notation [linha][coluna] by an access in a continuous memory zone by making the following calculation:

<linha> * <quantidade de elementos de cada linha> + <coluna>

It means that when you want to access row 2 and column 1 with:

m[2][1]

He does: 2 * 3 + 1 which gives you 7, and so that’s the memory position to be accessed corresponding to [2][1] as you can see in the figure I put on.

This means that although you declared the array wrong and accessed it wrong, both errors play right with the layout of the elements in memory internally and so you see the correct values.

It would be different if you had done an array of arrays through pointers and allocation with malloc. In such a situation this effect would not occur and would visually have the following:

inserir a descrição da imagem aqui

Using dynamic allocation I could have this layout in memory with the following code:

int **n = malloc(sizeof(int*) * 3);
int i;
for (i = 0; i < 3; ++i){
    n[i] = malloc(sizeof(int) * 3);
}

And in this case the accesses you’re making would effectively show junk in memory from the 4 position accessed.

Confirm yourself on Ideone

Completion

Although you did wrong, you saw the right result by coincidence of internal details of disposition in memory. This is typical in C and is one of the details that makes language more difficult.

In other languages such as Java or C# you would never see this effect because off-limits accesses generate exceptions, so the error would be detected immediately.

In the code that has the compiler warns you of the incorrect declaration error of the array, but the access code outside the limits does not. This would however be possible to detect with a tool like Valgrind.

1

You have good answers above.

But I’m going to leave an example in C of this in practice and maybe illustrate these things better

I was trying to create a matrix using while and I realized that the program displays all elements correctly if I just increment the value from j (column) up to 8. It was not for the program to print random values, since there are only 3 columns?

C is not FORTRAN. In C there are no matrices or this sort of multi-dimensional vectors.

In C there are only vectors. And vector vectors. And vector vectors. And so on.

The data is saved by line. one after the other. All that exists is the start address and the calculation from the indexes to find the element. There is only base and offset, an address and a value. And that is the general formula:

A fórmula geral

An example in C

Instead of a vector int[3][3] as in your program I will use a char[12] to show the mechanics: 12 is multiple of 2, 3, 4 and 6 after all. I will not use more than 2 indexes because it would be longer for no reason. The above formula shows the account for any number of indices: base(ar) is the initial address of the array, esize is the number of columns. The formula is everywhere, but this image has been copied from Data Structures Using C, Tenenbaum and others, '89 A textbook used everywhere.

The program

The following code uses this vector

    char        x[13] = "0123456789AB"; // 12+1

And then access as vectors 2x6, 3x4, 4x3 and 6x2 , changing the ninth element to '0', '1' and '2' in the vector char[] and showing the effect on the other "matrices"

the output of the program


x[8] = '0' = 48 (dec)
x_26[1][2] = '0' = 48 (dec)
x_34[2][0] = '0' = 48 (dec)
x_43[2][2] = '0' = 48 (dec)
x_62[4][0] = '0' = 48 (dec)

x[8] = '1' = 49 (dec)
x_26[1][2] = '1' = 49 (dec)
x_34[2][0] = '1' = 49 (dec)
x_43[2][2] = '1' = 49 (dec)
x_62[4][0] = '1' = 49 (dec)

x[8] = '2' = 50 (dec)
x_26[1][2] = '2' = 50 (dec)
x_34[2][0] = '2' = 50 (dec)
x_43[2][2] = '2' = 50 (dec)
x_62[4][0] = '2' = 50 (dec)

The code C

The program has only 20 lines, serves only to show how to address this in C and as what exists is only base and offset.

#include <stdio.h>
int         main(void){
    char        x[13] = "0123456789AB"; // 12+1
    char      (*x_26)[2][6] =  NULL;
    char      (*x_34)[3][4] =  NULL;
    char      (*x_43)[4][3] =  NULL;
    char      (*x_62)[6][2] =  NULL;

    x_26 = x_34 = x_43 = x_62 = x;
    for ( int i='0'; i<'3'; i+=1)
    {
        x[8] = i;
        printf( "\nx[8] = '%c' = %d (dec)\n", x[8], x[8]);
        printf( "x_26[1][2] = '%c' = %d (dec)\n", (*x_26)[1][2], (*x_26)[1][2] );
        printf( "x_34[2][0] = '%c' = %d (dec)\n", (*x_34)[2][0], (*x_34)[2][0] );
        printf( "x_43[2][2] = '%c' = %d (dec)\n", (*x_43)[2][2], (*x_43)[2][2] );
        printf( "x_62[4][0] = '%c' = %d (dec)\n", (*x_62)[4][0], (*x_62)[4][0] );
    }
    return 0;
};

That’s the important line:

    x_26 = x_34 = x_43 = x_62 = x;

Since all are pointers to the same address, changing the dimensions of the vectors changes the formula above, but the sum will always give 8 :) and, as the basis is also always the same, all pointers point to the same thing.

Here are the accounts:

To char x[12]: x[8] will be in *(x + 8), displacement 8 from x

To char x[2][6]: x[1][2] will be in *(x + 1*6 + 2), displacement 8 from x

To char x[3][4]: x[2][0] will be in *(x + 2*4 + 0), displacement 8 from x

To char x[4][3]: x[2][2] will be in *(x + 2*3 + 2), displacement 8 from x

To char x[6][2]: x[4][0] will be in *(x + 4*2 + 0), displacement 8 from x

Okay, it’s bad to read, but you can see the formula in effect. And how to declare these things in C.

1

Important to understand what is happening: The memory of the array is stored sequentially on the computer.


So when you use matrix[0][6] it starts from the beginning of the Matrix until the seventh position. The proof is, that if you do matrix[0][12] he will access the thirteenth position, which is a position with memory junk.


And when you access the first index as matrix[index][0] and as if you skipped the size used in the index fold declaration. Example:
array[2][0] in the statement you made is equal to matrix[0][6].

  • So it’s like an array [3][3] and a [0][8] do the same function?

  • 1

    I was editing the answer when you commented, I think I improved the explanation, and also answers your question.

0

Insert the code here The truth always has the memory junk, if you want to print correctly would recommend doing 2 is (in case you’re just walking the column) to print would be something like this:

int i=0,j=0;
for(i; i < 3 i++){
   for(j; j < 3; j++){
   printf("%d ",m[i][j]);
   
   }
   print("\n");
}
  • it is. But in this case my code, it is printing all values correctly (I already changed the values to test) and I wanted to understand why it works, since it is as if he is going through 9 columns and a row as if it were an array.

  • It’s kind of complex the explanation but the memory of the computer is sequential, when you make the matrix is a way to facilitate the visualization, but in the physical memory this sequential, the problem can be when is too big the information, where you can take another part of the code in another part of the memory, if it makes it easier I design

  • then theoretically it’s as if the matrix really is a vector with 9 elements for the computer

  • 1

    Yes, but it is possible to stay in another part of the program if it is very large, a practical way of you see is with pointer, want me to teach you how to do?

  • I am learning little by little, I have not yet reached the pointers but thanks for the explanation

  • 1

    Dude, if you need to call me in Whats, I just graduated in computer science, I might help a little kkkkk 11913180308

Show 1 more comment

0

This is because you are not filling a matrix in the correct way, the correct would be:

#include <stdio.h>

int main() {
    int m[3][3] = {         {10,2,3},          {4,5,6},          {7,8,9}     };

    int i=0,j=0;

    for(i; i < 3 i++){
       for(j; j < 3; j++){
       printf("%d ",m[i][j]);
       
       }
       print("\n");
    }

    return 0;
}
  • Actually I already filled the matrix using this bracket between the numbers and the result was the same. I know this method with For works, but I wanted to understand why it doesn’t work when I use the method I used in the code.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.