What's the difference between char[] and char[n] in C/CPP? - c++

Trying to figure out string literal types in C/CPP
printf("%s\n", typeid("abc").name());
printf("%s\n", typeid(const char[]).name());
print
A4_c
A_c
Not familiar with C, is different length/capacity of array means different type in C? if yes, why could we pass char[n] as char[] when passing function parameters?

is different length/capacity of array means different type in C?
Yes.
if yes, why could we pass char[n] as char[] when passing function parameters?
It is actually not possible to accept an array value as a function argument. When a function argument is declared to be an array type, that declaration is adjusted by the language to mean a pointer to an element of that array type. I.e. array of char becomes pointer to char. Same applies to return types. Example:
void function(char argument[N]); // this
void function(char *argument); // actually means this
Similarly, when an array name is used as a value argument, that array name implicitly converts to a pointer to the first element of the array. This implicit conversion is called decaying. Example:
void function(char *argument);
char array[N];
function(array); // array decays to pointer to first element
Note that this adjustment is only applied to "toplevel" arrays. Pointers to arrays and references to arrays are not adjusted to be pointers or references to pointers to element of that array even in function argument declarations.
What's the difference between char[] and char[n] in C/CPP?
char[n] is an array type. It is an array of n elements. It is a complete type. It is possible to create arrays of this type.
char[] is an array of unknown bound. It is an incomplete type. It is not possible to create an array of this type. This type can only be used in contexts where it is adjusted to another type. In a function declaration, it is adjusted to a pointer to element.
In a declaration of a non-argument array, it is adjusted to the the actual array type of known bound that is deduced from an initialiser:
char arr[] = {'1', '\0'}; // type of arr is adjusted to char[2]
char arr[]; // ill-formed declaration

Since the typeid().name() output varies from compiler to compiler, piping output to c++filt is a better way to see the type's name.
This code:
#include <iostream>
int main(void)
{
std::cout << typeid("abc").name() << std::endl;
std::cout << typeid(const char[]).name() << std::endl;
return (0);
}
compiled and ran as ./a.out outputs:
A4_c
A_c
but when ran as ./a.out | c++filt
outputs the following:
char [4]
char []
For char[] vs char[4] its minimal, but for big nested types and auto in later standards of C++ it becomes a real handy tool for seeing whats happening under the hood.
(Sorry for not being a direct answer but oh how I cringe to see unformatted typeid names in any context, especially since they are compiler specific)

Related

Can a pointer convert to an array during a function call?

Consider the following piece of code:
#include <iostream>
#include <typeinfo>
void use_pointer(int *ptr)
{
std::cout << typeid(ptr).name() << std::endl;
}
void use_array(int arr[])
{
std::cout << typeid(arr).name() << std::endl;
}
int main()
{
int *ptr = nullptr;
// std::cout << typeid(int *).name() << std::endl; // output: Pi
int arr[1];
// std::cout << typeid(int[]).name() << std::endl; // output: A_i
use_pointer(arr);
use_array(ptr);
}
Compiling this program using g++ 6.5.0 outputs:
$ g++ -std=c++11 arrays_2.cpp -Wall
$ ./a.out
Pi
Pi
Now, when calling use_pointer(arr) the array is being decayed to a pointer. (Is that correct? The word "decay" is new to me.)
And the C++ Standard says at [conv.array#1]:
An lvalue or rvalue of type “array of N T” or “array of unknown bound
of T” can be converted to a prvalue of type “pointer to T”. The
temporary materialization conversion ([conv.rval]) is applied. The
result is a pointer to the first element of the array.
I think I understand that my "array of int" converts to a "pointer to int". (Is that correct?)
Now, what happens exactly when calling use_array(ptr). Since the type of the parameter in this case is array of int, did the "pointer to int" convert to an "array of int"?
A pointer to the standard would be much appreciated.
Pointers are pointers, and arrays are arrays. However, arrays naturally decays to pointers to their first element. So when you pass the array arr to any of the function you have, it will decay to &arr[0].
Also note that when declaring function arguments, array-notation (like int arr[]) doesn't mean that it's an array, it's still translated by the compiler as a pointer (i.e. int* arr).
Regarding the decay from array to pointer, it can't happen the other way. Once you have a pointer, all you have is that pointer and the single element it points to.
Your code has little to do with pointer decay. Rather, it demonstrates type adjustment of function parameters.
When used as a type of a function parameter, T[] is being adjusted to T*. This is not a decay, not a conversion, it's a rewrite. The program is rewritten to use T*, as if no T[] was ever there in the source code.
To reiterate, there are no function parameters of type T[] in C++. When the programmer writes one, the compiler immediately substitutes T* and forgets that T[] was ever there. This is in contrast with normal arrays and their decay to pointers. There are most definitely arrays in C++ which are very different from pointers. They decay to pointers only in (many but not all) expressions. It's a one way street: a pointer never un-decays to an array
Can a pointer convert to an array during a function call?
No. See the last example when passing by reference.
An array type has a size (that includes all elements). When it decays into a pointer this size information is lost. So there is no way to convert a pointer back into an array (as you don't know how big the array is).
You probably noticed that array and pointer parameters are actually synonyms for each other to the compiler:
> cat b.cpp
void use_pointer(int *ptr)
{}
void use_pointer(int (arr)[])
{}
> g++ b.cpp
b.cpp:8:6: error: redefinition of 'use_pointer'
void use_pointer(int (arr)[])
^
b.cpp:4:6: note: previous definition is here
void use_pointer(int *ptr)
It does not matter to the compiler these are both the same thing.
So when you pass an array as a parameter it will usually decay into a pointer (for the address of the first element of the array).
Now you can pass an array by reference but the syntax is different.
void use_pointerref(int (&arr)[1]) // You have to specify the exact size
{} // And lace the ampersand in there.
But you will notice that a pointer will not bind to this function.
use_arrayref(arr);
use_arrayref(ptr);
> g++ b.cpp
b.cpp:31:5: error: no matching function for call to 'use_arrayref'
use_arrayref(ptr);
^~~~~~~~~~~~
b.cpp:14:6: note: candidate function not viable: no known conversion from
'int *' to 'int (&)[1]' for 1st argument
void use_arrayref(int (&arr)[1])
Some notes:
But let us think about that. C++ semantics are normally pass by value. So if you had succeeded (or should I say if the language allowed) in passing the array you would have made a copy of the array that would have been passed to the function. This is probably not desirable so apssing the pointer is an efficient way of passing by value (though you do . get a type change).
Note that the pass by reference is very limited as it must know the exact type. We usually get around the size issue by making it a template.
template<int S>
void use_pointerref(int (&arr)[S])
{
std::cout << typeid(arr).name() << " Size: " << S << std::endl;
}

What is the difference between 'struct (*)[]' and 'struct *[]'?

I am playing around with pointers, arrays of pointers, and arrays.
I created a struct called Fraction and tried passing an array of Fraction into a function that takes an array of Fraction pointers. I get the error:
Error 1 error C2664: 'void display(Fraction *[],int)' : cannot convert
argument 1 from 'Fraction (*)[10]' to 'Fraction *[]'
I understand that this does not work, but what you you call each of these? Fraction(*)[] and Fraction*[]. for example: int[] is an array of integers, and an int* is an integer pointer. The above code, looks identical to me aside from the parenthesis around the *.
I do not need to fix this error I just simply want to understand the differences between the two, seemingly similar structures.
Cause of the error
The parameter Fraction *fracArr[] expects an array of pointers to fractions.
You have defined Fraction farry[max_size]; meaning that farry is an array of fractions.
When you call the function providing &farry as first argument, you are trying to take a pointer to an array (Fraction (*)[10]) instead of an array of pointers (Fraction *[]). Therefore the mismatch error.
Solution
If your goal is to work with an array of fractions, just change your function as follows:
void display(Fraction fracArr[], int fracCounter){
for (int i = 0; i < fracCounter; i++){
cout << fracArr[i].num << "/" << fracArr[i].den << endl;
}
}
and call it with display(farry, fracCounter);.
Additional remarks:
More generally, an argument of type array of unknown size T arg[] is passed as a pointer T *arg pointing to the first element.
Defining your argument Fraction *arg[] or Fraction **arg would result in the same code. The [] just hides this technical detail and make the intent clearer (i.e. working with an array of pointers vs. working with a pointer to pointer)
Fraction*[] is an array of Fraction* (an array of pointers). Fraction(*)[] is a pointer to Fraction[] (pointer to an array). The difference is that parentheses isolate the "pointer" from the Fraction, because otherwise the two would bind to each other and give you a different type than intended.
Mechanically, a * or a & would much rather bind to a type name than be isolated and represent the entire thing, so you have to use parentheses to isolate it from the element type. This is also true when declaring function pointers: int*(int, int) is a function that takes two ints and returns an int*, while int(*)(int, int) is a pointer to a function that takes two ints and returns an int.
Consider this simple program:
#include <iostream>
#include <typeinfo>
struct Type {};
// 1: Array of Type*.
void func(Type *arr [3]) {
std::cout << "Type* array.\n"
<< typeid(arr).name() << "\n\n";
}
// 2: Array of Type&.
// Illegal.
// void func(Type &arr [3]) {
// std::cout << "Type& array.\n"
// << typeid(arr).name() << "\n\n";
// }
// 3: Pointer to array of Type.
void func(Type (*arr) [3]) {
std::cout << "Pointer to Type array.\n"
<< typeid(arr).name() << "\n\n";
}
// 4: Reference to array of Type.
void func(Type (&arr) [3]) {
std::cout << "Reference to Type array.\n"
<< typeid(arr).name() << "\n\n";
}
int main() {
// Array of Type.
Type t_arr[3] = {};
// Array of Type*.
Type* tp_arr[3] = { &t_arr[0], &t_arr[1], &t_arr[2] };
// Array of Type&.
// Illegal.
// Type& tr_arr[3] = { t_arr[0], t_arr[1], t_arr[2] };
std::cout << "Type[3]: " << typeid(t_arr).name() << "\n\n";
func(t_arr); // Calls #4.
func(&t_arr); // Calls #3.
func(tp_arr); // Calls #1.
}
Depending on the compiler used, it'll output either mangled or unmangled types for arr, and the output shows that all three are different types:
// MSVC:
Type[3]: struct Type [3]
Reference to Type array.
struct Type [3]
Pointer to Type array.
struct Type (*)[3]
Type* array.
struct Type * *
// GCC:
Type[3]: A3_4Type
Reference to Type array.
A3_4Type
Pointer to Type array.
PA3_4Type
Type* array.
PP4Type
This syntax is a bit wonky if you're not used to it, and can be somewhat easy to mistype, so it may be a good idea to make a type alias if you need to use it.
// Array.
typedef Type Type_arr_t[3];
// Pointer.
typedef Type (*Type_arr_ptr_t)[3];
// Reference.
typedef Type (&Type_arr_ref_t)[3];
// ...
// Without typedefs.
Type arr [3];
Type (*arr_p)[3] = &arr;
Type (&arr_r)[3] = arr;
// With typedefs.
Type_arr_t arr2;
Type_arr_ptr_t arr2_p = &arr2;
Type_arr_ref_t arr2_r = arr2;
This is extremely useful when declaring functions that return pointers or references to arrays, because they look silly without typedefs, and are really easy to get wrong and/or forget the syntax for.
typedef Type (*Type_arr_ptr_t)[3];
typedef Type (&Type_arr_ref_t)[3];
// Without typedefs.
Type (*return_ptr())[3];
Type (&return_ref())[3];
// With typedefs.
Type_arr_ptr_t return_ptr_2();
Type_arr_ref_t return_ref_2();
For more information regarding how to parse something like this, see the clockwise spiral rule.
Note: When an array is passed by value as a function parameter, and in many other situations (specifically, in any situation where an array isn't expected, but a pointer is), type and dimension information is lost, and it is implicitly converted to a pointer to the first element of the array; this is known as the array decaying into a pointer. This is demonstrated in func(Type*[3]) above, where the compiler takes a parameter type of Type*[3], an array of Type*, and replaces it with Type**, a pointer to Type*; the [3] is lost, and replaced with a simple *, because functions can take pointers but not arrays. When func() is called, the array will decay due to this. Due to this, the following signatures are treated as identical, with the parameter being Type** in all three.
void func(Type*[3]);
void func(Type*[] ); // Dimension isn't needed, since it'll be replaced anyways.
void func(Type** );
This is done because it's more efficient than trying to pass the entire array by value (it only needs to pass a pointer, which easily fits inside a single register, instead of trying to load the entire thing into memory), and because encoding the array type into the function's parameter list would remove any flexibility from the function regarding the size of the array it can take (if a function were to take Type[3], then you couldn't pass it a Type[4] or a Type[2]). Due to this, the compiler will silently replace a Type[N] or Type[] with a Type*, causing the array to decay when passed. This can be avoided by specifically taking a pointer or reference to the array; while this is as efficient as letting the array decay (the former because it still just passes a pointer, the latter because most compilers implement references with pointers), it loses out on flexibility (which is why it's usually paired with templates, which restore the flexibility, without removing any of the strictness).
// Will take any pointer to a Type array, and replace N with the number of elements.
// Compiler will generate a distinct version of `func()` for each unique N.
template<size_t N>
void func(Type (*)[N]);
// Will take any reference to a Type array, and replace N with the number of elements.
// Compiler will generate a distinct version of `func()` for each unique N.
template<size_t N>
void func(Type (&)[N]);
Note, however, that C doesn't have the luxury of templates, and thus any code that is intended to work with both languages should either use the C idiom of passing a "size" parameter along with the array, or be written specifically for a certain size of array; the former is more flexible, while the latter is useful if you will never need to take an array of any other size.
void func1(Type *arr, size_t sz);
void func2(Type (*arr)[3]);
Also note that there are situations where an array won't decay into a pointer.
// Example array.
Type arr[3];
// Function parameter.
void func(Type arr[3]);
void func(Type (*arr)[3]);
void func(Type (&arr)[3]);
// Function template parameter.
template<typename T>
void temp(T t);
// Class template parameter.
template<typename T>
struct S { typedef T type; };
// Specialised class template parameter.
template<typename T> struct S2;
template<typename T, size_t Sz>
struct S2<T[Sz]> { typedef T type[Sz]; };
func(arr); // C: arr decays into Type*.
// C++: arr either binds to a Type(&)[3], or decays into Type*.
// If both are available, causes error due to ambiguous function call.
func(&arr); // C/C++: No decay, &arr is Type(*)[3].
sizeof(arr); // C/C++: No decay, evaluates to (sizeof(Type) * 3).
alignof(arr); // C/C++: No decay, evaluates to alignof(Type).
decltype(arr); // C++: No decay, evaluates to Type[3].
typeid(arr); // C++: No decay, evaluates to a std::type_info for Type[3].
for (Type& t : arr); // C++: No decay, ranged-based for accepts arrays.
temp(arr); // C++: arr decays to Type* during function template deduction.
temp<Type[3]>(arr); // C++: No decay, deduction isn't required.
// For class templates, deduction isn't performed, so array types used as template parameters
// don't decay.
S<Type[3]>::type; // C++: No decay, type is Type[3].
S2<Type[3]>::type; // C++: No decay, type is Type[3].
// String literals are arrays, too.
decltype("Hello."); // C++: No decay, evaluates to const char[7].
char c_arr[] = "Hello."; // C/C++: No decay, c_arr is a local array, of type char[7],
// containing copy of "Hello."
const char* c_ptr = "Hello."; // C/C++: const char[7] "Hello." is stored in read-only
// memory, and ptr points to it.
// There may be other cases in which arrays don't decay, which I'm currently not aware of.
So, in short, while, say, Type[3] is an array type, and Fraction*[5] is an array type, there are cases where a declaration of the two will be silently replaced with a Type* or Fraction**, respectively, by the compiler, and where type and dimension information will be lost due to this; this loss is known as array decay or array-to-pointer decay.
Thanks go to juanchopanza for reminding me to mention array-to-pointer decay.
This is one of these places where the compiler outputting a raw type versus a parameter declaration causes a little confusion. If you re-insert the variable names, the comparison is now between:
Fraction (*farray)[10]
and:
Fraction *farray[]
At this point the error becomes obvious if you are willing to accept that declarations have a precedence just like regular expressions.
According to C/C++'s precedence table, [] as the array index operator binds more tightly than unary * the pointer de-reference operator.
If you apply this same rule to the declarations, the second becomes an array of pointers, while the first one has the "pointer" bound more tightly due to the parentheses, therefore it is a pointer to an array.

Is an array argument passed to a function not a constant pointer?

Consider the code:
void foo(char a[]){
a++; // works fine, gets compiled
//...
}
Now, consider this:
void foo(){
char a[50];
a++; // Compiler error
//...
}
I heard an array is equivalent to a constant pointer and can't be incremented as it is not a lvalue...
Then why does first code gets compiled, is it so because array arguments to functions are passed as a pointer, i.e. T[] is converted to T* for passing..
So, foo(a) passes a as a pointer.
But is it not back converted to T[] again because is declared as:
void foo(char a[]);
When you pass an array as an argument to a function, it decays to a pointer.
So the thing you increment inside the function body is a pointer, not an array.
This is a rather unfortunate feature inherited from the C language, with the rather yucky name of "decay." Since C once did not allow passing compound types by value, they decided to allow programmers to specify arrays as function parameter types, but only cosmetically. The array type decays to a pointer type, implementing a sort of pass-by-reference semantic different from the rest of the language. Ugly.
To recap (and others have already said this), the signature
void foo(char a[]); // asking for trouble
is unceremoniously mangled into
void foo(char *a);
… all for the sake of compatibility with ancient C code. Since you aren't writing ancient C code, you should not make use of this "feature."
However, you can cleanly pass a reference to an array. C++ requires that the size of the array be known:
void foo( char (&a)[ 50 ] );
Now a cannot be modified within the function (edit: its contents can, of course — you know what I mean), and only arrays of the right size may be passed. For everything else, pass a pointer or a higher-level type.
I heard an array is equivalent to a constant pointer
You can think of it that way, but they're not equivalent.
An array decays to a pointer when passed to a function, that's why inside the function it's valid.
Just because the signature is void foo(char a[]) doesn't make a an array.
Outside the function, it's just an array, and you can't do pointer arithmetics on it.
In C++, any function parameter of type "array of T" is adjusted to be "pointer to T". Try this code:
void foo(char a[]) {}
void foo(char* a) {} //error: redefinition
They are indeed the same function.
So, why can we pass an array argument to a function as a pointer parameter? Not because an array is equivalent to a constant pointer, but because an array type can be implicitly converted to an rvalue of pointer type. Also note the result of the conversion is an rvalue, that's why you can't apply the operator ++ to an array, you can only apply this operator to an lvalue.
I heard an array is equivalent to a constant pointer and can't be incremented as it is not a lvalue...
Almost.
An array expression is a non-modifiable lvalue; it may not be an operand to operators such as ++ or --, and it may not be the target of an assignment expression. This is not the same thing as a constant pointer (that is, a pointer declared as T * const).
An array expression will be replaced with a pointer expression whose value is the address of the first element of the array except when the array expression is an operand of the sizeof or unary & operators, or when the array expression is a string literal being used to initialize another array in a declaration.
When you call a function with an array argument, such as
int a[N];
...
foo(a);
the expression a is converted from type "N-element array of int" to "pointer to int" and this pointer value is what gets passed to foo; thus, the corresponding function prototype should be
void foo (int *arr) {...}
Note that in the context of a function parameter declaration, T a[] and T a[N] are identical to T *a; in all three cases, a is declared as a pointer to T. Within the function foo, the parameter arr is a pointer expression, which is a modifiable lvalue, and as such it may be assigned to and may be the operand of the ++ and -- operators.
Remember that all these conversions are on the array expression; that is, the array identifier or other expression that refers to the array object in memory. The array object (the chunk of memory holding the array values) is not converted.
When you pass an array a[] to a function, it passes the value of 'a', an address, into the function. so you can use it as a pointer in the function. If you declare an array in the function, 'a' is constant because you can't change its address in memory.

Should I cast arrays to pointers when passing them to variadic functions like printf?

Can I pass an array to printf directly:
char text[1024] = "text";
printf("%s", text);
Or should I explicitly cast it to a char pointer:
char text[1024] = "text";
printf("%s", (char*) text);
I'm asking because I thought maybe it copies the array elements directly into the va_list instead of putting just a pointer to the first element.
Yes, you can pass an array directly. Exactly, name of array represents address of the array which makes no difference with char *.
(char*)text and text hardly make any difference in this example! Base address of an array decays into a pointer when passed as a function argument.
In fact, even if text was not a char array, still it would hardly make any difference to printf because it is a variable argument function
int printf(const char *format, ...);
and all it cares is about the first argument. Logic might go wrong but printf doesn't care!
Short answer, yes, arrays in C _are_a pointer to their first element. (Edit: Longer answer, well, actually, arrays in C are identifiers or labels which are just names, but when used as part of an expression that doesn't preface them with & or sizeof they get converted into or otherwise evaluated as pointers).
Even longer answer: You see, functions can't really receive an array as an argument, but the compiler has to send them something, so it sends them a pointer. Thus the first sentence should probably start with "As far as functions like printf() are concerned..."
Some functions purport to take an array, but that's just an illusion. These two functions are the same.
func(char *a);
func(char a[]);
The C FAQ entry below goes into more detail about the subtle differences between arrays and pointers:
char a[] = "this";
char *b = "that";
a[0] generates completely different code than b[0] yet they are roughly equivalent in that the subscripting operator [] gives us the expected result.
Interestingly, the format specifier "%s\n" is also passed to printf() as a pointer.
http://www.lysator.liu.se/c/c-faq/c-2.html
Isn't an array/arrayname always a pointer to the first element in C?
Except when it is the operand of the sizeof or unary & operator, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be replaced by an expression of type "pointer to T" whose value is the address of the first element of the array.
When you write
printf("%s", text);
the expression text is replaced by a new expression of type char * whose value is &text[0], and this pointer value is what gets passed to printf. This means you don't have to cast the argument. It also means that you cannot pass an array expression as a function parameter and have the called function receive it as an array type. The conversion happens before the function is called.
names of pointers and arrays are exchangeable. (Besides the memory allocation)
When you have
char *a = "abcdef";
char b[5];
You could write
char z = a[2]; // 'c'
char x = *b;
char y = *(b+2);
But you can't do
b ++;
You may consider array b as char * const b as a unmodifyable pointer

Cannot convert from 'T[N][2]' to 'T[][2]'

I have an API taking some options:
void init_api(const char* options[][2]);
I am allowed to pass a NULL pointer for no options, alternatively, an options array such as this can be passed:
const char* some_options[][2] = { {"opt1", "val1"},
{"opt2", "val2"},
{0,0}
};
This works without problems:
...
init_api(some_options);
... or ...
init_api(NULL);
...
However, this fails to compile:
const char* my_options[][2] = NULL; // error C2440: 'initializing' : cannot convert from 'int' to 'const char *[][2]'
if(...) {
my_options = some_options; // error C2440: '=' : cannot convert from 'const char *[4][2]' to 'const char *[][2]'
}
init_api(my_options); // no error here
What is going on here? Can someone explain this?
To declare an empty array of array of pointers to const char, you should use:
const char* my_options[][2] = {};
You need to declare a pointer to an array of pointers to const char instead. I recommend using a typedef to simplify the syntax.
typedef const char* array_of_two_cstring[2];
array_of_two_cstring* my_options = NULL;
if (...) {
my_options = some_options;
}
init_api(my_options);
In C++ (it is herited from C), array can be implicitly converted to pointer (only once though, that is char[] is compatible with char* but char[][] is compatible with char*[] but not `char**). However, the variable cannot be reassigned. So here you need to use a pointer instead of an array.
The init_api option accepts NULL as a parameter because for the compiler, its prototype is void init_api(char const* (*)[2]) (the first array degenerated into a pointer), and NULL is a valid pointer.
The compiler must know the array size.
If you omit the size of the array (ie: using []) you need to initialize the array with the definition, in order to let the compiler count how many items that array will contain.
Moreover you are assigning a pointer (NULL) to an array: const char *x[][2] is an array of two pointers to const char.
Edit:
In C++ (as in C), arrays can decay into pointers when you use them (with three exceptions which are not interesting here).
When you pass an array to a function expecting an array, what happens is that you actually pass a pointer to the array, since the array decays; you cannot pass an array by value in C or C++.
For this reason you can pass NULL to your function; the function parameter will be NULL, and if you try to access the array within your function (options[0]) your application will crash: you'll be dereferencing an invalid pointer.
You cannot however set your array variable to NULL, since it's not a pointer, it's an array: it will only decay when you'll use it in an expression.
const char* options[][2]
is an array of const char* pointers. You can't assign a pointer to an array.
A parameter declared as being an array of type T[N] or T[] becomes actually a parameter of type T*. Same is done for functions (a parameter declared as R(Params) becomes actually a parameter of type R(*)(Params...)).
Such transformation however is not done for other declarations. The reason it's done for function by-value parameters is that there is no way in C to actually copy an array directly (that is, to actually copy its contents) and it doesn't make sense to try and copy a function either, so such parameters are transformed in a way that conveys their purpose in a meaningful way.
So while you are initializing a pointer in the function parameter case, you are trying to initialize an array in the other case.