int doSomething(int n, int arr[n])
What confuses me here is the reusing of the n variable inside the parameter list. Can anyone explain why this is valid and what are the use cases?
It is valid.
For this specific case it is equivalent to:
int doSomething(int n, int arr[]);
int doSomething(int n, int *arr);
because the array parameters are automatically transformed into pointers to arrays' elements. In general there are a few differences between array-like and pointer declarations of parameters. See link for more details.
IMO, the better usage should be:
int doSomething(int n, int arr[static n]);
This static n tell the compilers that at least n elements pointer by arr are valid. Moreover it makes the declaration visually different from a declaration of array what helps to avoid surprising issues when sizeof arr is used.
C allows several syntaxs for passing an array pointer including:
int* arr, int arr[] or int arr[10]
However ALL of these are equivalent to the first - that is the compiler sees only an int pointer and does not use any size that may have been specified in the array square brackets to enforce integrity on the size of the array passed. In all the above cases the compiler will generate:
void __cdecl foo(int * const)
The only way to specify the size of an array is, as you have done, pass a separate parameter indicating the size/length of the array.
In the example you cite the n in arr[n] is not used by the compiler to maintain any integrity on the array bounds - in the example it is only included as an indication that the first parameter is the size.
A less controversial form of the same code might be just to state that more explicitly:
int doSomething(int arr_len, int arr[])
In terms of what is the best convention or preference for passing an pointer and its array size see this question.
Related
This question already has answers here:
Should I use char** argv or char* argv[]?
(10 answers)
Closed 8 years ago.
I've been fiddling around to see if there's any way to retain information about an array's length automatically when passed into a function (see my other question here: Why is this array size "workaround" giving me a warning?), but my question is more about a warning that gcc is giving that doesn't make sense to me.
According to this website (EDIT: I misread the website), char *p[10] declares a pointer to a 10-wide array of chars. But when I tried to pass in a pointer to an array into a function, I got this error message from the compiler:
Here is the rest of the program:
I know that when an array is passed into a function, it decays into a pointer (losing information about its length), but it seems that the declaration itself is decaying. What's going on here?
EDIT: When I replace the char *p[10] with char (*p)[10], it doesn't give the warning anymore, and more importantly, it displays the proper array length: 10. I guess my questions are 1) Why do the parentheses change things? and 2) Is this a well-known workaround or am I relying on some behavior of the compiler that isn't guaranteed? (i.e. that array length info can be passed by indirectly passing in a pointer to it?)
In fact char *p[10] is an array, of length 10, of pointers to char. You are looking for char (*p)[10]. That is a pointer to an array, of length 10, of char.
You might find http://cdecl.org/ a useful resource to help you test your understanding of declarations.
Regarding the discussion surrounding dynamic arrays, you are going to have to accept that once you allocate an array dynamically, the system provides no means for you to recover the length of the array. It is your responsibility to remember that information.
The subject of your question has been answered already but I wanted to address the heart of it, which is "can I encode the length of an array in its type?" Which is in fact what a pointer-to-array does. The real question is whether you can actually gain any brevity or safety from this. Consider that in each scope where you have a declaration of your type, the length still needs to be known a-priori. To show you what I mean let's generalize your example slightly by making 10 a compile-time constant N.
#define N 10
size_t arraylength(char (*arrayp)[N]) {
return sizeof(*arrayp);
}
int main(void) {
char array[N];
assert( arraylength(&array) == N ); //always true
}
So far so good. We didn't have to pass the length of array anywhere. But it's easy to see that anywhere the expression sizeof(*arrayp) is used, we also could have written N. And any place we declare a char(*)[ ], the bracketed length must come from somewhere.
So what if N isn't a compile time constant, and array is either a VLA or a pointer-to-array from malloc? We can still write and call arraysize, but it looks like this:
size_t arraylength(size_t N, char (*arrayp)[N]) {
return sizeof(*arrayp);
}
int main(void) {
size_t N = length_from_somewhere();
char array[N];
assert( arraylength(sizeof(array), &array) == N );
}
In defining arraysize N must still be visible before the declaration of arrayp. In either case, we can't avoid having N visible outside of the declaration of arrayp. In fact, we didn't gain anything over writing arraysize(size_t N, char* array) and passing array directly (which is a bit silly given the purpose of this function.) Both times arraylength could have equally been written return N;
Which isn't to say that array pointers are useless as parameters to functions -- in the opposite situation, when you want to enforce a length, they can provide type checking to make sure somefunc(char (*)[10]); receives a pointer to an array that is really (sans shady casting) 10 elements long, which is stronger than what a construct like [static 10] provides.
Also keep in mind that all of the length measurements above depend on the underlying type being char where length == size. For any larger type, taking the length requires the usual arithmetic e.g.
sizeof(*arrayp)/sizeof((*arrayp)[0])
In C, arrays decay to pointers to their first elements on most uses. In particular, what a function receives is always just a pointer to the first element, the size of the array is not passed with it.
Get a good text on C and read up on arrays.
I've been fiddling around to see if there's any way to retain information about an array's length automatically when passed into a function
The problem is so annoying that lots of programmers would love to have an answer. Unfortunately, this is not possible.
It seems that the declaration itself is decaying
Pointer to an array is not the same as a pointer to a pointer; that is why you are getting an error.
There is no decaying going on in your code, because you are not passing an array in your code sample: instead, you are trying to pass a pointer to an array &p. The pointer to an array of characters is not compatible to the expected type of the function, which is char**. Array size from the declaration is ignored.
You need to keep in mind two things:
1. Arrays are not pointers.
2. Array names decays to pointers (in most cases) when passed as arguments to functions.
So, when you declare
int a[10]; // a is an array of 10 ints
int *b; // b is a pointer to int
both of a and b are of different types. Former is of type int [10] while latter is of type int *.
In case of function parameter
void foo1 (int a[10]); // Actually you are not passing entire array
void foo2 (int a[]); // And that's why you can omit the first dimension.
void foo3 (int *a); // and the compiler interprets the above two third
ain all of the above function declarations is of same data type int *.
Now in your case
unsigned long arraySize(char *p[10]);
you can declare it as
unsigned long arraySize(char *p[]);
and hence
unsigned long arraySize(char **p);
All are equivalent.
char *p[10] char *p[] and char **p all are exactly equivalent but when they are declared as parameter of a function otherwise char *p[10] (an array of 10 pointers to char) and char **p (a pointer to pointer to char)are entirely of different type.
Suggested reading: C-FAQ: 6. Arrays and Pointers explains this in detailed.
Array name itself is a constant pointer. for example int arr[10]={0};
arr contains the address of arr[0]. hence arr equals&arr[0] .
when u pass the arraysize(&p) , you are actually passing a double pointer .
The correct format to pass a array pointer would be arraysize(&p[0]) or arraysizeof(p)
Note Array name is constant pointer , you cant change its value .
int arr[10];
arr++;
is invalid.
In your case you cant find a size of an array in function by passing the array name . it would return size of pointer(4 or 8 depends on your processor .
The method is to pass the size along with the array
func(array_name , array_size);
When we pass an array as an argument we accept it as a pointer, that is:
func(array);//In main I invoke the function array of type int and size 5
void func(int *arr)
or
void fun(int arr[])//As we know arr[] gets converted int *arr
Here the base address gets stored in arr.
But when the passed array is accepted in this manner:
void func(int arr[5])//Works fine.
Does the memory get allocated for arr[5]?
If yes, then what happens to it?
If no, why isn't the memory allocated?
Does the memory gets allocated for arr[5]?
No, it doesn't.
If no,why memory is not allocated?
Because it's not necessary. The array, when passed to a function, always decays into a pointer. So, while arrays are not pointers and pointers are not arrays, in function arguments, the following pieces of code are equivalent:
T1 function(T2 *arg);
T1 function(T2 arg[]);
T1 function(T2 arg[N]);
void func(int *arr)
void func(int arr[])
void func(int arr[5])
are all equivalent in C.
C says a parameter of an array of type is adjusted to a pointer of type.
When you place an array size in a parameter declaration the number is totally ignored. In other words
void foo(int x[5]);
is exactly the same as
void foo(int *x);
Hope the following code/comments helps. When coding C the programmer must ensure that many items agree in various programs. This is both the fun and the bane of programming in C.
Note. Defining int arr[5] in a parameter list does NOT allocate storage for the data that is being passed. The declaration is valid and endorsed but only because it lets the compiler perform its type checking. Although, the compiler does allocate storage when a function is called that storage does not store YOUR data. You must allocate your data either through an explicit declaration (as in main in the following example) or you need to issue an malloc statement.
I ran the following code in Eclipse/Microsoft C compiler and NO statements were flagged with a warning or an error.
//In main invoke the functions all referring the same array of type int and size 5
void func1(int *arr)
{ // arr is a pointer to an int. The int can be the first element
// of an array or not. The compiler has no way to tell what the truth is.
}
void func2(int arr[])
{ // arr is a pointer to an array of integers, the compiler can check this
// by looking at the caller's code. It was no way to check if the number
// of entries in the array is correct.
}
void func3(int arr[5])
{ // arr is a pointer to an array of 5 integers. The compiler can
// check that it's length is 5, but if you choose to overrun the
// array then the compiler won't stop you. This is why func1 and func2
// will work with arr as a pointer to an array of ints.
}
void main()
{
int data_array[5] = {2,3,5,7,11};
func1(data_array);
func2(data_array);
func3(data_array);
}
Hope this helps, please ask for more info.
I'm curious if I give an n-dimensional array for n>1 as an argument to a function, are the contents of whole array copied or just the pointer to the address of the first element.
Assume function signature is something like this:
int someFunction(int n, int arr[n][n]);
[Where I just re-learned this yesterday]
This is a C99 extension that is not widely known. The array is still passed by reference as they always have been, but the compiler is able to interpret it as an array similar to the way it handles Variable-Length Arrays.
This won't give you bound-checking, of course. C doesn't have that.
In your function signature,
int someFunction(int n, int arr[n][n]);
The final n doesn't really buy you anything, it just gets ignored. But int arr[n][] is new. That's what C89 didn't have. Previously, the only option was to calculate indices manually off of the base pointer, arr[n*x+y].
In Ansi-C (C89), you'd have to declare the function as,
int someFunction(int n, int arr[]);
which is equivalent to
int someFunction(int n, int *arr);
and calculate the two dimensions as a single index.
The new C99 magic, reduces the original to this:
int someFunction(int n, int *arr[n]);
I'd describe the process more as array adoption, than passing. It just mitigates some of the losses that were incurred by the original decision to pass arrays as pointers.
It's important to understand that second two examples are sort-of internalized equivalents. Figurative illustrations. In the final one, the array is not converted into an array of pointers, but the bounds of the lowest dimension is dropped. Perhaps it's better illustrated like this:
int someFunction(int n, int arr[n][]);
The only way to copy an array as a function argument is to wrap it in a struct. But then you cannot have variable dimensions.
struct arr {int n; int arr[n][n];}; //Nope, won't compile!
enum { n = 3 };
struct arr { int arr[n][n]; };
struct arr someFunction( struct arr ); //argument and return value are copied.
And this has been legal since 1989.
That's an illegal signature for a function, as arrays in function signatures must have constant dimensions (other than the final dimension).
The reason that arrays must have constant sizes as function parameters is because they aren't actually copied in the function call -- only a pointer is passed.
I want to manipulate an array of integers in a function, but I am not sure if I should use the address prefix or the pointer prefix:
void shuffle(int *deck[]){
//statements
}
or
void shuffle(int &deck[]){
//statements
}
Further, when I directly want to manipulate the values of the array, I'm not sure if I should use (within the function)
*deck[4] = 34
or something else.
Any clarification is appreciated.
There are no references in C(your Q is tagged C only) so you will have to use the pointer version.
When you say pass an array to the function, what you essentially pass to the function is the pointer to its first element, both of the following two syntaxes mean one and the same thing to the compiler:
void shuffle(int *p_deck);
void shuffle(int deck[]);
Neither.
Since arrays can be passed by reference only, you don't need to do tricks, just pass a pointer and dereference it. (That syntax involving the & in your 2nd function is not valid, anyway). So:
void shuffle(int arr[])
{
arr[0] = 1337;
}
or
void shuffle(int *arr)
{
}
etc. And you can pass it like this:
int deck[52];
shuffle(deck);
You would have to use the pointer version, but because of the Right-Left Rule, I believe your:
int * deck[] would actually be an array of int pointers, instead of an array of ints.
http://cseweb.ucsd.edu/~ricko/CSE131/rt_lt.rule.html
You should just pass a reference to the first element in the array:
int * deck but you may want to pass in the size of the array so you avoid going out of bounds.
Arrays decay into pointers when passed to functions, so there's no point in using an additional level of indirection here.
According to ยง6.3.2.1 of the C99 standard,
Except when it is the operand of the sizeof 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. If the array object has register storage class, the behavior is undefined.
void shuffle(int deck[]) {
...
}
Note that C does not support references, only pointers. You're probably thinking of C++.
You can pass an array by reference in C++, if you specify the size of the array:
void shuffle(int (&deck)[52])
{
deck[4] = 34;
}
If you don't know the size, or if you're limited to C, you need to use pointers.
The other (C++) option is to use a vector for the deck and pass a reference to the vector instead.
C doesn't have references (a), that's C++.
In C, arrays of X decay into a pointer to X[0] when passed to functions, so one way is to use:
void shuffle (int *pDeck) {
// use pDeck[something]
}
:
int deck[] = {0,1,2,3,4,5,6,7,8,9};
shuffle (deck);
I actually prefer that method to the void shuffle (int pDeck[]) variant since the former makes it absolutely clear that you're now dealing with a pointer rather than an array.
The reason this is important is because you lose the size information when you do that, so you may want to pass that in as well:
void shuffle (int *pDeck, size_t sz) {
// use pDeck[0 thru sz-1]
}
:
int deck[] = {0,1,2,3,4,5,6,7,8,9};
shuffle (deck, sizeof (deck) / sizeof (*deck));
(a): Although they are a very nice feature. I do hope that ISO considers them for the next C standard, since a large number of problems newcomers to the language have are involved with pointers, and references can hide the complexity of that very well.
typedef int array [x][];
What does that means. What will happen if we have such a typedef. It was my interview question.
Let's assume you have somewhere:
#define x 3
As others point out, typedef int array [3][]; will not compile. You can only omit the most significant (ie first) element of an array length.
But you can say:
typedef int array [][3];
This means that array is an int array (of as-yet unspecified length) of length 3 arrays.
To use it, you need to specify the length. You can do this by using an initialiser like so:
array A = {{1,2,3,},{4,5,6}}; // A now has the dimensions [2][3]
But you CAN'T say:
array A;
In this case, A's first dimension isn't specified, so the compiler doesn't know how much space to allocate for it.
Note that it's also fine to use this array type in a function definition - as arrays in function definitions are always converted to pointers to their first element by the compiler:
// these are all the same
void foo(array A);
void foo(int A[][3]);
void foo(int (*A)[3]); // this is the one the compiler will see
Note that in this case:
void foo(int A[10][3]);
The compiler still sees
void foo(int (*A)[3]);
So, the 10 part of A[10][3] is ignored.
In summary:
typedef int array [3][]; // incomplete type, won't compile
typedef int array [][3]; // int array (of as-yet unspecified length)
// of length 3 arrays
You'll get a compilation error. For multidimensional arrays, at most the first dimension may be omitted. So for example, int array[][x] would be valid instead.
You'll get a diagnostic.
int [x][] is an incomplete array type that cannot be completed.