accessing C multidimensional array via array syntax vs pointer arithmetic - c

I'm diving into C again after a number of years. I thought that the following two print statements would have evaluated to the same output, based on other answers I have found; however it does not appear to be the case.
int main()
{
int** arr = malloc(
3 * sizeof(int*)
);
for(int y = 0; y < 3; y++) {
int* subarr = malloc(
3 * sizeof(int)
);
for(int x = 0; x < 3; x++) {
subarr[x] = y * 3 + x + 1;
}
arr[y] = subarr;
}
printf("%d\n", *(&arr[0][0]) + 3);
printf("%d\n", (&arr[0][0])[3]);
}
Could anyone explain what is going on here/what I am missing?

First of all, Let me explain what you are doing (At least for me).
arr[0] = A pointer to array {1, 2, 3}
arr[1] = A pointer to array {4, 5, 6}
arr[2] = A pointer to array {7, 8, 9}
First Case: *(&arr[0][0]) + 3
&arr[0][0] = Address of first element of {1, 2, 3}
*(&arr[0][0]) = 1 // Dereferencing the address
So, It prints 1 + 3 = 4
Second Case: (&arr[0][0])[3]
(&arr[0][0]) = Address of first element of {1, 2, 3}
But the length of array is 3, So you can only access indices up to 2.
So, It is causing undefined behaviour.

Related

Why can't i assign the name of a 2d array to a 2d pointer?

Practicing some simple arrays and pointers basics, I met something I can't understand:
in all written sources I could find, they say that a name of a 2D array is actually a 2D pointer,
meaning that if I write:
int a[3][4];
and also declare a pointer:
int **d2;
both of them are of the same type and I can safely assign:
d2 = a;
making the pointer point to the beginning of the first line.
But most surprisingly, that doesn't work, and my question is-why?
I would copy for you the code and the warnings I am getting:
#include <stdio.h>
int main() {
int **d2;
int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };
d2 = a;
while (d2 < a + 2) {
printf("%d", **d2);
d2++;
}
return 0;
}
I get this diagnostic:
warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
d=a;
^
ptr.c:11:9: warning: comparison of distinct pointer types lacks a cast
while(d<a+2)
They have totally different inner structure, so they are not interchangeable.
2D array
2D array is like this:
p[3][3]
|
v
+---------+---------+---------+---------+---------+---------+---------+
| p[0][0] | p[0][1] | p[0][2] | p[1][0] | p[1][1] | p[1][2] | ... |
+---------+---------+---------+---------+---------+---------+---------+
A single continuous space.
Pointer
pointer is like this:
**p
|
v
+------+ +---------+---------+---------+---------+---------+
| p[0] |-->| p[0][0] | p[0][1] | p[0][2] | p[0][3] | ... |
+------+ +---------+---------+---------+---------+---------+
| p[1] |-->| p[1][0] | p[1][1] | p[1][2] | p[1][3] | ... |
+------+ +---------+---------+---------+---------+---------+
| p[2] |-->| p[2][0] | p[2][1] | p[2][2] | p[2][3] | ... |
+------+ +---------+---------+---------+---------+---------+
| .... |
+------+
Where every p[i] may pointing to difference space with different size.
int **d2 defines a pointer to a pointer to an int. You can also think of it as a pointer to an array, and each array element contains a pointer to an int. Zang MinJie's answer shows this nicely.
An array int a[3][4]; is a contiguous block of memory, so equivalent to int a[3*4];. As you see, it is not an array of pointers to the rows.
Note that the compiler knows when you work with a contiguous array and with an array of pointers, and uses the correct indexing method. For example:
void f(int **d2)
{
printf("%d", d2[3][4]);
}
void g(void)
{
int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };
printf("%d", a[3][1]);
}
As you see, the way to write the index expressions is the same, even though the underlying representations are completely different. I can understand that is confusing. Just remember that "the compiler knows".
This is because int **d2 is a pointer to pointer. There is no such thing as 2d pointer.
For example, lets consider 1d array:
int a[2] = {1, 2};
Now, an integer pointer will point to the first element of the array if assigned as follows:
int *d = a;
Talking about pointer to pointers, a pointer to pointer points to a memory location which holds address of other pointer!
For example,
// This will declare an integer pointer
int *a;
The above pointer will store address of some other variables.
For example, you can assign values to it as follows:
int x = 5;
int *a = &x;
Now, here is what a pointer to pointer does:
A pointer to pointer holds memory address of other pointers.
This means that, in the above example we can declare a pointer to pointer that holds address of pointer a:
int x = 5;
int *a = &x;
// A pointer to pointer
int **b = &a;
// To print value of x using b
printf("%d", **b);
The above code can be understood as follows:
a = &x; so *a = *(&x) = 5
b = &a; so *b = *(address in b) = *(address of a) = address of x
But **b = *(*b) = *(address of x) = 5
Hope this clears your confusion about pointer to pointers.
Now, coming towards your use case, if you want a pointer to point to your 2d integer array, you can just use a pointer and assign it the address of first element of the array. This can be done as follows:
int a[2][2] = {{1, 2}, {3, 4}};
// Declare a pointer that points to first element of array
int *b = &a[0][0];
Now if you want to access the element a[i][j] of this array using pointer, you can access it as b + 2*i + j.
In general, if the dimensions of array is p*q, the element a[i][j] can be accessed using b + q * i + j.
For example, to print the array using 2 for loops,
int rows = 2;
int cols = 3;
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int *b = &a[0][0];
for(int i=0; i<rows; i++) {
for(int j=0; j<cols; j++) {
// Print a[i][j] using pointer b
printf("%d ", *(b + cols * i + j));
}
}
// 1 2 3 4 5 6
You can also use single loop,
int rows = 2;
int cols = 3;
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int *b = &a[0][0];
for(int i=0; i<rows*cols; i++) {
printf("%d ", *(b + i));
}
// 1 2 3 4 5 6
int **d2; is not a 2D pointer, it is a pointer to a pointer to int, sometimes called a double pointer.
int a[3][4]; defines an array of 3 arrays of 4 int, also called a 2D array.
These 2 objects have incompatible types: there is no int pointer variable here, the address of which could be stored into d2.
Let's take an analogy:
an int is like a house with 32 rooms;
a street is an array of houses;
a block is a group of contiguous streets;
a pointer is an entry in an address book with the address of a house;
a double pointer is a piece of paper where you wrote the location where you stored your address book.
The rows in a 2D array are consecutive in memory, so you can enumerate the entries in the array this way:
#include <stdio.h>
int main() {
int *p;
int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };
printf("iterating as a 2D array:\n");
p = &a[0][0];
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 2; col++) {
printf("%d ", *p);
p++;
}
printf("\n");
}
printf("\n");
printf("iterating as a single array:\n");
p = &a[0][0];
while (p < &a[5][0]) {
printf("%d ", *p);
p++;
}
printf("\n");
return 0;
}
Output:
iterating as a 2D array:
1 2
3 4
5 7
7 8
9 11
iterating as a single array:
1 2 3 4 5 7 7 8 9 11

I am confused how to understand this code. contains double pointers

I don't understand why the code below changes the array b:
int a[] = { 3, 6, 9 };
int b[] = { 2, 4, 6, 8, 10 };
int **c;
int **d[2];
c = (int **)malloc (b[1] * sizeof(int *));
*c = &a[1];
c[1] = c[0] + 1;
*d = c;
c = c + 2;
*c = b;
c[1] = &c[0][3];
*(d + 1) = c;
d[0][3][1] = d[1][0][0];
d[1][0][2] = d[0][1][0];
I have run this code and found the values of array a and array b but I am unable to understand how these values come.
Array a remains unchanged while b becomes 2, 4, 9, 8, 2. How does this happen?
c = (int**)malloc(b[1] * sizeof(int*)); //int **c[4] ???
c is an array of double pointers *c = &a[1] this means that c[0] has the address of array a's second index. I am not getting the way to interpret this.
The code contains actual statements, therefore it must be part of a function body, hence all declarations herein have automatic storage. It is highly convoluted, with purposely contrived double indirections... Lets analyse it one line at a time:
int a[] = { 3, 6, 9 }; -- a is an array of 3 ints initialized with some explicit values.
int b[] = { 2, 4, 6, 8, 10 }; -- likewise, b is an array of 3 ints initialized with some explicit values.
int **c; -- c is an uninitialized pointer to a pointer to int, that can be made to point to an array of pointers to int.
int **d[2]; -- d is an uninitialized array of 2 pointers to pointers to int, each of which can be made to point to an array of pointers to int.
c = (int **)malloc(b[1] * sizeof(int *)); -- c is set to point to a block of uninitialized memory with a size of 4 pointers to int. In short, c now points to an uninitialized array of 4 pointers to int.
*c = &a[1]; -- The element pointed to by c (aka A[0]) is set to point to the second element of a (aka a[1], with a value of 6). The value of A[0] is &a[1].
c[1] = c[0] + 1; -- The second element in the array pointed to by c (aka A[1]) is set to point to the element after the one pointed to by c[0], hence it points to the third element of a (aka a[2] with a value of 9). The value of A[1] is&a[2]`.
*d = c; -- The first element of d is set to the value of pointer c, which is the address of A[0]. The value of d[0] is &A[0].
c = c + 2; -- The pointer c is incremented by 2, it now points to the third element of the array A allocated with malloc(), A[2].
*c = b; -- The element pointed to by c, A[2], which is itself a pointer, is set to point to the first element of b, b[0]. The value of A[2] is &b[0].
c[1] = &c[0][3]; -- The element after that, A[3], the 4th element of the array allocated by malloc, is set to point to the 4th element of the array pointed to by the element c points to. &c[0][3] is equivalent to c[0] + 3 or &(*c)[3] or simply *c + 3. This element is b[3] which has the value 8. The value of A[3] is&b[3]`.
*(d + 1) = c; -- This is equivalent to d[1] = c; which sets the second element of d to the value of the pointer c, which is the address of the 3rd element of the array allocated wth malloc(), A[2], which points to b[0]. The value of d[1] is &A[2].
d[0][3][1] = d[1][0][0]; -- Let's rewrite these terms:
d[0][3][1] => (&A[0])[3][1] => A[3][1] => (&b[3])[1] => *((b + 3) + 1) => b[4]
d[1][0][0] => (&A[2])[0][0] => (*&A[2])[0] => A[2][0] => (&b[0])[0] => b[0] which is the value 2.
Hence b[4] = 2;.
d[1][0][2] = d[0][1][0]; -- Let's rewrite these:
d[1][0][2] => (&A[2])[0][2] => (*&A[2])[2] => A[2][2] => (&b[0])[2] => (b + 0)[2] => b[2].
d[0][1][0] => (&A[0])[1][0], ie A[1][0] => (&a[2])[0] => *&a[2] => a[2] that has a value of 9.
Hence b[2] = 9;
As a consequence, the array b now has elements { 2, 4, 9, 8, 2 }.
You can run the program:
#include <stdio.h>
#include <stdlib.h>
int main() {
int a[] = { 3, 6, 9 };
int b[] = { 2, 4, 6, 8, 10 };
int **c;
int **d[2];
c = (int **)malloc (b[1] * sizeof(int *));
*c = &a[1];
c[1] = c[0] + 1;
*d = c;
c = c + 2;
*c = b;
c[1] = &c[0][3];
*(d + 1) = c;
d[0][3][1] = d[1][0][0];
d[1][0][2] = d[0][1][0];
printf("a = { ");
for (size_t i = 0; i < sizeof a / sizeof *a; i++)
printf("%d, ", a[i]);
printf("};\n");
printf("b = { ");
for (size_t i = 0; i < sizeof b / sizeof *b; i++)
printf("%d, ", b[i]);
printf("};\n");
return 0;
}

Why is p[0]++ different from *(p)++ in C

When I tried to execute these codes in C
#include <stdio.h>
int main(void)
{
int arr[] = {10, 20};
int *p = arr;
p[0]++; // *(p)++;
printf("arr[0] = %d, arr[1] = %d",arr[0], arr[1]);
return 0;
}
output: arr[0] = 11, arr[1] = 20
on replacing p[0]++; by *(p)++;
output : arr[0] = 10, arr[1] = 20
Please explain why this is. I know a[i] is equivalent to *(a + i)
The expression:
p[0]++;
basically increments the value at index 0.
While
*(p)++
Increments the pointer to the next address .
Example :
int arr = {10,20};
int *p = arr; // p points to the first element of array, i.e 10
*(p)++; // now p points to the next element i.e 20.
below code can be used:
p[0]++ means = p[0] + 1;
p[0]++ = 10 + 1;
p[0]++ = 11;
You can't do with this same thing with an array, so be careful.
I hope it helps!

Decrementing an array from last element in C

I want to decrement an array from last element in C. I first wrote the following code to increment an array from the first element:
#include<stdio.h>
int x[11] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *pointer, count;
int main (void) {
pointer = x;
for (count = 0; count < 11; count++)
printf("%d\n", *pointer++);
return 0;
}
This works fine. But then I tried to decrement the elements by modifying the code to this:
#include<stdio.h>
int x[11] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *pointer, count;
int main (void) {
pointer = x[10];
for (count = 0; count < 11; count++)
printf("%d\n", *pointer--);
return 0;
}
But of course I am doing something wrong. I'd appreciate it very much if you could help me to understand my mistake.
You have two options which are equivalent.
pointer = &x[10];
pointer = x + 10;
Either will achieve the effect of making the pointer point at the 10th element of x.
pointer = x[10]; should be pointer = &x[10];.
You're setting pointer to the integer value x[10]. What you want to do is set pointer to the address of the last element in x.

Modifying an integer in an array of integers

If I have a an array of ints, how could I directly edit each int?
int i = arr + 1; // Getting the integer in pos 1
i is just a copy, correct? If I set i = 4, then arr + 1 would still be 1.
Would this work?
int *i = &(arr + 1);
*i = 4;
You should use the array operators:
int i = arr[1];
arr[1] = 4;
Change your code to this:
int *i = arr + 1;
*i = 4;
and it will work. Arrays in C are just pointers to first element in the array.
So this arr + 0 will give address of first element in array and this arr + 1 is an address of second element.
You've got:
int arr[4] = {0, 1, 2, 3};
Want to edit it further?
arr[0] = 42;
// arr[] = {42, 1, 2, 3};
Want to change all of them at once? There's:
for(int i = 0; i < 4; ++i)
arr[i] = i * 2;
// arr[] = {0, 2, 4, 6};
And don't forget memset()!
memset(arr, 42, 4);
// arr[] = {42, 42, 42, 42};
Want to change everything but the first element to 7?
memset(&arr[1], 7, 4 - 1);
// arr[] = {42, 7, 7, 7};
Would you like to know somethin' about pointers? (Here's a more useful link.)
See this? (If you can't, please stop reading this. Thanks!)
int *ptr = &arr[1];
It's equivalent to:
int *ptr = arr + 1;
Which is also equivalent to:
int *ptr = arr;
ptr = ptr + 1;
OK, now that we've got that down, let's show you a more efficient for-loop than the one I did above:
int *ptr = arr;
for(int i = 0; i < 4; ++i)
{
*ptr = i << 2;
// i * 2 == i << 2
++ptr;
}
// arr[] = {0, 2, 4, 6};
Not that you should code like that; the compiler will handle it for you, most likely.
Would you like another answer in the form of a series of questions?
Array indexing operators can do what you need.
arr[3] = 101; //assignment to array
int x = arr[37]; //get value from array
etc.
No need for for that memory arithmetic here..

Resources