Brett Reinhard Brett Reinhard - 1 month ago 6
C++ Question

What does this mean? 'struct (*)[10]' to '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 I would like to understand why I get this error, and what the difference is between
Fraction(*)[10]
to
Fraction*[]
.

I do not need to fix this error I just simply want to understand the differences between the two, seemingly similar structures.

This caused the error:

const int max_size = 10;
Fraction farry[max_size];
...
display(&farry, fracCounter); // <=== this line


This is the function I was trying to use:

void display(Fraction *fracArr[], int fracCounter){
for (int i = 0; i < fracCounter; i++){
cout << fracArr[i]->num << "/" << fracArr[i]->den << endl;
}
}

Answer

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;

For more information regarding how to parse something like this, see the clockwise spiral rule.

Comments