Related
I'm currently learning function that work with multi-dimensional array in C.
Let's say I have
short myArray[10][30].
I have declared following function prototype to work with myArray.
void first(int n, short ar[][30]);
void second(int n, short ar[30]);
void third(int n, short (*ar)[30]);
My understanding is that the ar parameter in both first and third function are equivalent. short ar[][30] in first function is equivalent to the short (*ar)[30] in third function because they are both pointer to array of 30 short.
The ar in second function is different because in short ar[30], ar is a pointer to short, not a pointer to array of 30 short.
Is my understanding correct?
Yes your understanding is correct. 1 and 3 are equivalent. And 2 is also right (But not for passing 2d array - it is correct for passing 1D array). But will clarify a bit the second case.
And the second one that 30 inside of third brackets are not considered by the compiler. You can omit it still the compiler won't complain. Actually here you have passed an 1D array of short that decayed into pointer to the first element (First element being short it is short*). So the second one you can also write as short *ar.
void second(int n, short ar[]);
void second(int n, short* ar );
These two works and they are equivalent in this context. The second one is for passing 1D array something like
second(n, myArray[5]);
The thing is, most of the time array decays into pointer (exception is sizeof operator or Alignof etc). Passing an array to a function is a case where the array decays.
Also you are passing int arrays so it is wrong to write short.(int and short may have same size but it is guaranteed that size of int would be larger than or equal to the size of short). If you used short and then wrote int in the declaration that would have worked.
Edit: The second one is not for passing 2d array. Let's be clear on that. You can't pass 2d array to a function with the prototype declared as the second one. For pointers there are 2 things to consider - it's type and it's value. If you tried to pass a 2d array to the same function that would be illegal. 2d array decays into int (*)[30] which is not in anyway same as int * or int[].
1 and 3 are indeed the same, as would be
void fourth(int n, short ar[10][30]);
because when you pass an array as function parameter, it decays to a pointer to its first parameter, so the compiler sees 1 and 4 as 3.
That explains why this would also be correct:
void fifth(int n, short arr[15][30]);
As it decays to a pointer, the declared size of the first dimension is not used. You are supposed to give the actual size in another way.
But this one is different:
void second(int n, short ar[30]);
and your compiler should raise a warning there because the expected paramater is a pointer to short, when you pass a pointer to an array of 30 short. Of course the pointers will have same value (same address), and common compilers will give expected results, but aliasing a pointer to array and a pointer to element is not allowed by the standard. So please avoid it.
With such a declaration, second should be called as
cr = second(n, arr[0]);
because arr[0] is a short array and will correctly decay to a short *.
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
I have a function in C (I am using gcc 4.8.0) which receives an array as its argument. Is there a way to accurately determine the number of elements in the array, without passing an additional argument array_count?
There is no way to determine in general the number of array elements passed as parameter.
When you pass a array as argument in C, you only pass the pointer to that array, that is the pointer to the first element (indexed 0) of the memory zone holding that array.
Very often, programmers have the convention of passing, as another argument, the size of that array. For example, the standard qsort(3) library function is expecting, as its second nmemb argument, the number of elements in the array to be sorted.
Alternatively, you might use flexible array members, e.g. pass (in C99, not in earlier C standard) address of structures like
struct my_flexarray_st {
int size; // allocated size
int len; // used length, should be less than size
double arr[]; /// only len numbers are meaningful
};
Whatever method you use, you need to understand that array sizes are conventionally known by functions when passed by argument. So please, document that convention. You could even have the (bad) convention that all arrays have some global variable as their dimension.
For heap allocated memory zones, the standard gives you malloc, calloc and friends to get such fresh zones, but no way to query their allocated sizes. Some C libraries have non-standard extensions to query that (but I don't recommend using them).
In recent C++11 (which is not the same as the C language) you might be interested by std::vector and std::array template containers.
Arrays decay into pointers in function arguments so size cannot be determined.
An lvalue [see question 2.5] of type array-of-T which appears in an expression decays (with three exceptions) into a pointer to its first element; the type of the resultant pointer is pointer-to-T because an array is not a "modifiable lvalue,"
(The exceptions are when the array is the operand of a sizeof or & operator, or is a literal string initializer for a character array.)
You can't , unless the array is static (i.e. not dynamic-allocated) , then you can use sizeof operator.
Not possible. This is why you either use a data structure like a linked list where you can actually determine the length or you require a length argument in your function.
Even with the required length argument alone there is no way to know it is true.
So, your API should also require a standard sentinel value to terminate the array.
Generally this could be NULL or something but depends on your array type.
This is doable in C. You can query size of array if you are in the same scope of its definition via sizeof. If you are in scope of function which takes array as parameter you need to declare the parameter as pointer to array (not just as array - in this case array will decay to pointer to first element), in this case size of array would be saved during argument passing:
void foo(int (*param)[3] ) { assert (sizeof(*param) == sizeof(int)*3; }
However, if you mean by "array" pointer to some dynamically allocated memory with not-known at compile time size, then you definetely need to pass size separately.
you can do a while look
function(arr[])
{
/* not this is pseduo code */
int i = 0;
while (*arr[i++] != null)
{
}
// i is number of elements
}
So I have this array in a header file like this:
// header.h
static const unsigned int array1[]={0x00,0x01,0x02,0x03};
And:
// file.c
main()
{
unsigned int *ptrToArray;
ptrArray = &array1[0];
}
Correct me if I am wrong. I assume: to find the number of bytes of array elements, instead of sizeof(array1) the equivalent will be sizeof(*ptrArray), right?
And to access the elements of the array, instead of array[i], it will now be:
*(ptrArray) for the first element,
*(ptrArray+1) for the 2nd element so on right?
The type of *ptrToArray is int, therefore sizeof(*ptrToArray) is the same as sizeof(int). So it won't tell you anything about the number of elements in array1.
Whilst you can write *(ptrArray+1), etc., you should just write ptrToArray[1]!
A pointer is not an array, and an array is not a pointer. An array can decay into a pointer when convenient, but it is still a complete type.
So, the type of *someIntPointer is int, not an array, even if that pointer happens to point to the first element in an array. sizeof(someArray) works as you would expect because it knows that the type is actually an array.
sizeof won't behave in the same way for your pointer: your example will give you the size of the datatype: unsigned int.
And while you can use pointer arithmetic to reference elements through ptrArray, you can just as well use standard array dereferencing: ptrArray[0], ptrArrray[1], ... and in most cases you're better off doing so.
Sizeof will return the size of the pointer for regular pointer types. If you sizeof a dereferenced pointer type, you will get the size of the element (i.e. sizeof(unsigned int)). You will need to either keep track of the number of elements in the array yourself, or use sizeof on the array declaration.
As for accessing, you could do it that way, but you can just use the bracket notation as you would with a normal array.
Arrays are a special class of pointer. The compiler knows when to treat an array as an array and when to treat it as a pointer: that's how it knows how big an array is, but you can still pass it to functions that expect an array (when you do this, you get a pointer to the first element). The same does not work in reverse however: The compiler will never treat a pointer declared as a pointer as an array.
By the way, [] just simplifies to pointer arithmetic. You can add a pointer to an int, but you can also add an int to a pointer. You can thus (but probably shouldn't) do weird things like 1[ptrArray]
This question already has answers here:
Why isn't the size of an array parameter the same as within main?
(13 answers)
Closed 7 years ago.
#include "stdio.h"
#define COUNT(a) (sizeof(a) / sizeof(*(a)))
void test(int b[]) {
printf("2, count:%d\n", COUNT(b));
}
int main(void) {
int a[] = { 1,2,3 };
printf("1, count:%d\n", COUNT(a));
test(a);
return 0;
}
The result is obvious:
1, count:3
2, count:1
My questions:
Where is the length(count/size) info stored when "a" is declared?
Why is the length(count/size) info lost when "a" is passed to the test() function?
There's no such thing as "array pointer" in C language.
The size is not stored anywhere. a is not a pointer, a is an object of type int[3], which is a fact well known to the compiler at compile time. So, when you ask the compiler to calculate sizeof(a) / sizeof(*a) at compile time the compiler knows that the answer is 3.
When you pass your a to the function you are intentionally asking the compiler to convert array type to pointer type (since you declared the function parameter as a pointer). For pointers your sizeof expression produces a completely different result.
Where is the length(count/size) info stored when "a" is declared?
It's not stored anywhere. The sizeof operator (used in the COUNT() macro) returns the size of the entire array when it's given a true array as the operand (as it is in the first printf())
Why is the length(count/size) info lost when "a" is passed to the test() function?
Unfortunately, in C, array parameters to functions are a fiction. Arrays don't get passed to functions; the parameter is treated as a pointer, and the array argument passed in the function call gets 'decayed' into a simple pointer. The sizeof operator returns the size of the pointer, which has no correlation to the size of the array that was used as an argument.
As a side note, in C++ you can have a function parameter be a reference to an array, and in that case the full array type is made available to the function (i.e., the argument doesn't decay into a pointer and sizeof will return the size of the full array). However, in that case the argument must match the array type exactly (including the number of elements), which makes the technique mostly useful only with templates.
For example, the following C++ program will do what you expect:
#include "stdio.h"
#define COUNT(a) (sizeof(a) / sizeof(*(a)))
template <int T>
void test(int (&b)[T]) {
printf("2, count:%d\n", COUNT(b));
}
int main(int argc, char *argv[]) {
int a[] = { 1,2,3 };
printf("1, count:%d\n", COUNT(a));
test(a);
return 0;
}
Nowhere.
Because it wasn't stored in the first place.
When you refer to the array in main(), the actual array declaration definition is visible, so sizeof(a) gives the size of the array in bytes.
When you refer to the array in the function, the parameter is effectively 'void test(int *b), and the size of the pointer divided by the size of the thing it points at happens to be 1 on a 32-bit platform, whereas it would be 2 on a 64-bit platform with LP64 architecture (or, indeed, on an LLP64 platform like Windows-64) because pointers are 8 bytes and int is 4 bytes.
There isn't a universal way to determine the size of an array passed into a function; you have to pass it explicitly and manually.
From the comment:
I still have two questions:
What do you mean by "..the actual declaration is visible.."? [T]he compiler (or OS) could get the length info through sizeof(a) function?
Why the pointer &(a[0]) doesn't contain the length info as the pointer "a"?
I think you learned Java before you learned C, or some other more modern language. Ultimately, it comes down to "because that is the way C is defined". The OS is not involved; this is a purely compiler issue.
sizeof() is an operator, not a function. Unless you are dealing with a VLA (variable length array), it is evaluated at compile time and is a constant value.
Inside main(), the array definition (I misspoke when I said 'declaration') is there, and when the sizeof() operator is applied to the name of an actual array - as opposed to an array parameter to a function - then the size returned is the size of the array in bytes.
Because this is C and not Algol, Pascal, Java, C#, ...
C does not store the size of the array - period. That is a fact of life. And, when an array is passed to a function, the size information is not passed to the function; the array 'decays' to a pointer to the zeroth element of the array - and only that pointer is passed.
1. Where is the length(count/size) info stored when "a" is declared?
It isn't stored. The compiler knows what a is and therefore knows it's size. So the compiler can replace sizeof() with the actual size.
2. Why is the length(count/size) info lost when "a" is passed to the test() function?
In this case, b is declared as a pointer (even though it may point to a). Given a pointer, the compiler does not know the size of the data pointed to.
Array pointer does not store the size. However, the[] type is not actually a pointer. It's a different type. When you say int a[] = {1,2,3}; you define array of 3 elements, and since it is defined so, sizeof(a) gives you the size of the whole array.
When however you declare parameter as int a[], it's pretty much the same as int *a, and sizeof(a) would be the size of the pointer (which coincidentally may be the same as the size of int, but not always).
In C, there's no way to store the size in pointer type, so if you need the size, you'd have to pass it as additional argument or use struct.
Where is the length(count/size) info stored when "a" is declared?
Nowhere. The question doesn't make sense BTW.
Why is the length(count/size) info lost when "a" is passed to the test() function?
Array decays into pointer(to the first element) when passed to a function. So the answer is 'nowhere' and similar to the previous question this one again doesn't make any sense.