Inspired this quiestion .
Why does -
void display(int p[][SIZE]) // allowed
and
void display(int p[][]) // not allowed
?
Because arrays decay to pointers when passed to a function. If you do not provide the cardinality of the second dimension of the array, the compiler would not know how to dereference this pointer.
Here is a longer explanation: when you write this
p[index]
the compiler performs some pointer arithmetic to find the address of the element that it needs to reference: it multiplies index by the size of p's element, and adds it to the base address of p:
address = <base address of p> + index * <size of p's element>
When you try passing an array like this, p[][], the compiler knows only the base address of p, but not the size of its element. It is in order for the compiler to know the size of p's element that you need to provide the cardinality of the second dimension.
Because a 2D array is stored row wise and hence the function needs the number of columns so that it knows when the next row begins.
That's because caculations in pointers requires that.
When p points to 2 dimentional array that's size is SIZE this operation p+=3 is calculated this way:
if the value of p is a constant equal ADRESS
p receive this value ADRESS + 3*SIZE.
That's because arithmitic in pointers is different from arithmitic in real numbers. This calculation can't be done without knowing the size of the array.
Related
Consider
float a[] = { 0.1, 0.2, 0.3};
I am quite confused about the fact that a is later passed to a function foo(float* A). Shouldn't a variable of type float* point to a single float, right? Like what is mentioned in this tutorial
Good question.
float * does point to a single float value.
However, in C, you can do pointer arithmetic. So, when you get the value from the first pointer, you can actually go to the next float in memory.
And in an array, all of the floats are laid out contiguously in memory.
So, in order to access the whole array, you just need to get the pointer to the first element in the array and iterate till the end of the array. This is why the size of the array is also passed to the function along with the pointer to the first element of the array.
void do_stuff(float * a, int n)
{
for(int i=0; i<n; ++i)
{
do_stuff_with_current_element(a[i]);
}
}
In C, arrays are contiguous chunks of memory, which means every element is next to each other in memory. So, a pointer to the first element combined with the knowledge of the type of the element lets you denote an array with just a pointer to the first element. Note that this does not include information on length, which is why many C functions that deal with arrays (like memcpy) also take a parameter for the length.
The pointer reaches the base adress and checks the type of your variable. Every step it sums with your variable byte. So in real you have just your base adress and variable information.
A float* variable (when properly initialized or assigned) does, indeed, point to a single float variable. However, that single variable could be the first element of an array of float data.
Further, when you give an array as an argument to a function, the array implicitly decays to a pointer to its first element. So, calling your foo function with the array, a, as its parameter, is passing a pointer to that array's first element (a float item) - so the argument fully satisfies the function's requirement to take a "pointer-to-float" parameter.
If I have:
int A[10][20];
printf("%p",A[3]);
it will print the address of A[3][0].
However, I'd like to know if this one dimensional array A[3] containing pointers really exists, or it is calculated in some way.
The way you have defined A means that the compiler will allocate for it a contiguous block of memory large enough to hold 10 x 20 (200) integers; see here (scroll down to "Multidimesional arrays"). As I'm sure you realize, if you were to do printf("%p", A); you would see the address of the beginning of that allocated block.
Now, when the compiler sees the expression A[3], it will add what it calculates as the necessary amount of "integer sizes" to the base address (that of A, or A[0][0]); in this case, it will add "3" (the index specified) multiplied by the combined size of all the other dimensions (in this case, there's only one, which is 20).
So, in your case, there is no actual array of pointers; just a memory block that the compiler can interpret according to how you described any part(s) of it.
However, in a more versatile approach, one can actually define a 2D array in terms of an actual array of pointers, like so:
int **A;
A = malloc(10 * sizeof(int*));
for (int n = 0; n < 10; ++n) A[n] = malloc(20 * sizeof(int));
In this case, using printf("%p",A[3]); would still be valid, but it would give a very different offset value from printf("%p",A); or printf("%p",A[0]);.
It's also, perhaps, worth noting that, even though these two different declarations for A can both resolve an individual element through an expression like A[i][j] (but the compiler would evaluate the addresses differently), there is here scope for major confusion! When, for example, passing such an array to a function: if the function expects data allocated in the second form, and you give it an array defined in the first form (and vice versa), you're gonna get major undefined behaviour .
yes there is a way to calculate the position:
for A[i][j]
the position of the memory block will be
pos = A + i*(number_of_columns_in_each_row) + j
here A is the pointer to the first element of the array
However, I'd like to know if this one dimensional array A containing pointers really exists, or it is calculated in some way.
The way you defined the array A :
int A[10][20];
does not contain any pointers as elements of the array. it contains only integer elements.
if you want to make an array of pointers, which should be assigned to int-variables is defined like that:
int *A[10][20];
You also can set a pointer to the start of the array, which means element [0] [0]
by using:
int *pointer;
int *A[10][20];
pointer = &A;
You also be able to set the pointer slightly forwards according to each element by increase the pointer.
pointer++;
int stud[5][2] = {{1,2},{3,4},{5,6},{7,8},{9,8}};
printf("%u %u",*(stud+1),stud+1);
printf("%u, %u", &stud,stud);
Why this statement prints similar values, stud[1] or *(stud+1) is actually an array hence must get the base address i.e &stud[0][0], but stud itself is a pointer to an array of array. Also the third statement prints identical values.
Your observations are correct concerning the expressions are all address-results. But the types of those addresses per the standard are different. Your phrase "but stud itself is a pointer to an array of array". is not accurate. stud is an array of arrays. Pointers are not arrays. After decades of trying to come up with a solid vernacular that describes how it works, and refusing steadfastly to walk the "decay" plank (a word that appears exactly one times in the C standard and even there it is used as a verb-footnote), the best I could come up with is this:
Pointers are not arrays. A pointer holds an address. An array is an address.
Each expression is shown below Given int stud[5][2];
stud int (*)[2]
stud+1 int (*)[2]
*(stud+1) int *
&stud int (*)[5][2]
Remembering that, per the standard, the expressive value of an array is the address of its first element, and pointer-to-element-type is the type of said-address. In both outputs each pair of expressions have equivalent addresses, but they're different types. This is verifiable with some expansion of the original code:
#include <stdio.h>
int main()
{
int stud[5][2] = {{1,2},{3,4},{5,6},{7,8},{9,8}};
printf("%p %p\n", *(stud+1), stud+1);
printf("%p %p\n", &stud,stud);
int (*p1)[2] = stud+1; // OK
// int (*p2)[2] = *(stud+1); // incompatible types
int *p3 = *(stud+1); // OK
int (*p4)[5][2] = &stud; // OK
return 0;
}
int stud[5][2] = {{1,2},{3,4},{5,6},{7,8},{9,8}};
The above statement defined stud to be an array of 5 elements where each element is of type int[2], i.e., an array of 2 integers. It also initializes the array with an initializer list.
Now, in the expression stud + 1, the array stud decays into a pointer to its first element. Therefore, stud + 1 evaluates to &stud[1] and is of type int (*)[2], i.e., a pointer to an array of 2 integers . *(stud + 1) is then *(&stud[1]), i.e., stud[1]. stud[1] is again an array type, i.e., int[2], so it again decays to a pointer to its first element, i.e., &stud[1][0] (which is the base address of second element of the array stud[1]) in the printf call.
Please note that stud + 1 and *(stud + 1) evaluate to the same address but they are not the same type.
Similarly, &stud and stud decay to the same address but they are different types. stud is of type int[5][2] where as &stud is of type int (*)[5][2].
Why this statement prints similar values, stud[1] or *(stud+1) is actually an array hence must get the base address i.e &stud[0][0], but
stud itself is a pointer to an array of array.
You are wrong here. The base address of stud[1] or *(stud + 1) is &stud[1][0] and not &stud[0][0]. Also, stud is not a pointer but an array type. It decays to a pointer to its first element in some cases like here but it does mean it is a pointer.
Also, you should use %p conversion specifier for printing addresses.
Without using any decaying syntax it may be clearer (these are the same addresses as your code; the first line is in the opposite order; and my parentheses are redundant but hopefully it improves clarity of this example):
printf( "%p %p\n", &(stud[1]), &(stud[1][0]) );
printf( "%p %p\n", &(stud), &(stud[0]) );
In both cases the first address on the line matches the second because the first element of an array lives at the same address as the array. Arrays can't have initial padding, and in C the address of an object is the address of its first byte.
The first element of stud is stud[0], and the first element of stud[1] is stud[1][0].
Since all of those values you are trying to display are all pointers you should use %p instead of %u. If you do that you will see that the addresses pointed to:
printf("%p, %p", &stud,stud);
are different than:
printf("%p %p",*(stud+1),stud+1);
because as you said stud is a pointer to an array of array.
Lets analyze the program
int stud[5][2] = {{1,2},{3,4},{5,6},{7,8},{9,8}};
Now address will be like this (assuming 2 byte integer). Brackets denote corresponding elements in array.
1 element of 2-D array ---> 4001(1) 4003(2)
2 element of 2-D array ---> 4005(3) 4007(4)
3 element of 2-D array ---> 4009(5) 4011(6)
4 element of 2-D array ---> 4013(7) 4015(8)
5 element of 2-D array ---> 4017(9) 4019(8)
We know that arr[i] gives the ith element of array. So when we say stud[0] we expect 0th element of array stud[5][2].
We can assume 2-d array as collection of 1-d array. So with statement like printf("%u",stud[0]) we exptect 0th element to get printed and what is 0th element for this array. It is one dimensional array. We know that just mentioning 1-D array gives its base address. Hence printf would print base address of 0th 1-D array and so on.
With this information we can analyze your problem.
Remember stud is 2-D array. stud is treated as pointer to zeroth element of 2-D array. So (stud + 1) would give address of 2nd element of 2-D array. And thus printing (stud+1) would print address of 2nd element of stud array. What is it. It will be 4005 from above addresses.
Now lets see why *(stud +1) also gives the same value.
Now we know that *(stud +1) is equivalent to stud[1]. From above we know stud[1] would print base address of 2nd 1-D array. What is 1-d array at 2nd position it is (3,4) with address (4005,4007). So what is it base address. It is 4005. Thus *(stud+1) also prints 4005.
Now you say stud[0] and &stud[0] print the same value.
From above stud[0] is 1-d array and printing it gives its base address. Now so &stud[0] should give address of 1-D array which is same as its base address. Thus they print the same address.
Similar explanation will hold for other cases.
I'm not sure if the history tag is relevant, but feel free to add it.
I would presume the reason is historical, which is why I suggest this.
Why is it that I cannot declare a function signature such as the following?
void foo(int doubly_indexed_array[][]) {
...
}
which gives
$ gcc mem.c
mem.c:4: error: array type has incomplete element type
Why must you declare one of the dimensions as in the following?
void foo(int doubly_indexed_array[][10]) {
...
}
You need to declare the second one and not only one. It has to do with memory layout, a 2-d array is stored contiguously in memory which means all the second dimension arrays are contigous.
So for int[2][2] the memory layout looks like(assuming initialization to 0):
[[0, 0][0, 0]]
Compiler has to know by how much to increment the pointer when indexing on the first dimension for example. So if an int array is named a,
a[i][j] is really (address of a) + i*sizeof(int)*second_dimension + j*sizeof(int)
All of this need to be known at compile time so the compiler can generate code.
In essence, all arrays in C are one-dimensional. In order to be able to access an element by it's index, C needs to now the type of the elements.
Consider a one-dimensional array of ints. Since C knows the size of an int (say 4 bytes) it knows that to access element 50 it simply adds 50 * 4 = 200 bytes to the base address of the array. So it only needs to know the base address and the type of the elements, and not the overall number of elements (since C does not check for an out-of-range access, which would otherwise require the overall size).
Now a two-dimensional array is really a one-dimensional array whose elements are themselves arrays. In order to access an element in the "outer" array, you need to know its "type", which is an array of a certain type and size.
Consider a two-dimensional array declared as int a[100][10]. Since C knows that the type of the "outer" array is an array of 10 ints, it can calculate the position of the element (which itself is an array) at offset 50 by adding 50 * 4 * 10 to the base address. Note that the size of the "inner" array is necessary to find the position of the element. From that point it does the same thing as the previous example to find the position within the "inner" array of the requested int element.
Overall you have to declare the sizes of all the dimensions except the outermost one in order for C to be able to properly access the array.
The declaration void foo(int array[][]) violates C 2011 (N1570) 6.7.6.2 1, which addresses array declarations and says, in part, “The element type shall not be an incomplete or function type.” Since array is an array of array of int, its element type is array of int, and it is incomplete because the number of int in that array is not specified.
Contrary to other answers, though, this number is not needed by the compiler at this point. You can make an equivalent declaration of void foo(int (*array)[]). There are two points to note about this:
It does not violate the rule in 6.7.6.2 because it declares array to be a pointer, rather than an array. Pointers are permitted to point to incomplete types.
If the first declaration were permitted, it would actually be the same as this declaration, because 6.7.6.3 says “A declaration of a parameter as “array of type” shall be adjusted to “qualified pointer to type”,…
However, the only way you can access elements with this declaration is in the form (*array)[i]. This is legal because the * operator may dereference a pointer to an incomplete type, so *array is the first row of the array, and then (*array)[i] is the ith element of that row.
You cannot properly access other rows of the array, because this requires an expression such as array[j][i], which requires performing pointer arithmetic on array, and pointer arithmetic requires that the pointer point to an object of complete type (because, for the compiler to figure out where objects beyond the one pointed to are, it must know how big the objects are, so it must have complete information about their size).
Array in C is just like pointers, it doesn't include the size. Therefore if you don't provide the last dimension the compiler won't know how to calculate the address of the element
TYPE array[A][B];
&array[a][b] = (char*)array + a*sizeof(array[a]) + b
= (char*)array + a*(B*sizeof(array[a][b])) + b
= (char*)array + a*B*sizeof(TYPE) + b
As you see, if B is not declared then it have 3 unknown variables to solve when you're accessing array[a][b], that's the 2 dimension's index and B. That's why the compiler needs the last dimension size to produce code. Similarly it'll need the last n-1 dimensions of an n-dimensional array
First I declare an array a with 10 elements. Then I call the function bubbleSort
bubbleSort( a, 10);
where bubbleSort is a function declared as
void bubbleSort(int* const array, const int size)
My question is if "array" is a pointer- which means it stored the address of array a (array= &a [0]) then how can we understand these terms array[1], array[2], array[3]... in the function bubbleSort?
It is the bubble sort program and this part is very confusing for me.
array[1] means, by definition in the C standard, *(array+1). So, if array is a pointer, this expression adds one element to the pointer, then uses the result to access the pointed-to object.
When a is an array, you may be used to thinking of a[0], a[1], a[2], and so on as elements of the array. But they actually go through the same process as with the pointer above, with one extra step. When the compiler sees a[1] and a is an array, the compiler first converts the array into a pointer to its first element. This is a rule in the C standard. So a[1] is actually (&a[0])[1]. Then the definition above applies: (&a[0])[1] is *(&a[0] + 1), so it means “Take the address of a[0], add one element, and access the object the result points to.”
Thus, a[1] in the calling code and array[1] in the called code have the same result, even though one starts with an array and the other uses a pointer. Both use the address of the first element of the array, add one element, and access the object at the resulting address.
C defines operations of addition and subtraction of integers and pointers, collectively called pointer arithmetics. The language specification says that adding N to a pointer is equivalent to advancing the pointer by N units of memory equal to the size of an object pointed to by the pointer. For example, adding ten to an int pointer is the same as advancing it by ten sizes of int; adding ten to a double pointer is equivalent to advancing the pointer by ten sizes of double, and so on.
Next, the language defines array subscript operations in terms of pointer arithmetics: when you write array[index], the language treats it as an equivalent of *((&array[0])+index).
At this point, the language has everything necessary to pass arrays as pointers: take &array[0], pass it to the function, and let the function use array subscript operator on the pointer. The effect is the same as if the array itself has been passed, except the size of the array is no longer available. The structure of your API indirectly acknowledges that by passing the size of the array as a separate parameter.
You have an array of int, identified by the address of its first element.
array[1] Is equivalent to *(array + 1) which mean "The value of what is pointed by array + the size of one element, which is known as int because you prototyped it as int *"
When you declare a to be an array of size 10, the c program stores the address of a[0] in a and since the memory is allocated continuously therefore you can access the subsequent integers by using a[2], a[4] etc. Now when you copy a to array it is actually the address that gets copied and therefore you can access the integers using array[0], array[1] etc.