繁体   English   中英

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

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

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

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

Error   1   error C2664: 'void display(Fraction *[],int)' : cannot convert 
                             argument 1 from 'Fraction (*)[10]' to 'Fraction *[]'

我知道这不起作用,但你对这些中的每一个都称之为什么? Fraction(*)[]Fraction*[] 例如:int []是一个整数数组,int *是一个整数指针。 除了围绕*的括号外,上面的代码看起来与我相同。

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

错误原因

参数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);调用它display(farry, fracCounter);

补充说明:

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

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

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

在机械方面,一个*&宁愿绑定到除被隔离类型名称,代表了整个事情,所以你必须用括号把它从元素类型隔离。 声明函数指针时也是如此: int*(int, int)是一个接受两个int并返回一个int*的函数,而int(*)(int, int)是一个指向一个带两个int的函数的指针s并返回一个int

考虑这个简单的程序:

#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 mangled或unmangled类型,输出显示所有三种都是不同的类型:

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

有关如何解析此类内容的更多信息,请参阅顺时针螺旋规则


注意:当数组作为函数参数传递值时,以及在许多其他情况下(特别是在任何不期望数组但指针是的情况下),类型和维度信息都会丢失,并且它是隐式的转换为指向数组第一个元素的指针; 这被称为衰变为指针的数组。 这表现在func(Type*[3])以上,其中,所述编译器采用的参数类型Type*[3]阵列Type* ,并用其替换Type** ,指针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*静默替换Type[N]Type[] ,从而导致数组在传递时衰减。 通过专门获取指向数组的指针或引用可以避免这种情况; 虽然这与让数组衰减一样有效(前者因为它仍然只传递指针,后者因为大多数编译器使用指针实现引用),但它会失去灵活性(这就是为什么它通常与模板配对,这会恢复灵活性,没有删除任何严格性)。

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

但是请注意,C没有丰富的模板,因此任何旨在使用这两种语言的代码都应该使用传递“size”参数和数组的C语言,或者专门为一定大小的数组; 前者更灵活,而后者则非常有用,如果您永远不需要采用任何其他大小的数组。

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]是一个数组类型,而Fraction*[5]是一个数组类型,但有些情况下,两个声明将被静默替换为Type*Fraction**分别由编译器和类型和维度信息将由此丢失; 这种损失称为阵列衰减或阵列到指针衰减。

谢谢你去juanchopanza提醒我提到阵列到指针的衰变。

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

Fraction (*farray)[10]

和:

Fraction *farray[]

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

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

如果将相同的规则应用于声明,则第二个变为指针数组,而第一个由于括号而使“指针”绑定得更紧,因此它是指向数组的指针。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM