'struct(*)[]'和'struct * []'有什么区别?

时间:2016-11-01 23:14:34

标签: c++ arrays pointers struct

我正在玩指针,指针数组和数组。

我创建了一个名为struct的{​​{1}},并尝试将Fraction数组传递给一个带有Fraction指针数组的函数。我收到错误:

Fraction

我知道这不起作用,但你称之为这些? Error 1 error C2664: 'void display(Fraction *[],int)' : cannot convert argument 1 from 'Fraction (*)[10]' to 'Fraction *[]' Fraction(*)[]。例如:int []是一个整数数组,int *是一个整数指针。除了围绕*。

的括号外,上面的代码看起来与我完全相同

我不需要修复这个错误我只是想了解这两个看似相似的结构之间的差异。

3 个答案:

答案 0 :(得分:5)

错误原因

参数Fraction *fracArr[]需要一个指向分数的指针数组。

您已定义Fraction farry[max_size];,表示farry是一系列分数。

当您调用提供&farry作为第一个参数的函数时,您试图获取指向数组(Fraction (*)[10])而不是指针数组(Fraction *[])的指针。因此不匹配错误。

<强>解决方案

如果你的目标是使用一系列分数,只需按照以下方式更改你的功能:

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

并使用display(farry, fracCounter);进行调用。

补充说明:

更一般地,未知大小T arg[]的类型数组的参数作为指向第一个元素的指针T *arg传递。

定义参数Fraction *arg[]Fraction **arg会产生相同的代码。 []只是隐藏了这个技术细节并使意图更清晰(即使用指针数组而不是使用指针指针)

答案 1 :(得分:4)

Fraction*[]Fraction*(指针数组)的数组。 Fraction(*)[]是指向Fraction[](指向数组的指针)的指针。区别在于括号将“指针”与Fraction隔离开,因为否则两者会相互绑定并为您提供与预期不同的类型。

从机制上讲,*&更倾向于绑定到类型名称而不是被隔离并代表整个事物,因此您必须使用括号将其与元素类型隔离开来。声明函数指针时也是如此:int*(int, int)是一个函数,它接受两个int并返回一个int*,而int(*)(int, int)是一个指向函数的指针int并返回int

考虑this简单程序:

#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.
}

根据所使用的编译器,它将输出arr的错位或非错误类型,输出显示所有三种类型都不同:

// 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

如果您不习惯这种语法有点不可思议,并且可能有点容易输入错误,因此如果您需要使用它,建立类型别名可能是个好主意。

// 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;

这在声明返回指针或对数组的引用的函数时非常有用,因为它们在没有typedef的情况下看起来很傻,并且真的容易出错和/或忘记了语法。

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();

有关如何解析此类内容的详细信息,请参阅clockwise spiral rule

注意:当数组作为函数参数传递值时,并且在许多其他情况下(特别是在任何不期望数组但指针是的情况下),类型和维度信息都会丢失,并且它被隐式转换为指向数组第一个元素的指针;这称为数组decaying into a pointer。这在上面的func(Type*[3])中得到了证明,其中编译器采用参数类型Type*[3],数组为Type*,并将其替换为Type**,指向{{1} }}; Type*丢失,并替换为简单的[3],因为函数可以使用指针而不是数组。调用*时,阵列将因此而衰减。因此,以下签名被视为相同,所有三个参数均为func()

Type**

这样做是因为它比尝试按值传递整个数组更有效(它只需要传递一个指针,它很容易适合单个寄存器,而不是试图将整个数据加载到内存中),并且因为将数组类型编码到函数的参数列表中将删除函数关于它可以采用的数组大小的任何灵活性(如果函数要采用void func(Type*[3]); void func(Type*[] ); // Dimension isn't needed, since it'll be replaced anyways. void func(Type** ); ,那么你无法将它传递给Type[3]或者Type[4])。因此,编译器将以Type[2]静默替换Type[N]Type[],导致数组在传递时衰减。通过专门获取指向数组的指针或引用可以避免这种情况;虽然这与让数组衰减一样有效(前者因为它仍然只传递指针,后者因为大多数编译器使用指针实现引用),但它会失去灵活性(这就是为什么它通常与模板配对,这会恢复灵活性,没有删除任何严格性。)

Type*

但请注意,C没有足够的模板,因此任何适用于这两种语言的代码都应该使用将“size”参数与数组一起传递的C语言,或者专门为一定大小的数组编写的;前者更灵活,而后者非常有用,如果你从不需要采用任何其他大小的数组。

// 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]);

另请注意,在某些情况下会出现数组won't decay into a pointer

void func1(Type *arr, size_t sz);
void func2(Type (*arr)[3]);

所以,简而言之,虽然,// 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. 是一个数组类型,而Type[3]是一个数组类型,但有些情况下,两个语句的声明将被静默替换为{{编译器分别为1}}或Fraction*[5],其中类型和维度信息将因此而丢失;这种损失称为阵列衰减或阵列到指针衰减。

感谢juanchopanza提醒我提到数组到指针的衰减。

答案 2 :(得分:2)

这是输出原始类型与参数声明的编译器导致一点混淆的地方之一。如果重新插入变量名,则现在进行比较:

Fraction (*farray)[10]

Fraction *farray[]

此时,如果您愿意接受声明优先级与正则表达式一样,则错误会变得明显

根据C / C ++的优先级表,[]作为数组索引运算符比指针去引用运算符的一元*绑定得更紧密。

如果将相同的规则应用于声明,则第二个变为指针数组,而第一个具有&#34;指针&#34;由于括号更紧密地绑定,因此它是指向数组的指针。