What is useful about a reference-to-array parameter? - c++

I recently found some code like this:
typedef int TenInts[10];
void foo(TenInts &arr);
What can you do in the body of foo() that is useful, that you could not do if the declaration was:
void foo(int *arr); // or,
void foo(int arr[]); // or,
void foo(int arr[10]); // ?
I found a question that asks how to pass a reference to an array. I guess I am asking why.
Also, only one answer to "When is pointer to array useful?" discussed function parameters, so I don't think this is a duplicate question.

The reference-to-array parameter does not allow array type to decay to pointer type. i.e. the exact array type remains preserved inside the function. (For example, you can use the sizeof arr / sizeof *arr trick on the parameter and get the element count). The compiler will also perform type checking in order to make sure the array argument type is exactly the same as the array parameter type, i.e. if the parameter is declared as a array of 10 ints, the argument is required to be an array of exactly 10 ints and nothing else.
In fact, in situations when the array size is fixed at compile-time, using a reference-to-array (or pointer-to-array) parameter declarations can be preceived as the primary, preferred way to pass an array. The other variant (when the array type is allowed to decay to pointer type) are reserved for situations when it is necessary to pass arrays of run-time size.
For example, the correct way to pass an array of compile-time size to a function is
void foo(int (&arr)[10]); // reference to an array
or
void foo(int (*arr)[10]); // pointer to an array
An arguably incorrect way would be to use a "decayed" approach
void foo(int arr[]); // pointer to an element
// Bad practice!!!
The "decayed" approach should be normally reserved for arrays of run-time size and is normally accompanied by the actual size of the array in a separate parameter
void foo(int arr[], unsigned n); // pointer to an element
// Passing a run-time sized array
In other words, there's really no "why" question when it comes to reference-to-array (or pointer-to-array) passing. You are supposed to use this method naturally, by default, whenever you can, if the array size is fixed at compile-time. The "why" question should really arise when you use the "decayed" method of array passing. The "decayed" method is only supposed to be used as a specialized trick for passing arrays of run-time size.
The above is basically a direct consequence of a more generic principle. When you have a "heavy" object of type T, you normally pass it either by pointer T * or by reference T &. Arrays are no exception from this general principle. They have no reason to be.
Keep in mind though that in practice it is often makes sense to write functions that work with arrays of run-time size, especially when it comes to generic, library-level functions. Such functions are more versatile. That means that often there's a good reason to use the "decayed" approach in real life code, Nevertheless, this does not excuse the author of the code from recognizing the situations when the array size is known at compile time and using the reference-to-array method accordingly.

One difference is that it's (supposed to be) impossible to pass a null reference. So in theory the function does not need to check if the parameter is null, whereas an int *arr parameter could be passed null.

You can write a function template to find out the size of an array at compile time.
template<class E, size_t size>
size_t array_size(E(&)[size])
{
return size;
}
int main()
{
int test[] = {2, 3, 5, 7, 11, 13, 17, 19};
std::cout << array_size(test) << std::endl; // prints 8
}
No more sizeof(test) / sizeof(test[0]) for me ;-)

Shouldn't we also address the words in bold from the question:
What can you do in the body of foo() that is useful, that you could not do if the declaration was void foo(int arr[]);?
The answer is: nothing. Passing an argument by reference allows a function to change its value and pass back this change to the caller. However, it is not possible to change the value of the array as a whole, which would have been a reason to pass it by reference.
void foo(int (&arr)[3]) { // reference to an array
arr = {1, 2 ,3}; // ILLEGAL: array type int[3] is not assignable
arr = new(int[3]); // same issue
arr = arr2; // same issue, with arr2 global variable of type int[3]
}

You can ensure that the function is only called on int arrays of size 10. That may be useful from a type-checking standpoint.

You get more semantic meaning regarding what the function is expecting.

Related

Why is the size of an array passed to a function by reference known to the compiler in C++?

I know that when I want to pass an array to a function, it will decay into pointer, so its size won't be known and these two declarations are equivalent:
void funtion(int *tab, int size);
and
void funtion(int tab[], int size);
And I understand why. However, I checked that when I pass an array as a reference:
void funtion(int (&tab)[4]);
the compiler will know the size of the array and won't let me pass an array of different size as an argument of this function.
Why is that? I know that when I pass an array by address, the size isn't taken into account while computing the position of the ith element in the array, so it is discarded even if I explicitly include it in the function declaration:
void funtion(int tab[4], int size);
But what is different when I pass an array by reference? Why is its size known to the compiler?
Note: I'm interested in arrays whose size is known at compile time, so I didn't use any templates.
I found a similar question on Stack Overflow, however it doesn't answer my question - it doesn't explain why the compiler knows the size of the array, there is just some information on how to pass arrays to functions.
Because it can, and because checking adds extra safety. The compiler knows the size of the array because you tell it so, right there in the function declaration. And since that information is available, why wouldn't it use it to signal errors in your source code?
The real question is why your last example wouldn't do the same check. This is, unfortunately, another piece of C legacy - you are never passing an array, it always decays into a pointer. The size of the array then becomes irrelevant.
Could a check be added? Possibly, but it would be of limited use (since we are all using std::array now - right!?), and because it would undoubtedly break some code. This, for example:
void func (char Values [4]);
func ("x");
This is currently legal, but wouldn't be with an additional check on array size.
Because there is no odd implicit type change committed by the compiler in the case. Normally when you write:
void func(int[4]);
or
void func(void());
The compiler decides to "help" you and translates those into:
void func(int *);
or
void func(void(*)());
Funny thing though - it wouldn't aid you in such a way when you try returning one of those. Try writing:
int func()[4], func1()();
Ooops - surprise - compiler error.
Otherwise arrays are arrays and have constant size which can be acquired by using the sizeof operator.
This however is often forgotten because of the compiler behavior noted above and also because of the implicit pointer conversion applied to objects of array type when such isn't expected. And this is very often. Though here are the few exceptions when no implicit array object conversion is applied:
size_t arr[4],
(*parr)[3] = &arr, //taking the address of array
(&refarr)[3] = arr, //storing reference to array
sizearrobject = sizeof(arr); //taking the array object size
The above examples will trigger compiler error because of incompatible types on the second and third line.
I'm talking about the cases when arr object isn't automatically converted to something like this:
(size_t*)&arr
Well, there are several ways to pass an array to function. You can pass it by pointer an by reference, and there are ways to define or not to define it's size explicitely for both ways.
In your question you compare these 2 ways:
Pointer to first element: void f(int *arr)
Reference to an entire array: void f(int (&arr)[size])
You ask why you need to specify size only in one of these cases.
It looks like you assume that the only difference between them is the fact that one uses pointer and another uses reference. But this statement is incorrect, they have more differences: One is pointer to first element, but second is a reference to an entire array.
You can pass an array by pointer to an entire array:
void f(int (*arr)[size])
Compare it with your example, with passing by refence to an entire array:
void f(int (&arr)[size])
They are similar, they have similar syntax, they both explicitely define array size.
Also, consider this:
void f(int &arr)
It looks like passing a single int by reference, but you can pass an array of unknown size to it.
Pointer alternative to it is
void f(int *arr)
You ask why you need to specify array size only in one of those cases. It's because of the syntax you used, not because one is pointer and other is reference.
As I said, you can use pointer or reference. And you can specify array size or you can allow an array of any size to be used. These two are not connected.
// by pointer by reference
/* Any size */ void f(int *arr) void f(int &arr)
/* Specific size */ void f(int (*arr)[x]) void f(int (&arr)[x])

Why do C and C++ compilers allow array lengths in function signatures when they're never enforced?

This is what I found during my learning period:
#include<iostream>
using namespace std;
int dis(char a[1])
{
int length = strlen(a);
char c = a[2];
return length;
}
int main()
{
char b[4] = "abc";
int c = dis(b);
cout << c;
return 0;
}
So in the variable int dis(char a[1]) , the [1] seems to do nothing and doesn't work at
all, because I can use a[2]. Just like int a[] or char *a. I know the array name is a pointer and how to convey an array, so my puzzle is not about this part.
What I want to know is why compilers allow this behavior (int a[1]). Or does it have other meanings that I don't know about?
It is a quirk of the syntax for passing arrays to functions.
Actually it is not possible to pass an array in C. If you write syntax that looks like it should pass the array, what actually happens is that a pointer to the first element of the array is passed instead.
Since the pointer does not include any length information, the contents of your [] in the function formal parameter list are actually ignored.
The decision to allow this syntax was made in the 1970s and has caused much confusion ever since...
The length of the first dimension is ignored, but the length of additional dimensions are necessary to allow the compiler to compute offsets correctly. In the following example, the foo function is passed a pointer to a two-dimensional array.
#include <stdio.h>
void foo(int args[10][20])
{
printf("%zd\n", sizeof(args[0]));
}
int main(int argc, char **argv)
{
int a[2][20];
foo(a);
return 0;
}
The size of the first dimension [10] is ignored; the compiler will not prevent you from indexing off the end (notice that the formal wants 10 elements, but the actual provides only 2). However, the size of the second dimension [20] is used to determine the stride of each row, and here, the formal must match the actual. Again, the compiler will not prevent you from indexing off the end of the second dimension either.
The byte offset from the base of the array to an element args[row][col] is determined by:
sizeof(int)*(col + 20*row)
Note that if col >= 20, then you will actually index into a subsequent row (or off the end of the entire array).
sizeof(args[0]), returns 80 on my machine where sizeof(int) == 4. However, if I attempt to take sizeof(args), I get the following compiler warning:
foo.c:5:27: warning: sizeof on array function parameter will return size of 'int (*)[20]' instead of 'int [10][20]' [-Wsizeof-array-argument]
printf("%zd\n", sizeof(args));
^
foo.c:3:14: note: declared here
void foo(int args[10][20])
^
1 warning generated.
Here, the compiler is warning that it is only going to give the size of the pointer into which the array has decayed instead of the size of the array itself.
The problem and how to overcome it in C++
The problem has been explained extensively by pat and Matt. The compiler is basically ignoring the first dimension of the array's size effectively ignoring the size of the passed argument.
In C++, on the other hand, you can easily overcome this limitation in two ways:
using references
using std::array (since C++11)
References
If your function is only trying to read or modify an existing array (not copying it) you can easily use references.
For example, let's assume you want to have a function that resets an array of ten ints setting every element to 0. You can easily do that by using the following function signature:
void reset(int (&array)[10]) { ... }
Not only this will work just fine, but it will also enforce the dimension of the array.
You can also make use of templates to make the above code generic:
template<class Type, std::size_t N>
void reset(Type (&array)[N]) { ... }
And finally you can take advantage of const correctness. Let's consider a function that prints an array of 10 elements:
void show(const int (&array)[10]) { ... }
By applying the const qualifier we are preventing possible modifications.
The standard library class for arrays
If you consider the above syntax both ugly and unnecessary, as I do, we can throw it in the can and use std::array instead (since C++11).
Here's the refactored code:
void reset(std::array<int, 10>& array) { ... }
void show(std::array<int, 10> const& array) { ... }
Isn't it wonderful? Not to mention that the generic code trick I've taught you earlier, still works:
template<class Type, std::size_t N>
void reset(std::array<Type, N>& array) { ... }
template<class Type, std::size_t N>
void show(const std::array<Type, N>& array) { ... }
Not only that, but you get copy and move semantic for free. :)
void copy(std::array<Type, N> array) {
// a copy of the original passed array
// is made and can be dealt with indipendently
// from the original
}
So, what are you waiting for? Go use std::array.
It's a fun feature of C that allows you to effectively shoot yourself in the foot if you're so inclined. I think the reason is that C is just a step above assembly language. Size checking and similar safety features have been removed to allow for peak performance, which isn't a bad thing if the programmer is being very diligent. Also, assigning a size to the function argument has the advantage that when the function is used by another programmer, there's a chance they'll notice a size restriction. Just using a pointer doesn't convey that information to the next programmer.
First, C never checks array bounds. Doesn't matter if they are local, global, static, parameters, whatever. Checking array bounds means more processing, and C is supposed to be very efficient, so array bounds checking is done by the programmer when needed.
Second, there is a trick that makes it possible to pass-by-value an array to a function. It is also possible to return-by-value an array from a function. You just need to create a new data type using struct. For example:
typedef struct {
int a[10];
} myarray_t;
myarray_t my_function(myarray_t foo) {
myarray_t bar;
...
return bar;
}
You have to access the elements like this: foo.a[1]. The extra ".a" might look weird, but this trick adds great functionality to the C language.
To tell the compiler that myArray points to an array of at least 10 ints:
void bar(int myArray[static 10])
A good compiler should give you a warning if you access myArray [10]. Without the "static" keyword, the 10 would mean nothing at all.
This is a well-known "feature" of C, passed over to C++ because C++ is supposed to correctly compile C code.
Problem arises from several aspects:
An array name is supposed to be completely equivalent to a pointer.
C is supposed to be fast, originally developerd to be a kind of "high-level Assembler" (especially designed to write the first "portable Operating System": Unix), so it is not supposed to insert "hidden" code; runtime range checking is thus "forbidden".
Machine code generrated to access a static array or a dynamic one (either in the stack or allocated) is actually different.
Since the called function cannot know the "kind" of array passed as argument everything is supposed to be a pointer and treated as such.
You could say arrays are not really supported in C (this is not really true, as I was saying before, but it is a good approximation); an array is really treated as a pointer to a block of data and accessed using pointer arithmetic.
Since C does NOT have any form of RTTI You have to declare the size of the array element in the function prototype (to support pointer arithmetic). This is even "more true" for multidimensional arrays.
Anyway all above is not really true anymore :p
Most modern C/C++ compilers do support bounds checking, but standards require it to be off by default (for backward compatibility). Reasonably recent versions of gcc, for example, do compile-time range checking with "-O3 -Wall -Wextra" and full run-time bounds checking with "-fbounds-checking".
C will not only transform a parameter of type int[5] into *int; given the declaration typedef int intArray5[5];, it will transform a parameter of type intArray5 to *int as well. There are some situations where this behavior, although odd, is useful (especially with things like the va_list defined in stdargs.h, which some implementations define as an array). It would be illogical to allow as a parameter a type defined as int[5] (ignoring the dimension) but not allow int[5] to be specified directly.
I find C's handling of parameters of array type to be absurd, but it's a consequence of efforts to take an ad-hoc language, large parts of which weren't particularly well-defined or thought-out, and try to come up with behavioral specifications that are consistent with what existing implementations did for existing programs. Many of the quirks of C make sense when viewed in that light, particularly if one considers that when many of them were invented, large parts of the language we know today didn't exist yet. From what I understand, in the predecessor to C, called BCPL, compilers didn't really keep track of variable types very well. A declaration int arr[5]; was equivalent to int anonymousAllocation[5],*arr = anonymousAllocation;; once the allocation was set aside. the compiler neither knew nor cared whether arr was a pointer or an array. When accessed as either arr[x] or *arr, it would be regarded as a pointer regardless of how it was declared.
One thing that hasn't been answered yet is the actual question.
The answers already given explain that arrays cannot be passed by value to a function in either C or C++. They also explain that a parameter declared as int[] is treated as if it had type int *, and that a variable of type int[] can be passed to such a function.
But they don't explain why it has never been made an error to explicitly provide an array length.
void f(int *); // makes perfect sense
void f(int []); // sort of makes sense
void f(int [10]); // makes no sense
Why isn't the last of these an error?
A reason for that is that it causes problems with typedefs.
typedef int myarray[10];
void f(myarray array);
If it were an error to specify the array length in function parameters, you would not be able to use the myarray name in the function parameter. And since some implementations use array types for standard library types such as va_list, and all implementations are required to make jmp_buf an array type, it would be very problematic if there were no standard way of declaring function parameters using those names: without that ability, there could not be a portable implementation of functions such as vprintf.
It's allowed for compilers to be able to check whether the size of array passed is the same as what expected. Compilers may warn an issue if it's not the case.

size of string array through function

How to find the size of string array passed to a function. The size should be computed inside the function.
#include<iostream>
using namespace std;
template <typename T,unsigned S>
unsigned arraysize(const T (&v)[S]) { return S; }
void func(string args[])
{
unsigned m=arraysize(args);
cout<<m;
}
int main()
{
string str_arr[]={"hello","foo","bar"};
func(str_arr);
}
What i dont understand is:
If the statement arraysize(str_arr) is used in main,it wouldn't pose a problem. The str_arr is an array, so str_arr acts as a pointer, so when we use arraysize(str_arr) that means we're sending the address to arraysize function.(correct me if i'm wrong).
But in function func(), i dont understand why there is a problem, i.e. the statement arraysize(args) sends the address of the string array args(or the address of pointer args).. or is it more complicated since it becomes some double pointer?? Explain?
Also please correct the above code..
str_arr is an array of strings. When you do sizeof(str_arr), you get the size of that array. However, despite the fact that args looks like an array of strings, it's not. An argument that looks like an array is really of pointer type. That is, string args[] is transformed to string* args by the compiler. When you do sizeof(args) you are simply getting the size of the pointer.
You can either pass the size of the array into the function or take a reference to the array with a template parameter size (as you did with arraysize):
template <size_t N>
void func(string (&args)[N])
{
// ...
}
There is no way to determine the size of an array when sent to a function. You also have to remember that only a pointer to the array is sent to the function, which makes it even theoretically quite implausible to calculate the array's size.
The information of the array's size is never visible in your function, as you threw it away when you decided to use string args[] for the argument. From the compiler's perspective, it's the same as string* args. You could change the function to:
template<size_t M>
void func(string (&args)[M])
{
cout<<M;
}
but it seems you already know that, right?
If the statement arraysize(str_arr) is used in main,it wouldn't pose a
problem. The str_arr is an array, so str_arr acts as a pointer, so
when we use arraysize(str_arr) that means we're sending the address to
arraysize function.(correct me if i'm wrong).
I have to correct you here. You state a correct premise, but draw the wrong conclusion.
The key point is indeed that str_arr is an array in main. While an array decays to a pointer in many (most) expression contexts, this does not apply when a reference to array is initialized. That is the reason why array_size is declared to take a reference to array parameter - this is the only way to have a parameter of array type, which implies that it comes with a defined length.
That is not the case for func. When a function parameter is declared to be of plain array type, the the array to pointer decay is applied to that declaraction. Your declaration of func is equivalent to void func(string * args). Thus args is a pointer, not an array. You could call func as
string str_non_array;
func(&str_non_array);
Because of this, a reference-to-array can't bind to it. And anyways, args has completely lost all information about the size of the array it is pointing to.
You could use the same reference-to-array trick as is used in arraysize: declare func as
template <std::size_t N>
void func(string (&args)[N]);
But this gets impractical to do everywhere (and may lead to code bloat, if applied naively to all array-handling code). The C++ equivalent of an array-with-length as available in other languages is std::vector<string> (for dynamically sized arrays) or std::array<string,N> (for fixed size known at compile time). Note that the latter can cause the same code bloat as mentioned above, so in most cases, std::vector<string> would be the preferred type for array that you need to pass to various functions.
Dmitry is right and I would like to explain it a bit further. The reason its happening is because array is not a First Class citizen in C++ and when passed as parameter it decays to pointer and what you get in called function is a pointer to its first element and size is lost.
You can refer C++ arrays as function arguments to see what alternative options are available.

C++ pass an array by reference

is this allowed to pass an array by reference ?
void foo(double& *bar)
Seems that my compiler says no. Why? What is the proper way to pass an array by reference? Or a work around? I have an array argument that my method should modify and that I should retrieve afterwards. Alternatively, I could make this array a class member, which works fine, but it has many drawbacks for other part of my code (that I would like to avoid).
Thanks and regards.
Arrays can only be passed by reference, actually:
void foo(double (&bar)[10])
{
}
This prevents you from doing things like:
double arr[20];
foo(arr); // won't compile
To be able to pass an arbitrary size array to foo, make it a template and capture the size of the array at compile time:
template<typename T, size_t N>
void foo(T (&bar)[N])
{
// use N here
}
You should seriously consider using std::vector, or if you have a compiler that supports c++11, std::array.
Yes, but when argument matching for a reference, the implicit array to
pointer isn't automatic, so you need something like:
void foo( double (&array)[42] );
or
void foo( double (&array)[] );
Be aware, however, that when matching, double [42] and double [] are
distinct types. If you have an array of an unknown dimension, it will
match the second, but not the first, and if you have an array with 42
elements, it will match the first but not the second. (The latter is,
IMHO, very counter-intuitive.)
In the second case, you'll also have to pass the dimension, since
there's no way to recover it once you're inside the function.
As you are using C++, the obligatory suggestion that's still missing here, is to use std::vector<double>.
You can easily pass it by reference:
void foo(std::vector<double>& bar) {}
And if you have C++11 support, also have a look at std::array.
For reference:
http://de.cppreference.com/w/cpp/container/vector
http://de.cppreference.com/w/cpp/container/array
If you want to modify just the elements:
void foo(double *bar);
is enough.
If you want to modify the address to (e.g.: realloc), but it doesn't work for arrays:
void foo(double *&bar);
is the correct form.
8.3.5.8 If the type of a parameter includes a type of the form “pointer to array of unknown bound of T” or “reference
to array of unknown bound of T,” the program is ill-formed
Like the other answer says, put the & after the *.
This brings up an interesting point that can be confusing sometimes: types should be read from right to left. For example, this is (starting from the rightmost *) a pointer to a constant pointer to an int.
int * const *x;
What you wrote would therefore be a pointer to a reference, which is not possible.
Here, Erik explains every way pass an array by reference: https://stackoverflow.com/a/5724184/5090928.
Similarly, you can create an array reference variable like so:
int arr1[] = {1, 2, 3, 4, 5};
int(&arr2)[5] = arr1;

when do we need to pass the size of array as a parameter

I am a little bit confused about pass an array in C/C++. I saw some cases in which the signature is like this
void f(int arr[])
some is like this
void f(int arr[], int size)
Could anybody elaborate what's the difference and when and how to use it?
First, an array passed to a function actually passes a pointer to the first element of the array, e.g., if you have
int a[] = { 1, 2, 3 };
f(a);
Then, f() gets &a[0] passed to it. So, when writing your function prototypes, the following are equivalent:
void f(int arr[]);
void f(int *arr);
This means that the size of the array is lost, and f(), in general, can't determine the size. (This is the reason I prefer void f(int *arr) form over void f(int arr[]).)
There are two cases where f() doesn't need the information, and in those two cases, it is OK to not have an extra parameter to it.
First, there is some special, agreed value in arr that both the caller and f() take to mean "the end". For example, one can agree that a value 0 means "Done".
Then one could write:
int a[] = { 1, 2, 3, 0 }; /* make sure there is a 0 at the end */
int result = f(a);
and define f() something like:
int f(int *a)
{
size_t i;
int result = 0;
for (i=0; a[i]; ++i) /* loop until we see a 0 */
result += a[i];
return result;
}
Obviously, the above scheme works only if both the caller and the callee agree to a convention, and follow it. An example is strlen() function in the C library. It calculates the length of a string by finding a 0. If you pass it something that doesn't have a 0 at the end, all bets are off, and you are in the undefined behavior territory.
The second case is when you don't really have an array. In this case, f() takes a pointer to an object (int in your example). So:
int change_me = 10;
f(&change_me);
printf("%d\n", change_me);
with
void f(int *a)
{
*a = 42;
}
is fine: f() is not operating on an array anyway.
WHen an array is passed in C or C++ only its address is passed. That is why the second case is quite common, where the second parameter is the number of elements in the array. The function has no idea, only by looking at the address of the array, how many elements it is supposed to contain.
you can write
void f( int *arr, int size )
as well, having latter (size) allows to not step outside the array boundaries while reading/writing to it
C and C++ are not the same thing. They have some common subset, though. What you observed here is that the "first" array dimension when passed to a function always results just in a pointer being passed. The "signature" (C doesn't use this term) of a function declared as
void toto(double A[23]);
is always just
void toto(double *A);
That is that the 23 above is somewhat redundant and not used by the compiler. Modern C (aka C99) has an extension here that lets you declare that A always has 23 elements:
void toto(double A[static 23]);
or that the pointer is const qualified
void toto(double A[const 23]);
If you add other dimension the picture changes, then the array size is used:
void toto(double A[23][7]);
in both C and C++ is
void toto(double (*A)[7]);
that is a pointer to an array of 7 elements. In C++ these array bounds must be an integer constant. In C it can be dynamic.
void toto(size_t n, size_t m, double A[n][m]);
They only thing that you have to watch here is that here n and m come before A in the parameter list. So better you always declare functions with the parameters in that order.
The first signature just passes the array with no way to tell how big the array is and can lead to problems with out-of-bounds errors and/or security flaws.\
The second signature is a more secure version because it allows the function to check against the size of the array to prevent the first versions shortcomings.
Unless this is homework, raw arrays are a bit out-dated. Use std::vector instead. It allows passing the vector around without having to manually pass the size as it does this for you.
The size of an array is not passed with the array itself. Therefore, if the other function needs the size, it will have it as a parameter.
The thing is, some functions implicitly understand the array to be of a certain size. So they won't need to have it specified explicitly. For example, if your function operates on an array of 3 floats, you don't need the user to tell you that it is an array of 3 floats. Just take an array.
And then there are those functions (let's call them "terrible" because they are) that will fill an array in with arbitrary data up to a point defined by that data. sprintf is probably the "best" example. It will keep putting characters in that array until it is finished writing them. That's very bad, because there's no explicit or implicit agreement between the user and the function as to how big this array could be. sprintf will write some number of characters, but there's no way for the user to know exactly how many get written (in the general case).
Which is why you should never use sprintf; use snprintf or _snprintf, depending on your compiler.
Anytime you need to know the size of the array, it needs to be provided. There is nothing special about the two forms of passing the array itself; the first parameter is the same either way. The second method simply provides the information needed to know the size of the array while the first does not.
Sometimes the array itself holds the information about its size, though. In your first example, for instance, perhaps arr[0] is set to the size of the array, and the actual data begins at arr[1]. Or consider the case of c-strings... you provide just a char[], and the array is assumed to end at the first element equal to \0. In your example, a negative value may act as a similar sentinel. Or perhaps the function simply doesn't care about the array's size, and will simply assume it is large enough.
Such methods are inherently unsafe, though... it is easy to forget to set arr[0] or to accidently overwrite the null terminator. Then, f suddenly has no way of knowing how much space it has available to it. Always prefer to provide the size explicitly, either via a size parameter like you show, or with a second pointer to the end of the array. The latter is the method generally taken by the standard library functions in C++. You still have the issue of providing an incorrect size, though, which is why in C++ it isn't recommended you ever use such an array in the first place... use an actual container that will keep track of that information for you.
The difference is that the second one includes a parameter that indicates the array size. The logical conclusion is that if you don't use such a parameter, the function doesn't know what the array size is. And this indeed turns out to be the case. In fact, it doesn't know you have an array. In fact, you don't have to have an array to call the function.
The array syntax here, without a specified size inside the square brackets, is a fake-out. The parameter is actually a pointer. For more information, please see http://c-faq.com/aryptr/index.html , especially section 4.