Explanation how pointers and multidimensional arrays work in C - c

I am trying to understand the following code.
#include <stdio.h>
#include <stdlib.h>
void print2(int (* a)[2]) {
int i, j;
for (i = 0; i < 3; i++ ) {
for (j = 0; j < 2; j++ ) {
printf("%d", a[i][j]);
}
printf("\n");
}
}
void print3(int (* a)[3]) {
int i, j;
for (i = 0; i < 2; i++ ) {
for (j = 0; j < 3; j++ ) {
printf("%d", a[i][j]);
}
printf("\n");
}
}
int main() {
int a[] = { 1, 2, 3, 4, 5, 6 };
print2((int (*)[2]) a);
print3((int (*)[3]) a);
return 0;
}
Running the code returns following output in console:
12
34
56
123
456
My problem is I don't understand where these numbers come from. I have trouble understanding what is actually going on in this code. More specifically, I'm uncertain of what this means:
int( (* a)[2])
I hope someone can explain this code to me, because I really want to understand how pointers and multidimensional arrays work in C.

TL;DR
This code contains incorrect and meaningless hacks. There is not much of value to learn from this code.
Detailed explanation follows.
First of all, this is a plain 1D array that gets printed in different ways.
These lines are strictly speaking bugs:
print2((int (*)[2]) a);
print3((int (*)[3]) a);
In both cases there is an invalid pointer conversion, because a is of type int[6] and a pointer to the array a would have to be int (*)[6]. But the print statements are wrong in another way too, a when used in an expression like this "decays" into a pointer to the first element. So the code is casting from int* to int(*)[2] etc, which is invalid.
These bugs can in theory cause things like misaligned access, trap representations or code getting optimized away. In practice it will very likely "work" on all mainstream computers, even though the code is relying on undefined behavior.
If we ignore that part and assume void print2(int (*a)[2]) gets a valid parameter, then a is a pointer to an array of type int[2].
a[i] is pointer arithmetic on such a type, meaning that each i would correspond to an int[2] and if we had written a++, the pointer would jump forward sizeof(int[2]) in memory (likely 8 bytes).
Therefore the function abuses this pointer arithmetic on a[i] to get array number i, then do [j] on that array to get the item in that array.
If you actually had a 2D array to begin with, then it could make sense to declare the functions as:
void print (size_t x, size_t y, int (*a)[x][y])
Though this would be annoying since we would have to access the array as (*a)[i][j]. Instead we can use a similar trick as in your code:
void print (size_t x, size_t y, int (*a)[x][y])
{
int(*arr)[y] = a[0];
...
arr[i][j] = whatever; // now this syntax is possible
This trick too uses pointer arithmetic on the array pointer arr, then de-references the array pointed at.
Related reading that explains these concepts with examples: Correctly allocating multi-dimensional arrays

void print2(int (*a)[2]) { /*...*/ }
inside the function print2 a is a pointer to arrays of 2 ints
void print3(int (*a)[3]) { /*...*/ }
inside the function print3 a is a pointer to arrays of 3 ints
int a[] = {1, 2, 3, 4, 5, 6};
inside the function main a is an array of 6 ints.
In most contexts (including function call context) a is converted to a pointer to the first element: a value of type "pointer to int".
The types "pointer to int", "pointer to array of 2/3 ints" are not compatible, so calling any of the functions with print2(a) (or print3(a)) forces a diagnostic from the compiler.
But you use a cast to tell the compiler: "do not issue any diagnostic. I know what I'm doing"
print3(a); // type of a (after conversion) and type of argument of print3 are not compatible
// print3((cast)a); // I know what I'm doing
print3((int (*)[3])a); // change type of a to match argument even if it does not make sense

It would be much easier to understand if you break it down and understand things. What if you were to pass the whole array to say a function print4, which iterates over the array and prints the elements? How would you pass the array to such a function.
You can write it something like
print4( (int *) a);
which can be simplified and just written as print4(a);
Now in your case by doing print2((int (*)[2]) a);, you are actually designing a pointer to an array of 2 int elements. So now the a is pointer in array of two elements i.e. every increment to the pointer will increase the offset by 2 ints in the array a
Imagine with the above modeling done, your original array becomes a two dimensional array of 3 rows with 2 elements each. That's how your print2() element iterates over the array a and prints the ints. Imagine a function print2a that works by taking a local pointer to a and increments at each iteration to the point to the next two elements
void print2a(int (* a)[2]) {
int (* tmp)[2] = a;
for( int i = 0; i < 3; i++ ) {
printf("%d%d\n", tmp[0][0], tmp[0][1] );
tmp++;
}
}
The same applies to print3() in which you pass an pointer to array of 3 ints, which is now modeled as a 2-D array of 2 rows with 3 elements in it.

The code seeks to reinterpret the array int a[6] as if it were int a[3][2] or int a[2][3], that is, as if the array of six int in memory were three arrays of two int (in print2) or two arrays of three int (in print3).
While the C standard does not fully define the pointer conversions, this can be expected to work in common C implementations (largely because this sort of pointer conversion is used in existing software, which provides motivation for compilers to support it).
In (int (*)[2]) a, a serves as a pointer to its first element.1 The cast converts this pointer to int to a pointer to an array of two int. This conversion is partially defined C 2018 6.3.2.3 7:
The behavior is undefined if the alignment of a is not suitable for the type int (*)[2]. However, compilers that have stricter alignment for arrays than for their element types are rare, and no practical compiler has a stricter alignment for an array of six int than it does for an array of two or three int, so this will not occur in practice.
When the resulting pointer is converted back to int *, it will compare equal to the original pointer.
The latter property tells us that the resulting pointer contains all the information of the original pointer, since it must contain the information needed to reconstruct the original pointer. It does not tell us that the resulting pointer is actually pointing to the memory where a is.
As noted above, common C implementations allow this. I know that Apple’s versions of GCC and Clang support this reshaping of arrays, although I do not know whether this guarantee was added by Apple or is in the upstream versions.
Given that (int (*)[2]) is passed to print2 as its a, then a[i][j] refers to element j of array i. That is, a points to an array of two int, so a[0] is that array, a[1] is the array of two int that follows it in memory, and a[2] is the array of two int after that. Then a[i][j] is element j of the selected array. In effect, a[i][j] in print2 is a[i*2+j] in main.
Note that no aliasing rules are violated as no arrays are accessed by a[i][j]: a is a pointer, a[i] is an array but is not accessed (it is automatically converted to a pointer, per footnote 1 below), and a[i][j] has type int and accesses an object with effective type int, so C’s aliasing rules in C 2018 6.5 7 are satisfied.
Footnotes
1 This is because when an array is used in an expression, it is automatically converted to a pointer to its first element, except when it is the operand of sizeof, is the operand of unary &, or is a string literal used to initialize an array.

Related

I am trying to pass an array to my function but nothing shows up on the output, why?

I have:
void displayMatrix(int a[][]){
int howManyRows = sizeof(a)/sizeof(a[0]);
int howManyColumns = sizeof(a)/sizeof(int);
int r, c;
for (r = 0; r < howManyRows; r++){
if (r >= 1){
printf("\n");
}
for (c = 0; c < howManyColumns; c++){
printf("%d ",a[r][c]);
}
}
}
but when I create an array
int samp[4][5] = {
{1,2,3,4,5},
{6,7,8,9,10},
{11,12,13,14,15},
{16,17,18,19,20}
};
and pass it to the function in main, nothing shows up on the screen. What is going on?
This happens because in function parameters, arrays decay to pointers on the top level. So your function
void displayMatrix(int a[4][5])
is no different from
void displayMatrix(int (*a)[5])
Regarding N1570, Section 6.7.6.3, Paragraph 7:
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, ...
This results in an unexpected behavior:
int howManyRows = sizeof(a)/sizeof(a[0]);
Where sizeof(a) == sizeof(void*) and sizeof(a[0]) == 5*sizeof(int)
Usually the length of 5 integers is longer than a pointer, so you get howManyRows = 0 after all.
Try hard-coding # of rows or pass it by an extra parameter:
void displayMatrix(int a[][5], int rows)
This is a classic gotcha of C programming. You have to understand what an array in C really is and how it works to understand why this code will not work.
When you create an "array" as such of size x, the compiler sets aside enough memory to hold x elements of some some object. This is really just a lump of memory with nothing otherwise special about it. If the array is one of 4 integers, each 4 bytes in size, then the compiler sets aside 4 * 4 bytes (16). The variable that contains the array is in fact nothing more than a pointer to the start of the array. Accessing the array is a simple matter of going to the start, and moving forward over the required number of bytes to find the object specified.
That said, arrays do have a special property within the function they are declared. They are known as arrays and their total size is stored, and can be accessed by the sizeof operator. This property is not valid anywhere outside of that function. If you pass the array as a parameter, all that is passed is the pointer and not any information about the array. There is no way to get its size. Your code here will never work and cannot be made to work.

passing structure array to function

I've been learning C for about 2 months, still a novice:(
I know there are other similar questions on this site. I've read them, but still couldn't really understand, so here I am. Below is my code:
//naming my structure as ball
typedef struct ball_room {
int enter;
int exit;
} ball;
//I've omitted some irrelevant details for brevity
int i, n, max;
scanf("%d", &n);
ball person[n];
.../*assign values to ball.enter and ball.exit with user input*/
max = 1;
for (i = 0; i < n; i++)
if (ball_room(person, person[i].enter, n) > max)
max = ball_room(person, person[i].enter, n);
printf("%d\n", max);
return 0;
}
and below is my function receiving the array:
//This function returns the number of people in the ballroom
//when b[j](person[j] in "main") enters
int ball_room(ball *b, int i, int n)
{
int people = 0, j;
for (j = 0; j < n; j++)
if (b[j].enter <= i && b[j].exit > i)
people++;
return people;
}
my question is that why is it b[j].enter instead of b[j]->enter, which my compiler wouldn't accept?
In my limited experience, when manipulating structure itself (the object), I use . to go inside the structure, and when it's a pointer (the address), I use -> (hope this is correct.)
And in this case, I pass the array to function using ball *b, which represent the address of person[0], so I can access the whole array. But shouldn't ball *b be in the form of a pointer and therefore I should use -> to access its content? It's just an address that I pass to the function.
This is my first time doing something with an array of structures, please help me get this clear, thank you!
Given ball *b, b[j] is an element from the elements that b points to. Thus b[j] is not a pointer; it is a struct. Since it is a struct, you use . to refer to members in it.
The definition of b[j] in the C standard is that it is *((b)+(j)). So it takes the pointer b, moves j elements beyond it, and then applies *.
Since * is already applied in b[j], you do not need ->, just ..
you use . instead of -> because of this declaration of parameters:
int ball_room(ball *b, int i, int n)
b is expected to be pointer to data with type ball, so you can access it in various ways:
array way: e.g. b[5].somefield = 15 - you use dot here, because if b is of type ball *, it means that b is pointer OR it is array of objects with type b, if it's array of objects with type b (which is your case) you use . to access fields of object
pointer way: e.g. (b+5)->somefield = 15 - it will do exactly same thing as code above, but you will access data in pointer way
In C/C++ an array devolves into the address of it's first member. So when you pass the array to ball_room what actually gets passed is &ball[0].
Now inside ball_room the reverse happens. b is a pointer to ball. But here you use it as an array b[j]. So it un-devolves back into an array of structs. So what b[j] gives you is the struct and not a pointer to a struct. Consequently you access it using . instead of ->.
You can also use (b + j)->somefield. Or for even more fun how about writing j[b].somefield. The later being a really confusing left-over from the eraly compiler days when a[b] truly got turned into *(a + b) internally.
For explanation of the current issue, see Eric's answer; in some of the answers given so far there is dangerous wording applied, so just to make clear: When do we have an array and when a pointer???
Consider the following:
int a[7];
As long as we can refer to a directly, we still have an array and can use any operations that are valid on, e. g. getting size:
size_t n = sizeof(a); // size in bytes, not ints, i. e. 7*sizeof(int)
You can pass arrays to functions or even do pointer arithmetics on:
f(a);
a + 1;
In both cases, the array "decays" to a pointer, though, and the result is a pointer as well. Be aware that you can assign new values to a pointer, but not to an array itself (you can assign new values to the array's elements, directly or via pointer), so you cannot do things like ++a either.
When an array decays to a pointer, it gets a pointer to its first element:
int* ptr = a;
int* ptr = &*a; // only pointers can be dereferenced -> a decays!
int* ptr = &a[0]; // short for &*(a + 0)...
All result in exactly the same; however, the following is invalid:
int* ptr = &a;
Taking the address of an entire array actually is possible, but the resulting pointer is not of type "pointer to element" nor of type "pointer to pointer to element" (int** in the example), but of type "pointer to array of specific size". Syntax for is ugly, though, but the following would be legal again:
int(*aptr)[7] = &a;
You need to read: if I dereference ptr, I get int[7]...
Once decayed, there is only a pointer to the array left (more precisely: to one of the array elements, directly after decaying, to the first; array and first element always share the same address, so, although of different type, both pointers ptr and aptr from above hold exactly the same value). Pointers can be moved around within the array, but they do not hold as much information as the array itself, especially, the array size gets lost. This is why one needs to pass the array's length together with the pointer to functions (if needed; another variant is a sentinel value denoting the array end such as the terminating null character in strings or the null pointer following the string arguments in main's arguments list):
int a[7];
f(a, sizeof(a)/sizeof(*a)); // division: sizeof is in bytes, dividing by size
// of first element gives number of elements
Possibly with f as:
void f(int b[], size_t n)
// ^^^^^^^ in function parameter lists, just alternative syntax for int* b !!!
// however, we can show more explicitly that we expect a pointer
// to an array this way...
{
size_t m = sizeof(b); // as b is a POINTER, gives the constant (but hardware specific!)
// size of a pointer (on typical modern 64-bit hardware 8 bytes),
// no matter what size of the array being pointed to is!!!
while(n)
{
*b++ = n--;
// ^^ advances pointer, NOT array!
}
}
Hope this helps to avoid confusion.
In C, the array name is a pointer to array’s first element, hence your function declaration has name ball *band works when you pass a ball[] instance.
Try dynamically allocating the memory by using malloc() and passing that pointer to your function.

Passing an array as double pointer in c warning message

I am trying to run this code but there is a compiler warning:
for the statement invoking the print function
print(arr,3,4);
passing argument 1 of 'print' from incompatible pointer type
[-Wincompatible-pointer-types] main.c /arrays line 23 C/C++ Problem
for the printf statement:
format '%d' expects argument of type 'int', but argument 2 has type
'int *' [-Wformat=] main.c /arrays line 6 C/C++ Problem
The code is to pass a 2D array to a function which receives it as a double pointer and print it inside the function.
void print (int **A, int m, int n){
for(m = 0; m < 3; m++){
for(n = 0; n < 4; n++){
printf("%d ", *((A+(m * 4) + n)));
}
printf("\n");
}
}
int main()
{
int arr[][4]={{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
};
print(arr,3,4);
return 0;
}
1) Passing a 2D array as a double pointer is incorrect in C?
here
The above link points only to C++? or this is possible in C as well?
2) If I am using a single pointer then which of the following assignments are correct/incorrect?
Listing 1:
int *ptr;
ptr = &arr[0][0];
for(int i = 0; i < 3; i++){
for(int j = 0; j < 4; j++){
printf("%d ", *(ptr + (i * 4) +j));
}
printf("\n");
}
Listing 2:
int * ptr;
ptr = arr;
Let’s start with:
int arr[][4]={{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
};
print(arr,3,4);
In print(arr,3,4);, arr is an array. Specifically, it is an array of 3 elements, each of which is an array of 4 elements, each of which is an int. Thus, arr is an array of 3 arrays of 4 int. You have probably heard or read that arrays “decay” to pointers. This is a colloquial term. The actual rule, which you can find in clause 6.3.2.1, paragraph 3, of the C 2011 standard, is:
Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type “array of type” is converted to an expression with type “pointer to type” that points to the initial element of the array object and is not an lvalue.
Here is how this rule applies to arr in print(arr,3,4);:
arr is an identifier, meaning it is the name of some object. As such, it designates its object. That object is an array of 3 arrays of 4 int.
This array is not the operand of sizeof or Alignof or &, and it is not a string literal. So, following the rule, it is converted from an array of 3 arrays of 4 int to a pointer to the first array of 4 int.
What happens next? Nothing. The expression we have is a pointer to an array of 4 int. There is no rule that says a pointer to an array is converted to a pointer to a pointer. We have a pointer to an array, but that array is not used in an expression yet, not in the simple expression arr. So it is not converted.
This means what you are passing to print is a pointer to an array of 4 int. But your declaration for print says it takes a pointer to a pointer to an int. Those are different things, and they are incompatible, so the compiler warns you.
(To see they are incompatible, consider the difference to a pointer to an array of 4 int and a pointer to a pointer to int. The memory at a pointer to an array of 4 int contains 4 int values. The memory at a pointer to a pointer to an int contains a pointer. These are very different things.)
Next, consider:
void print (int **A, int m, int n)
…
printf("%d ", *((A+(m * 4) + n)));
We know from above that you ought to change int **A to int (*A)[4], which is a pointer to an array of 4 int. You can also change it to int A[][4], because there is a rule in C that such a parameter declaration will be automatically adjusted to be int (*A)[4], as a convenience. However, suppose you keep it as int **A. Then does *((A+(m * 4) + n)) mean?
Since A is a pointer to a pointer to an int, then A+(m * 4) means to add m * 4 to the pointer. (That is strange spacing, by the way. m and 4 are more tightly bound by the higher-precedence multiplication than A and (m * 4) are by the addition, so why do they have looser spacing? A + m*4 would portray the meaning better.) Then A+(m * 4) + n means to add n to that. In total, we have moved m*4+n elements beyond where A points. Since A points to a pointer, we have advanced the pointer by m*4+n pointers. Then *((A+(m * 4) + n))) dereferences that. When you dereference a pointer to a pointer to an int, you get a pointer to an int. So the result of this expression is a pointer. But you wanted an int.
The link you reference talks about a “2D array”. The kinds of arrays it talks about are implemented using pointers to pointers. To create such an array, you create an array of pointers, and then you set each of those pointers to point to the elements of a row. Then a pointer to that array of pointers acts like a 2D array, in that A[i][j] refers to element j of row i. If you had an array like that, you could refer to element n of row m using A[m][n]. Equivalently, you could refer to it with *(*(A+m)+n). What this expression means is:
Take the pointer A and add m to it. Since A points to a pointer to an int, adding m advances the value of the pointer to point to m pointers further along. That is where we should find the pointer to the elements of row m.
*(A+m) gets the value of the pointer that A+m points to. This value should be a pointer to the elements of row m, specifically a pointer to the first element (with index 0).
*(A+m)+n advances the value of the pointer to point n int further along. That is where we should find element n of row m.
*(*(A+m)+n) gets the value of the int that *(A+m)+n points to.
Now suppose instead you changed print to be print(int A[][4], int m, int n). Then your printf statement should use A[m][n], just as before. Or it could use *(*(A+m)+n), also just as before. But, in this case, the expression is evaluated:
A is a pointer to an array of 4 int. Adding m to it advances value of the pointer to point m arrays further along.
*(A+m) gets the object that A+m points to. This object is an entire array. So this is an expression that designates an array. Following the C rule about arrays in expressions, this array is converted to a pointer to its first element. Thus, *(A+m) becomes a pointer to the first element of the array numbered m.
*(A+m)+n advances the value of the pointer to point n int further along. That is where we should find element n of row m.
*(*(A+m)+n) gets the value of the int that *(A+m)+n points to.
Thus A[m][n] has the same end result for pointers-to-pointers, for pointers-to-arrays, and for arrays-of-arrays, but the steps it goes through for each are different. C knows the types of each subexpression and processes them differently, to achieve the same result.
Finally, suppose you pass &A[0][0] to print and change its parameter to int *A. Now what is the expression *((A+(m * 4) + n)))? In this case, you are treating the array of 3 arrays of 4 int as one big array of 12 int. Then you calculate where element n of row m is. In this case, A is a pointer to int (not a pointer to a pointer to int). So A+(m * 4) + n is a calculation to where element n of row m ought to be, and *((A+(m * 4) + n))) gets the value of that element.
That is a method you ought to avoid when possible. Generally, you should use C’s built-in methods of addressing array elements and avoid doing your own calculations. Whether it is strictly conforming C code or not may depend on how pedantic you are about interpreting certain passages in the C standard.
You expectation that a 2D array will decay to a pointer to a pointer is ill-founded.
To be able to use arr as an argument to print, you have the following options.
Change print to
void print (int (*A)[4], int m){ // Not need for n. It is 4
Change print to use a VLA. For this to work, m and n have to come before A.
void print(int m, int n, int A[m][n] {
Both these changes will require you to change the call also.
If your intention is an array of pointers you cannot initialize it all at once. Your array compiles to the same thing as {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
The following compiles to an array of pointers to arrays:
int arr1[] = {1, 2, 3, 4};
int arr2[] = {5, 6, 7, 8};
int arr3[] = {9, 10, 11, 12};
int* my2DArr[3] = {arr1, arr2, arr3};
It is valid for a nested loop such as this:
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 4; j++)
{
printf("%d\n", my2DArr[i][j]);
}
}
For the array in your code, you can iterate over it as if it's just one array.

difference between int a[9] and a[3][3]

What is the difference between
#include <stdio.h>
int a[9];
int
main()
{
printf("%d\n", a[1]);
}
and
#include <stdio.h>
int a[3][3];
int
main()
{
printf("%d\n", a[1]);
}
I think both result in placing same 36 byte memory buffer in .bss section, what is the difference? Or is a[3][3] syntactic sugar over a[9] - a[3*3]?
Nope, they are not same, they represent different types. In reference to your code,
In the first case, a is an one dimensional array. Hence, a[1] is of type int.
§) To print the value, %d is fine.
In the second case, however, a is a two-dimensional array. Hence, a[1] is of type int [3].
§) When passed as a function argument, it decays to a pointer to the first element, basically a int *. You'll be needing %p to print that (and cast the pointer to void *, as required by the %p format specifier).
However, if you're bothered about the memory layout for both the variables, you can check the other answer by AnT or another one which details about the memory layout of multi-dimensional arrays.
int a[3][3] is a semantic sugar of sorts over int a[9]. The raw memory layout it the same (i.e. it is a contiguous block of 9 ints), but the language-level access syntax is different. When accessing a[3][3] as a[i][j], the apparent 2D-indexing is converted by the compiler to 1D indexing using i * 3 + j formula. The latter index translation scheme is easily extendable to any number of dimensions.
What is the difference between
int a1[9];
and
int a2[3][3];
(I've changed the names so I can talk about the declarations more easily.)
The difference is that they're of different types. They both have the same underlying memory layout,each consisting of a contiguous region of memory 9 times the size of an int. But a1 is an array of 9 int objects, while a2 is an array of 3 objects each of which is an array of 3 int objects. It's a multidimensional array, which in C is precisely an array of array, nothing more, nothing less.
The difference is not just syntactic sugar. You might get the same generated code for certain operations, for example a1[1] and a2[0][1]. But, for example, a1[3] refers to the 4th element of the array a1, while a2[0][3], though you might think it refers to the same thing, actually has undefined behavior. (Compilers are permitted, but not required, to perform run-time array bound checking, and are permitted to assume that array references do not go past the end of the indexed array object.)
printf("%d\n", a2[1]);
As others have mentioned, this has undefined behavior. a2[1] is an array object of type int[3]. An expression of array type is, in most contexts, converted to an expression of pointer type, so a2[1] ends up being of type int*, and yields a pointer to the initial element of the second row of a2. To print a pointer value, use %p -- which requires an argument of type void*, so you need to cast it:
printf("%p\n", a2[1]);
Recommended reading: Section 6 of the comp.lang.c FAQ.

multiArray and multiArray[0] and &multiArray[0] same?

On 6th line instead of multiArray[0], when I write multiArray, program still works. Don't understand why. I was thinking before that multiArray is a pointer to multiArray[0] which is a pointer to multiArray[0][0]. So multiArray alone is a pointer to a pointer. multiArray[0] is a pointer to a 4 element int array. So it seems that multiArray and multiArray[0] must be different. But in below code, both work. Print function I wrote expects a pointer to a 4 element int array. So only multiArray[0] must work and multiArray must not work. But both works. Didn't understand that.
#include <stdio.h>
void printArr(int(*ptr)[4]);
int i, k;
int main(void){
int multiArray[3][4] = { { 1, 5, 2, 4 }, { 0, 6, 3, 14 }, { 132, 4, 22, 5 } };
int(*point)[4] = multiArray[0];
for (k = 0; k < 3; k++)
{
printArr(point++);
}
getchar();
}
void printArr(int(*ptr)[4]){
int *temp = (int *)ptr;
for (i = 0; i < 4; i++)
{
printf("%d ", *temp);
temp++;
}
puts("\n");
}
Someone else wrote "Multi-dimensional arrays are syntactic sugar for 1-D arrays".
This is sort of like saying that int is just syntactic sugar for a unsigned char[4] . You could do away with expressions like 4 + 5 and get the same result by manipulating arrays of 4 bytes.
You could even say that C is just syntactic sugar for a Universal Turing Machine script, if you want to take this concept a bit further.
The reality is that multi-dimensional arrays are a part of the type system in C, and they have syntax associated with them. There's more than one way to skin a cat.
Moving on, the way C arranges what we are calling a multi-dimension array is to say: "Arrays can only have one dimension, but the element type may itself be another array". We say "multi-dimension array" as a matter of convenience, but the syntax and the type system actually reflect the one-dimensional nature of the array.
So, int multiArray[3][4] is an array of 3 elements. Each of those elements is an array of 4 ints.
In memory, an array's elements are stored contiguously -- regardless of what the element type is. So, the memory layout is an array of 4 int, immediately followed by another array of 4 int, and finally another array of 4 int.
There are 12 contiguous int in memory, and in the C type system they are grouped up into 3 groups of 4.
You will note that the first int of the 12 is also the first int of the first group of 4. This is why we find that if we ask "What is the memory location of the first int?", "What is the memory location of the first group of 4 ints?", and "What is the memory location of the entire bloc of 12 ints?", we get the same answer every time. (In C, the memory location of a multi-byte object is considered to start at the location of its first byte).
Now, to talk about the pointer syntax and representation. In C, a pointer tells you where in memory an object can be found. There are two aspects to this: the memory location of the object, and what type of object it is. (The size of the object is a corollary of the type).
Some presentations only focus on the first of those, they will say things like "A pointer is just a number". But that is forgetting about the type information, which is a crucial part of a pointer.
When you print the pointer with %p, you lose the type information. You're just putting out the location in memory of the first byte. So they all look the same, despite the fact that the three pointers are pointing at differently-sized objects (which overlap each other like matruskha dolls).
In most implementations of C, the type information is all computed at compile-time, so if you try to understand C by comparing source code with assembly code (some people do this), you only see the memory-location part of the pointer. This can lead to misunderstanding if you forget that the type information is also crucial.
Footnote: All of this is independent of a couple of syntax quirks that C has; which have caused a lot of confusion over the years (but are also useful sometimes). The expression x is a shortcut for &x[0] if x is an array, except when used as the operand of & or sizeof. (Otherwise this would be a recursive definition!). The second quirk is that if you write what looks like an array declarator in a function formal parameter list, it is actually as if you wrote a pointer declarator. I stress again that these are just syntax oddities, they are not saying anything fundamental about the nature of arrays and pointers, which is actually not that complicated. The language would work just as well without both of these quirks.
Multidiemensional arrays var_t arr[size_y][size_x] provide means of declaring and accessing array elements (memory) in a conveniant manner. But all multidiemensional arrays are internally continuous memory blocks.
You may say that arr[y][x] = arr[y*cols+x].
In terms of pointer-level, the pointers multiArray and multiArray[0] are the same, they're int* - though the formal type for arr will be int (*)[2]. Using that type will allow one to take advantage of all pointer mechanics (++ on such pointer will move the address by 8 bytes, not 4).
Try this:
void t1(int* param)
{
printf("t1: %d\n", *param);
}
void t2(int** param)
{
printf("t2: %d\n", **param);
}
int main(void) {
int arr[2][2] = { { 1, 2 } , { 3, 4 } };
t1(arr); // works ok
t1(arr[0]); // works ok
t2(arr); // seg fault
t2(arr[0]);
}
int(*point)[4] = multiArray[0];
This works because both multiArray[0] and multiArray point to same address, the address of first element of array: multiArray[0][0].
However in this case, you may get a warning from compiler because type of multiArray[0] is int* while of point is int [4]*(pointer to array of 4 integers).

Resources