This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
C sizeof a passed array
instead of sending the sizeof:
void main()
int x[] = {1, 2, 3, 1, 3, 0, 6};
dupes(x, sizeof x / sizeof x[0]);
...
}
int dupes(int a[], int n)
...
...
why cant i call it with only the array:
int main()
{
int x[] = {1, 2, 3, 1, 3, 0, 6};
dupes(x);
...
}
int dupes(int a[])
{
int n = sizeof a / sizeof a[0];
when i do this the size always gets 1 - and it cant be 1 casue i'm sending an array with 7 elements!
sizeof is not a function, it a compiler operator (so you don't "call" sizeof, you just use the sizeof operator). The compiler has no way to know the size of the formal argument a for your function dupes (and treat is as int* a, i.e. a pointer formal).
sizeof is always computed at compile time.
Arrays decay into a pointer to their first element when they are passed to a function. sizeof only works if you pass the actual original array.
In other languages array references always carry the length along with them but the C philosophy is against this kind of hidden preformance penalty so you have to pass the length yourself.
This prototype in C:
int dupes(int a[])
{
}
is equivalent to this prototype:
int dupes(int *a)
{
}
You array argument a is converted to a pointer to int.
Regarding function declarators, this appears in 6.7.5.3p7 in C99 standard
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to
‘‘qualified pointer to type’’, where the type qualifiers (if any) are
those specified within the [ and ] of the array type derivation.
Arrays in C are second class citizens, you cannot pass arrays to functions. You can just pass a pointer to the first element of the array.
So when you compute sizeof a, the result is the size of the pointer and not the size of the array.
sizeof a / sizeof a[0] in the body of your function actually computes this: sizeof (int *) / sizeof (int) which yields 1 in your system, because the size of an int * is the same as the size of an int in your system.
It's because when You pass an array to a function, it is copied into a pointer and the information about it's size is lost. In the called function it is just a normal pointer.
So size of (a) becomes 4 (because its a pointer) and sizeof (a[0]) is also 4 (because its an integer), so n equals 1
When an expression of type "N-element array of T" appears in most contexts, it is implicitly converted to an expression of type "pointer to T" and its value is the address of the first element in the array. The only exceptions to this rule occur when the array expression is an operand of the sizeof and unary & operators, or is a string literal being used to initialize an array in a declaration.
When you call dupes, the expression x is converted from type "7-element array of int" to "pointer to int", so the dupes function receives a pointer value, not an array. In the context of a function parameter declaration, T a[N] and T a[] are equivalent to T *a.
So in the dupes function, sizeof a / sizeof a[0] is equivalent to sizeof (int *) / sizeof (int), which in your case is 1.
sizeof a[0]; retruns the first element of the array
and when you pass an array it returns the address of the first element of the array so you have to specify the length also.
internally traversing an array as follow:
int a[]={1,2,3};
when you pass a[] as a function argument it sends the address of the element a[0] that is internal address of the 1 and calculates a[1] as
address of a[0] + size of int (type of an array)
when you type sizeof(a[]) it will return the sizeof(int pointer) as a[0] contains the internal address(integer pointer).
It's because you cannot pass an array directly to a function. You can only pass a pointer to an element within such an array.
In function parameters, the [] does not mean array, it means a pointer
int dupes(int a[])
is exactly the same as
int dupes(int *a)
When you use the name of an array as a value, it actually means the pointer to the 1. element in that array. And a pointer is just that, a pointer. It doesn't know it's pointing into an array of a given size.
So e.g. int *a = x; will assign a pointer to the first element in x to a- Same thing when you call a function, dupes(x) will pass a pointer to the first element in x as the argument to the dupes function. This is exactly the same as calling dupes(&x[0])
Inside the dupes() function, its argument is just a pointer. So sizeof x/sizeof x[0] is equivialent to sizeof(int*)/sizeof(int), which is 1 if your pointers are the same size as an int.
(However, since you passed a pointer to the 1. element in an array, you can increment that pointer to access the other elements. e.g. x + 2 will make x point to the 3. element (counting from 1) in that array. And if you dereference that pointer, you get its value: *(x + 2). And remember that as a value, x[2] is the same as *(x + 2)
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is array name a pointer in C?
Suppose I have a char array say arr and arr will represent the address of first element so arr++ should be perfectly legal then why compiler says 'lvalue required'.
Also if I do:
arr=arr+1 then why it is invalid conversion. I am just increasing pointer by one. Compiler tells that on LHS the operand type is char[4] but on RHS it is char *.
main()
{
char arr[]={'a','b','c','d'};
for(;arr!=arr+4;arr++) //lvalue required
printf("%c",*arr);
}
An array name is not a variable that can be assigned to. If you want to modify it, you should use a pointer variable:
char *arr_ptr = arr;
You can then do arr_ptr++ and arr_ptr = arr_ptr+1;
Arrays aren't pointers. arr does not represent an address.
An array name, or any expression of array type, is implicitly converted to a pointer to the array's first element unless it's either:
The operand of a unary & (address-of) expression (which yields the address of the whole array, not of its first element -- same memory address, different type); or
The operand of a sizeof operator (sizeof arr yields the size of the array, not the size of a pointer); or
The operand of an _Alignof operator (_Alignof arr yields the alignment of the array, not the alignment of a pointer); or
A string literal in an initializer that's used to initialize an arrary object.
_Alignof is new in C2011.
(The conversion is often referred to as a "decaying".)
None of these apply here, so arr++ tries to increment a pointer object. The problem is, there is no pointer object, just a pointer value. arr, after it decays to a pointer value, is not an lvalue, which means it cannot appear on the left side of an assignment or as the operand of ++.
Similarly, given:
int x;
the expression x is an lvalue, so you can write x = 42; -- but the expression (x + 1) is not an lvalue, so you can't write (x + 1) = 42;. You can assign to x because it refers to an object. (x+1) doesn't refer to an object, so there's nothing to assign to. arr, after it decays, doesn't refer to an object either (you have an array object, but there's no pointer object).
(Your use of arr != arr + 4 should have been a clue; that can never be true.)
Rather than this:
char arr[] = {'a', 'b', 'c', 'd'};
for (; arr != arr + 4; arr++) {
printf("%c", *arr);
}
you can write this:
char arr[] = {'a', 'b', 'c', 'd'};
char *ptr;
for (ptr = arr; ptr != arr + 4; ptr++) {
printf("%c", &ptr);
}
Incidentally, at the top of your program, you should change this:
main()
to this:
#include <stdio.h>
int main(void)
Also, run, do not walk, to the comp.lang.c FAQ and read section 6, "Arrays and pointers".
I am learning pointers and arrays in C and Here is a question that confused me for a while:
So the name of a 1D int array is a constant pointer to int, which points to the first element in that array. So when we evaluate the name of a 1D array, we should get the address of the first element in the array.
For a 2D int array, the array name is a pointer to the first array of int. So what will be the value of a 2D int array's name? I think it should be the address of the first array in this 2D array. But how is the address of an array defined in C? Is it just the address of the first element in that array?
So the name of a 1D int array is a constant pointer to int
This is wrong, and it is often taught badly. An array is an array. Here is some code for analogy:
int x = 5;
double d = x + 1.2;
In the second line x is converted to double for purposes of addition. This does not change x which is still an int, the result of conversion is "temporary" and only exists until the addition is finished. The conversion is demanded by the conditions of the + operator that both arithmetic operands must be brought to a common type (double in this case).
In the array case, say we have char *p = arrayname + 1 , then arrayname is still an array. But it is converted to a temporary pointer so that the addition can occur (the + operator requires this, it can add a pointer and an integer). The temporary pointer points to the first element of the array, but it is not correct to say that the temporary pointer is the array.
Most operators invoke this conversion of an array to a temporary pointer, but some do not. So it is not correct to say that arrayname is a pointer because it may be used with an operator that does not convert the array to a pointer, e.g. sizeof arrayname.
The result of converting an array to a pointer is a pointer to the first element of that array. This is always true even if the first element is itself an array.
But how is the address of an array defined in C? Is it just the address of the first element in that array?
No. Every variable has an address, this applies to arrays and non-arrays. If you understand the address of an int then you also understand the address of a 2x2 array of char.
Let's clear up some things:
int a = 24;
The above is many things:
a declaration: we declare a variable named a of type int.
a definition: an object of type int is created.
an initialization: this object is initialized with the value 24
So let's recap: an object of type int is created with the value 24 and the variable a names it.
Now let's apply the same to the following:
int a1[3] = {0, 1, 2};
a declaration: we declare a variable named a1 of type int[3] (aka array of 3 integers).
a definition: an object of type "array of 3 integers" is created
an initialization: the object is initialized with {0, 1, 2}
The variable a1 names that object.
So the name of a 1D int array is a constant pointer to int, which
points to the first element in that array.
Wrong. I know you might have been told or read this, but it is incorrect. An array is not a pointer!! Arrays and pointers are different types. That being said, for convenience and historical reasons, in most situations (but not all!) an array decays to a pointer to the first element:
int a1[3] = {0, 1, 2};
int* p = a1; // here a1 decays to a pointer to its first element
In the above snippet p points to the element 0 of the array a1
You can view 2D or 3D or nD array the same way:
T a2[3] = {l0, l1, l2};
Let's say T is a type. The above is an "array of 3 Ts".
if T is int then we have int a2[3] = {0, 1, 2} - an array of 3 integers. We call this an 1D int array.
but if T is int[2] then the above becomes int a2[3][2] = {{00, 01}, {10, 11}, {20, 21}} - you can see it as "an array of 3 Ts" or "an array of 3 int[2]" or "an array of 3 arrays of 2 integers".
And we can apply the same decaying rule:
int a2[3][2] = {{00, 01}, {10, 11}, {20, 21}};
int (*p2)[2] = a2; // a2 decays to a pointer to its first element.
// Its first element is an array of 2 int.
// So a2 decays to `int (*)[2]` - a pointer to an array of two elements.
In the above a2 points to the element {00, 01} of the array.
An arrays name is not a pointer. In most cases when the name of an array is used, it gets implicitly *converted* to a pointer to its first element, it is said, that the array decays into a pointer.
The name of an array does not decay to a pointer when it is the argument of the address-of operator (&), the sizeof-operator and when a string literal (which is an array of some character type) is used to initialize an array *).
That said, a two-dimensional arrays
T arr[COLS][ROWS];
first element is an array of type T[ROWS]. So arr decays to a pointer of type T(*)[ROWS] which points to the first element of arr.
*) If you might want to add that arrays also do not decay when they're the operand of the _Alignof-operator or read that somewhere else:
#EricPostpischi: Arrays cannot be operands of _Alignof. Including _Alignof in the exceptions for array conversion was an error in the C 2011 standard. _Alignof operands can only be types, not expressions.
When a 2D array decays to a pointer, you have a pointer to an array. Here's an example of what this would look like:
int arr[5][6];
int (*p)[6] = arr;
An array is not a pointer. An array's name, when used in an expression, "decays" into a pointer to the first element.
Strictly speaking, C only has one-dimensional arrays, and a 2D array is really just an array of arrays.
1D array:
The first element of int arr [x], is an int.
When arr is used in an expression, you get a pointer to that element, int*.
When doing pointer arithmetic on this pointer, each item has the size of the first element = sizeof(arr[0]).
2D array:
The first element of int arr [x][y] is an int [y].
When arr is used in an expression, you get a pointer to that element, int (*)[y].
When doing pointer arithmetic on this pointer, each item has the size of the first element = sizeof(arr[0]).
So it's the same rule. The int(*)[y] array pointer follows the same rules of pointer arithmetic as the ordinary pointer. But you can de-reference it one step further to get individual int in the array of arrays.
"So what will be the value of a 2D int array's name?"
"I actually understand that an array is not a pointer. In my question,
what I actually mean is that when the name of an array is used in an
expression, the compiler will generate the pointer constant."
You have to be careful here. As a follow-on to your comment below your question, there are nuances in how the array/pointer conversion rules apply that effect the type that results from the conversion. That will dictate whether and how you can use the array name in an expression.
"... the compiler will generate the pointer constant."
No. The compiler does not generate a pointer constant, the compiler follows C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3). When the array name is used in an expression, the expression is evaluated with the address that results from the conversion of the array to a pointer (subject to the 4-exceptions stated in paragraph 3).
The rule regarding array/pointer conversion does not depend on number of dimensions, the rule is applied the same regardless. However, the type of the pointer that results from the conversion does depend on the number of array dimensions. That is critical and that will dictate whether your use of the array name is valid.
One way to help cement what is happening in the conversion is take it step-by-step. Start with a 1D array and work your way up.
6.3.2.1 - 1D Array Conversion To Pointer On Access
When you have a simple array, e.g.
int array[10];
On access to array, is converted to a pointer to the first element, e.g., the address of the element, &array[0]. (which is simply a pointer to int, or with formal type int *)
6.3.2.1 - 2D Array Conversion To Pointer On Access
With a 2D array, rule applies just the same, e.g.
int array[10][10];
Here array, a 2D array, is essentially an array of 10 - int[10] arrays (an array of 1D arrays). On access array[10][10] is converted to a pointer to the first array of 10-int in exactly the same manner, &array[0][0] (which results in a pointer to an array of int[10] or with formal type int (*)[10]) It is not a pointer-to-pointer (e.g. int**), it is very specifically a pointer to an array of int[10].
(note the important difference between int *[10] (an array of 10 pointers which on access will become a pointer-to-pointer) and int (*)[10] (a pointer to array of 10 int))
Answer
"So ... the value of a 2D int array's name when used in an expression" -- is the address of the first 1D array of integers that make up the 2D array with formal type int (*)[N] (where N is the number of elements per-row).
Nuance In How The Standard Is Applied
The type is critical for the proper use of the array name. With a 2D array, the resulting address is a pointer to an array. What results if you dereference that pointer? (Answer: an array) What happens when you access that array through the derefernced pointer? (hint: the conversion on access rules apply again). You must know what the pointer type resulting from the conversion will be in order to properly use the array name in an expression.
An Example May Help
Or it may not, but working with the pointer types that result from the array access and pointer conversion may help things sink in. Below, the example declares a simple 4x3 2D array of int. It then declares a pointer (p) of proper type to allow the array name to be used in an expression assigning the array address to the pointer. The pointer initialized with the array name is then used to further initialize an integer pointer (ip) to the first element in the first array.
The example then outputs the address for each element, and then using the pointer p outputs the address of the beginning of each row-array that makes up the 2D array. Finally the code enters a validation loop comparing the addresses of each element by (1) array index, (2) the address held by pointer p using an offset, and (3) address held by ip. The purpose being the use of each of the different pointers resulting from the expression assigning the array name to then reference each element and ensuring the addresses held by each pointer agree.
#include <stdio.h>
int main (void) {
int array[ ][3] = { {1, 2, 3}, /* 2D array values */
{3, 4, 5},
{5, 6, 7},
{7, 8, 9} },
(*p)[3] = array, /* pointer to array */
*ip = *p; /* integer poiner */
size_t size = sizeof array,
nele = size / sizeof **array,
nrow = size / sizeof *array,
ncol = sizeof *array / sizeof **array;
printf ("2D array statistics:\n\n"
" size: %zu (bytes)\n nele: %zu (ints)\n"
" nrow: %zu\n ncol: %zu\n",
size, nele, nrow, ncol);
puts ("\naddress of each array element:\n");
for (size_t i = 0; i < nrow; i++) {
for (size_t j = 0; j < ncol; j++)
printf (" %p", (void*)&array[i][j]);
putchar ('\n');
}
puts ("\naddress of each 1D array:\n");
for (size_t i = 0; i < nrow; i++)
printf (" %p\n", (void*)p[i]);
puts ("\nvalidating each array element address by index & pointer:\n");
for (size_t i = 0; i < nrow; i++) {
for (size_t j = 0; j < ncol; j++) {
if (ip != &array[i][j] || ip != *p + j) {
fprintf (stderr, "address validation failed for "
"array[%zu][%zu]\n(%p != %p || %p != %p)\n",
i, j, (void*)ip, (void*)&array[i][j],
(void*)ip, (void*)(p + j));
return 1;
}
ip++;
}
p++;
}
puts (" done!");
return 0;
}
Example Use/Output
$ ./bin/array_2d_access
2D array statistics:
size: 48 (bytes)
nele: 12 (ints)
nrow: 4
ncol: 3
address of each array element:
0x7ffe7c9a9780 0x7ffe7c9a9784 0x7ffe7c9a9788
0x7ffe7c9a978c 0x7ffe7c9a9790 0x7ffe7c9a9794
0x7ffe7c9a9798 0x7ffe7c9a979c 0x7ffe7c9a97a0
0x7ffe7c9a97a4 0x7ffe7c9a97a8 0x7ffe7c9a97ac
address of each 1D array:
0x7ffe7c9a9780
0x7ffe7c9a978c
0x7ffe7c9a9798
0x7ffe7c9a97a4
validating each array element address by index & pointer:
done!
Let me know if that helped and whether you have any further questions.
I understand that when we use sizeof operator on an array name, it gives the total size of the array in bytes. For example
int main(int argc, const char * argv[]) {
int a[][5] = {
{1,2,3,4,5},
{10,20,30,40,50},
{100,200,300,400,500}
};
int n=sizeof(a);
printf("%d\n",n);
}
It gives 60 as output for 15 elements of the array. But when I write
int n=sizeof(*a);
It gives 20 as the output that is the size of the first row while *a is the base address of the 0th element of the 0th row, and its type is a pointer to an integer. And a points to the first row itself. Why is this happening?
*a is row 0 of a, and that row is an array of five int.
In most expressions, an array is automatically converted to a pointer to its first element. Thus, when you use *a in a statement such as int *x = *a;, *a is converted to a pointer to its first element. That results in a pointer to int, which may be assigned to x.
However, when an array is the operand of a sizeof operator, a unary & operator, or an _Alignof_ operator, it is not converted to a pointer to its first element. Also, an array that is a string literal being used to initialize an array is not converted to a pointer (so, in char foo[] = "abc";, "abc" is used as an array to initialize foo; it is not converted to a pointer).
*a is not a pointer, it's an int[5], which is coherent with your reading of 20 assuming a 4-byte int.
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
The expression a has type "3-element array of 5-element array of int"; thus, sizeof a should yield 3 * 5 * sizeof (int).
The expression *a is the same as the expression a[0] (a[i] is defined as *(a + i) - *a is the same as *(a + 0), which is the same as a[0]). Both *a and a[0] have type "5-element array of int"; thus sizeof *a and sizeof a[0] should both yield 5 * sizeof (int).
However...
If you pass a to a function, such as
foo( a );
then a is not the operand of the sizeof or unary & operators, and the expression will be converted from type "3-element array of 5-element array of int" to "pointer to 5-element array of int":
void foo( int (*a)[5] ) { ... }
If you computed sizeof a in function foo, you would not get 5 * sizeof (int), you would get sizeof (int (*)[5]), which, depending on the platform, would be 4 to 8 bytes.
Similarly, if you passed *a or a[i] to a function, what the function actually receives is a pointer to int, not an array of int, and sizeof will reflect that.
In this 2d array *a is a pointer because when you print it, its seems an address (but it is the 1st column address) :
printf("%d\n", *a);
Output : 9435248
So :
for(int i = 0;i < 3;i++)
printf("%d\n", *a[i]);
The output is :
1
10
100
When you use of *a like this : *a[3] its means you are in 3rd row and 1st column by default.
*a is the address of 1st column and we have 5 column, so when you try this :
sizeof(*a);
Output will be 20 => (5 column) * (int pointer which is 4 byte)).
A 2D array is viewed as an array of 1D arrays. That is, each row in a 2D array is a 1D array. For a given 2D array A, int A[m][n]
you can think of
A[0] as the address of row 0
A[1] as the address of row 1 etc & so on.
Dereferencing can be thought of as below,
A[i][j] = *(A[i] + j) = *(*(A+i) + j)
So when you say *A, it means A[0] which gives you the address of 1st row & not the 1st element of the matrix.
Dereference of A or *A gives the address of row 0 or A[0].
Dereference of A[0] gives the first entry of A or A[0][0] that is
**A = A[0][0].
& since you have 5 elements in the 1st row the size if 20 bytes.
Sizeof returns the size of the variable in memory expressed in bytes. This includes padding (unused bytes added by a compiler to a structure to improve performance). Your array has 15 elements of size 4. The sizeof an integer in memory is 4 in you case. You can easily verify this by running:
printf("sizeof an integer: %zu\n", sizeof(int));
It is always a good idea to use standard int types.
#inlcude <stdint.h>
uint32_t a[][5] = {
{1,2,3,4,5},
{10,20,30,40,50},
{100,200,300,400,500}
};
This will produce exactly the same code but wil clearly show the memory size of the (32 bit) integer.
In your case uint8_t (unsigned int of 8 bit) might be more appropriate. You can read more here. To get the number of elements you should devide the total memory of the array by the sizeof an element.
sizeof(a)/sizeof(a[0])
You can also use a macro to do this:
#define ARRAY_LENGTH(array)(sizeof(array)/sizeof(array[0]))
/*ARRAY_LENGTH(a) will return 15 as expected.*/
You can also look for an answer here: How can I find the number of elements in an array?
This question already has answers here:
Is an array name a pointer?
(8 answers)
Closed 6 years ago.
If I have:
int a[] = {1,2,3};
int len = sizeof(a) / sizeof(int);
I get 3, and I don't understand why.
They always taught me that an array is a pointer, doesn't store its length, but only the pointer to the first element.
Can someone explain this to me in a better way?
An array decays to a pointer to the first element of the array in most expressions. There are two cases where it does not and hence the outcome is not the same.
int array[5];
int* ptr = array; // Array decays to a pointer, OK.
// Same
int a = array[0];
int a = ptr[0];
// Same
void foo(int*);
foo(array);
foo(ptr);
// Not same
size_t s = sizeof(array); // s is 5*sizeof(int)
size_t s = sizeof(ptr); // s is sizeof(int*)
// Not same
int (*p1)[5] = &array; // p1 is a pointer to "an array of 5 ints"
int **p1 = &ptr; // p1 is a pointer to "an int*"
sizeof a returns the size of the entire array in storage units (bytes). sizeof (int) returns the size of a single integer in storage units. Since a is an array of 3 integers, sizeof a / sizeof (int) gives you 3.
They always taught me that an array that it is a pointer
"They" taught you incorrectly. Unless it is the operand of the sizeof or unary & operators, an array expression will be converted ("decay") to a pointer expression, and the value of the pointer expression will be the address of the first element of the array, but an array object is not a pointer.
When you declare a, what you get in memory looks something like this:
+---+
a:| | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
There is no object a that's separate from the array elements; there's no pointer anywhere.
They always taught me that an array that it is a pointer
That's your problem, right there. When you call a function and pass an array as an argument, it gets converted into a pointer to the first element. Otherwise, they're not equivalent. In particular, an array has an address, a type, and a size, whereas a pointer just has an address and a type.
This is a pretty common confusion among people learning C for the first time, and even some textbooks get it wrong.
edit: there are a few other cases where arrays "decay" to pointers, typically when they're used in an expression. See one of the other fine answers for a more-exhaustive treatment.
An array can decay to a pointer, but that does not mean an array is a pointer. See this SO question for details.
sizeof returns the size (in bytes) of the data type of its operand. In this case, the data type is an array of int of length three. But, since an int can be represented in different ways on different platforms, you must divide by the sizeof(int) to get the length.
See here for more details on sizeof.
The following snippet declares a 2-D array of 4 X 10 using malloc function
/* Declare a pointer to an array that has 10
ints in each row. */
int (*p)[10];
register int i, j;
/* allocate memory to hold a 4 x 10 array */
p = malloc(40*sizeof(int));
But I do not understand how does p become a 2-D array. Initially p is declared to be an array of pointers that point to int. What happens after the call to malloc ? I am unable understand this.
In C, pointers and arrays are not the same, despite looking very similar. Here p is of type "pointer to array of 10 ints". You're using it as a "pointer to array of 4 arrays of 10 ints", which is a single block of memory (the only pointer is the outermost pointer). It's basically a dynamically allocated int[4][10].
The trick to reading these definitions is to realize that they're written the same way you use the item. If you have:
*x[10];
The array subscript is applied first, then the pointer dereference. So it's an array of pointers if you define int *x[10]. If you use parenthesis to override normal precedence, you can get the pointer dereference to happen first, so you have a pointer to an array.
Confusing? It gets worse. In function arguments, the outermost array of a function parameter is converted into a pointer.
int *p[10]; // array of 10 pointer to int
int (*p)[10]; // pointer to array of 10 ints
void foo(int *p[10] /* pointer to pointer to int */);
void foo(int (*p)[10] /* pointer to array of 10 ints */);
Further, arrays are converted to pointers when you use them.
int x[10]; // array of 10 int
sizeof(x); // 10 * sizeof(int)
int *y = x; // implicitly converts to a pointer to &x[0]!
sizeof(y); // sizeof(int *)
This means that you can allocate memory for an array of arrays, then let that implicitly convert to a pointer to an array, which you in turn use as if it were an array of arrays!
Anyway, this is all very confusing so please do not ever use this in production code - at least, not without a clarifying typedef:
typedef int vector[3];
vector *array_of_vectors; // actually a pointer to a vector,
// but you can use it as an aray to a vector if enough
// memory is allocated
The memory, worth 40 ints, is reserved to the pointer p. p points at his memory block. It so happens that p chooses to organize this memory as 10 equal parts, each of which happen to hold 4 ints' worth.
That's if this code is actually correct. My C is very rusty at this point.
First, some background information:
Except when it is the operand of the sizeof, _Alignof, or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" is converted ("decays") to an expression of type "pointer to T", and its value is the address of the first element in the array. For example, given the array
int a[10];
anytime the expression a appears in the code, its type will be converted from "10-element array of int" to "pointer to int", or int *, except for cases like sizeof a, _Alignof a, and &a. If we have a 2D array of T, such as
int a[10][10];
the expression a will be converted from type "10-element array of 10-element array of int" to "pointer to 10-element array of int", or int (*)[10] (look familiar? that's the type of your pointer p).
If we want to dynamically allocate an N-element array of type T, we write something like
T *p = malloc(N * sizeof *p);
sizeof *p is equivalent to sizeof (T). In this particular case, type T is "10-element array of int", or int [10]. We want to allocate 4 such arrays, so we can write
int (*p)[10];
p = malloc(4 * sizeof *p);
This allocates space for 4 10-element arrays of int, and assigns the result to p. (sizeof *p == sizeof (int [10])).
So how does this become a 2D array?
Remember that the expression a[i] is equivalent to *(a + i); we find the address of the i'th element of type T following a and dereference the result. In this case p[i] gives us the address of the ith 10-element array of int following p. Since we dereference the pointer as part of the subscript operation, the type of the expression p[i] is "10-element array of int". Thus we can subscript this expression again and get p[i][j].