GMML
GMML

Reputation: 35

Dereferencing Issue with 2D Arrays

Trying to understand C pointers w/2D arrays. Looking at some old notes from back when I was a student.

The code:

#include<stdio.h>
#define size 100

int main(){
    float x[size][size],a[size],b[size],sum=0,result;
    int i,j,M,N;
    float *xPtr;
    xPtr=&x[0][0];
    
    printf("Please enter the number of students: ");
    scanf_s("%d",&M);
    printf("Please enter the number of quizzes: ");
    scanf_s("%d",&N);
    printf("\n");

    for(i=0;i<M;i++){
        for(j=0;j<N;j++){
          printf("Enter the grade for student # %d in quiz # %d: ",i,j);
          scanf_s("%f",&x[i][j]);
        }
    }

    printf("\n\t");
    for(j=0;j<N;j++){
        printf("\t\tquiz # %d",j);
    }
    printf("\n");
    for(i=0;i<M;i++){
        printf("student # %d",i);
        for(j=0;j<N;j++){
            printf("\t\t%.2f\t",x[i][j]);
        }
        printf("\n");
    }

    for(i=0;i<M*N;i++){
        printf("\nx[%d][%d]=%0.2f\n",i/N,i%N,*(xPtr+i));
    }

    return 0;
}

The console output (# of students=3, # of quizzes=2): output_1

The alternative given to avoid this problem was to change the dereferencing portion of the code from

for(i=0;i<M*N;i++){
        printf("\nx[%d][%d]=%0.2f\n",i/N,i%N,*(xPtr+i));
    }

to

for(i=0;i<M;i++){
        for(j=0;j<N;j++){
            printf("\nx[%d][%d]=%.2f\n",i,j,*((xPtr+i*size)+j));
        }
    }

The console output of this approach is successful (# of students=3, # of quizzes=2): output_2

The explanation given as to why the first dereferencing approach fails is the following:

QUESTION: Why are the elements x[1][0],x[1][1],x[2][0],x[2][1] coming out to be 0.00?

ANSWER: Because there is a mismatch between size=100 and the actual # of rows and columns. This problem does not arise with 1D arrays where, if the actual # of elements in the 1D array is less than size, it does not matter since the elements after the actual number of elements are zero and do not count.

MY QUESTION:

  1. I don't understand the explanation given (why is there a mismatch and why does the problem not arise w/1 arrays?). If the pointer is pointing to the first element of the array (ie. xPtr=&x[0][0];) then can't we increment by one each time (ie. xPtr+i) and step element by element since a 2D array is stored sequentially as a a 1D array with each row one after another?

  2. I don't understand the implementation of dereferencing in the successful approach, specifically, what is the function of including size in the dereference (*((xPtr+i*size)+j)), my intuition would have been to do something like *(*(xPtr+i)+j) with the two nested dereference operators. Would that work? How was he able to get away with using only one here? I guess he is using size to shift forward by the row size, but I'm not entirely sure how it all works together...

Upvotes: 2

Views: 126

Answers (1)

luantkow
luantkow

Reputation: 2959

I think that you already have enough information to understand that concept. You know that 2D array are really stored in linear address space.

The mismatch is caused by the fact that you allocated more space in a table row than you used. When you try to get the values you should omit that unused space. That's why the fix multiplies the current row by the row size, to jump to the first element of the row you are aiming to read from.

#include <stdio.h>

#define SIZE 4

void main()
{
    char buf[SIZE][SIZE];
    int n = 2;

    printf("Normal addresses:\r\n");
    for(int i = 0; i < SIZE; i++)
    {
        for(int j = 0; j < SIZE; j++)
        {
            printf("%p\t", &buf[i][j]);
        }
        printf("\r\n");
    }
    char* start = &buf[0][0];

    printf("Broken way:\n\r");
    for(int i = 0; i < n*n; i++)
    {
        printf("%p\t", start + i);  
        if((i+1)%n == 0)
        {
            printf("\r\n");
        }
    }
    printf("Fixed way:\n\r");
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            printf("%p\t", start + (i*SIZE)+j);  
        }
        printf("\r\n");
    }

}

Output:

Normal addresses:
0x7ffe128dd2e0  0x7ffe128dd2e1  0x7ffe128dd2e2  0x7ffe128dd2e3
0x7ffe128dd2e4  0x7ffe128dd2e5  0x7ffe128dd2e6  0x7ffe128dd2e7
0x7ffe128dd2e8  0x7ffe128dd2e9  0x7ffe128dd2ea  0x7ffe128dd2eb
0x7ffe128dd2ec  0x7ffe128dd2ed  0x7ffe128dd2ee  0x7ffe128dd2ef
Broken way:
0x7ffe128dd2e0  0x7ffe128dd2e1
0x7ffe128dd2e2  0x7ffe128dd2e3
Fixed way:
0x7ffe128dd2e0  0x7ffe128dd2e1
0x7ffe128dd2e4  0x7ffe128dd2e5

Regarding the second question, you can get the elements via *(*(xPtr+i)+j), but you have to give a compiler a hint how long the rows are. Please consider following example, compiler can find that information in pointer type.

#include <stdio.h>

#define SIZE 4

int main()
{
    char buf[SIZE][SIZE];
    int n = 2;

    printf("Normal addresses:\r\n");
    for(int i = 0; i < SIZE; i++)
    {
        for(int j = 0; j < SIZE; j++)
        {
            printf("%p\t", &buf[i][j]);
        }
        printf("\r\n");
    }
    char (*start)[SIZE] = &buf[0];
    printf("Read:\n\r");
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            printf("%p\t", (*(start+i)+j));  
        }
        printf("\r\n");
    }

}

Upvotes: 2

Related Questions