Reputation: 35
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):
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):
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 be0.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:
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?
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
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